(类似于这个问题,但有一些上下文和演示为什么rerere
不是答案。)
对于给定的历史:
/...o origin/master
o...o...o...o...o...o...o master
\...o........../ topic
我有一个已合并到 master 的主题分支,并进行了一次额外的提交。同时,上游有人在 origin/master 上再次提交,所以我不能再按原样推送我的 master。
我想将我的 master 重新定位到 origin/master 而不改变主题上的提交 SHA 并且不丢失已经在 master 上执行的冲突解决。(这是迄今为止我想要保留合并提交的最常见情况,所以我很惊讶这显然如此困难。)
rerere
启用后,git rebase -p
几乎可以正常工作-对于原始合并中的任何冲突,它都会记住我为修复它们所做的工作并重新应用它(尽管它使文件标记为冲突,因此我必须记住将每个文件标记为已解决而无需重新启动文件上的冲突解决,这在 TortoiseGit 前端有点烦人)。但是,如果在合并提交中也修复了文件的任何其他更改(例如,纯粹在合并中添加的行没有冲突,但由于其他地方的更改仍需要更正),这些都会丢失。
事情是这样的。在我(可能是有缺陷的)对合并提交的理解中,它们由两个(或更多)父级和一个唯一的变更集(用于存储冲突解决方案,以及在提交合并之前或稍后修改为合并提交之前所做的任何其他更改)组成。似乎rebase -p
重新创建了合并提交,但完全丢弃了这个额外的变更集。
为什么它不重新应用原始合并提交中的变更集?这将使 rerere 变得多余并避免丢失这些额外的更改。如果需要人工确认,它可能会将受影响的文件标记为冲突,但在许多情况下,这种自动解决方案就完全足够了。
换句话说,标记上面的一些提交:
/...N origin/master
o...o...o...o...B...M...A master
\...T........../ topic
T - the commit on topic
B - the merge-base of origin/master and master
N - the new commit on origin/master
M - the merge between B and T
A - the extra post-merge commit
M 有父母 B 和 T 以及一个独特的变更集 Mc。创建 M' 时,git 在父 N 和 T 之间执行新的合并,并丢弃 Mc。为什么 git 不能只重新应用 Mc 而不是丢弃它?
最后,我希望历史看起来像这样:
o...o...o...o...B...N...M'...A' master
\...T............../
其中 M' 和 A' 从 rebase 更改 SHA1,但 M' 包括 Mc 变更集,而 T 没有更改 SHA1 或父级。现在我可以将 origin/master 快进到 A'。
我还注意到有一个新选项一--rebase-merges
开始听起来不错,之后确实会产生正确的图表——但就像--preserve-merges
仍然会因 M' 上的冲突而停止,并且会丢失 Mc 中没有被 rerere 保存的任何独特更改。
该问题的另一种表述可能更有用:
给定上面的初始状态,并且刚刚开始一个交互式变基,现在处于 HEAD1 或 HEAD2 状态:
/...........(T)
/ \
/ /...M' HEAD2
/ /... HEAD1
/ /...N origin/master
o...o...o...o...B...M...A master
\...T........../ topic
(HEAD1 已签出 N,但尚未执行任何其他操作;HEAD2 创建了一个新的合并,其中 N 作为父级 1,T 作为父级 2,但由于未解决的冲突尚未提交)
是否有一些 rebase 命令和/或 git 命令序列将:
- 计算 M 和 B 之间的 diff Mc(选择 B,因为另一个父 T 没有变化)
- 将此应用于冲突的树 M' (应完全解决所有冲突,除非 N 引入新冲突)或只需将其应用于 N 之上(无需先进行任何合并)——这些应该是等价的;第二个可能更容易
- 暂停让人类解决由 N 引入的任何剩余冲突(如果有)。
- 提交 M' 作为 N 和 T 之间的合并
- 像往常一样继续(在这种情况下,在 M' 之上将 A 重新设置为 A')
为什么git默认不这样做?