假设我使用git rebase -i
进行交互式变基。如果出现一些冲突,我可能会遇到合并冲突并要求进行3向合并。使用meld
,我会看到三个窗口:LOCAL
(左),???
(中)和REMOTE
(右)。这里???
我的意思是meld
没有提供一些特殊名称来附加到文件。
在正常合并期间,这是有道理的,因为中间是共同的祖先,并且您正在将本地和远程更改合并到该祖先。然而,在交互式rebase期间似乎并非如此 - 目前还不清楚每个文件代表什么。
在交互式rebase期间,3向合并中的这些文件分别代表什么?编辑这些文件时,我的目标是什么?
更新:根据我看到的评论和实验:
LOCAL
):此时提交重播序列中文件的本地版本。REMOTE
):刚刚应用当前提交之后的文件状态。因此,我的任务是从中间到右侧确定增量,然后将此增量应用于左侧。在新的提交序列中应用当前提交增量后,Middle应反映文件的状态。
请注意,此配置似乎特定于融合,至少在某种程度上。 Git的3向合并行为可能因其他编辑而异。
答案 0 :(得分:5)
中间版本是合并基础,就像git merge
一样。
(名称"其他"可能比" remote"更合适;因为没有要求合并的另一面是远程的,并且因为Mercurial始终使用该名称"其他"对于它,并不是说Git需要匹配Mercurial,但是一些一致性可能会很好。请注意,Git使用名称"我们的"和#34;他们的"这里同样,我们永远不会从Git获得100%的一致性:-))
总是合并基础。
通常我们甚至不必找到它,因为每个补丁在作为补丁处理时都会干净利用(不尝试三向合并)。但有时补丁不会干净利落,我们做必须回归三方合并。
(顺便提一下,您可以停用此后备广告。请参阅the git-am documentation中的--3way
,--no-3way
和am.threeWay
,但此处链接的页面已经过时控制最近改变了。)
$ git rebase -i
pick aaaaaaa first commit
pick bbbbbbb second commit
pick ccccccc third commit
让我们也绘制提交图,这样我们就可以看到我们正在改变的内容:
A - B - C <-- branch
/
... - o - *
\
G - H <-- origin/branch
我们将挑选提交A
,B
和C
(A
= aaaaaaa
等)以便我们获取这个结果,最后:
A - B - C [abandoned]
/
... - o - * A' - B' - C' <-- branch
\ /
G - H <-- origin/branch
让我们仔细看看A
的第一个樱桃选择。
将(差异)A
与其父级进行比较,即提交*
,并尝试将生成的差异应用于提交H
。
提交H
已从提交*
稍微偏离。实际上,我们可以在A
和H
之间找到合并基础,它是...... commit *
。这实际上是一个相当不错的合并基础,尽管Git可以按原样应用补丁,而不必回退到三向合并代码。
因此,提交*
是将A
挑选到H
时的合并基础。合并完成后,我们得到新的提交A'
。 (例如,它的新SHA-1 ID可能是aaaaaa1
。可能不是;我们只需将其称为A'
。)
现在我们挑选B
。这会使B
与其父A
进行区分,并尝试将差异应用于A'
。
提交A'
已从提交B
稍微偏离。事实上,我们可以找到B
和A'
之间的合并基础,即...再次提交*
。不幸的是,这是一个可怜的合并基地。幸运的是,如果补丁不能按原样应用,Git只会依赖它,通常它可以。但如果它不能, Git会将 编辑 Git作弊。 (此代码最近在版本2.6中已更改,但总体策略保持不变。)*
与B
和*
vs A'
区分开,并尝试合并这两个差异。请注意,*
vs B
包含我们在A
中所做的所有更改,但*
vs A'
也包含所有相同的A
更改,所以如果我们很幸运,Git会注意到已经合并的更改并且不会复制它们。
当用于显示从提交git diff
到提交A
的更改时,请考虑B
的实际输出。这包括index
行:
diff --git a/foo b/foo
index f0b98f8..0ea3286 100644
左侧的值是commit foo
中文件A
版本的(缩写)哈希值。右侧的值是commit B
中文件版本的哈希值。
Git只是从左侧哈希中伪造了一个合并库。换句话说,commit A
中的文件版本将成为伪造的合并库。 (Git将--build-fake-ancestor
传递给git apply
。这要求特定文件blob对象在存储库中,但它们是在提交A
中。对于通过电子邮件发送的补丁,Git使用相同的代码,但blob可能存在也可能不存在。)
请注意,Git实际上也会在挑选提交A
时执行此操作,但这次合并基础文件是提交*
的版本,其中 合并基地。
最后,我们挑选C
。这与B
vs C
不同,就像上次我们对A
vs B
的分歧一样。如果我们可以按原样应用补丁,那么好;如果没有,我们会再次使用commit *
作为合并基础返回。它再一次是一个非常糟糕的合并基础。和以前一样,假装B
中的版本是共同的基础。
顺便提一下,这也解释了为什么你倾向于为这些rebase反复看到相同的合并冲突:我们每次都使用相同的合并库。 (启用 git rerere
可以提供帮助。)
答案 1 :(得分:-1)
在这方面,合并和rebase是相同的。合并和rebase之间的唯一区别是,历史看起来更好(更线性)与rebase。但是对于将要出现的和你必须解决的冲突,它们是相同的。