使用git和meld进行交互式变基的三向合并中的三个文件是什么?

时间:2016-05-03 00:24:52

标签: git rebase git-rebase meld

假设我使用git rebase -i进行交互式变基。如果出现一些冲突,我可能会遇到合并冲突并要求进行3向合并。使用meld,我会看到三个窗口:LOCAL(左),???(中)和REMOTE(右)。这里???我的意思是meld没有提供一些特殊名称来附加到文件。

在正常合并期间,这是有道理的,因为中间是共同的祖先,并且您正在将本地和远程更改合并到该祖先。然而,在交互式rebase期间似乎并非如此 - 目前还不清楚每个文件代表什么。

在交互式rebase期间,3向合并中的这些文件分别代表什么?编辑这些文件时,我的目标是什么?

更新:根据我看到的评论和实验:

  • 左(LOCAL):此时提交重播序列中文件的本地版本。
  • 右(REMOTE):刚刚应用当前提交之后的文件状态。
  • 中:原始提交序列中右侧的父级。

因此,我的任务是从中间到右侧确定增量,然后将此增量应用于左侧。在新的提交序列中应用当前提交增量后,Middle应反映文件的状态。

请注意,此配置似乎特定于融合,至少在某种程度上。 Git的3向合并行为可能因其他编辑而异。

2 个答案:

答案 0 :(得分:5)

中间版本是合并基础,就像git merge一样。

(名称"其他"可能比" remote"更合适;因为没有要求合并的另一面是远程的,并且因为Mercurial始终使用该名称"其他"对于它,并不是说Git需要匹配Mercurial,但是一些一致性可能会很好。请注意,Git使用名称"我们的"和#34;他们的"这里同样,我们永远不会从Git获得100%的一致性:-))

但是等等,怎么会有合并基础?

总是合并基础。

通常我们甚至不必找到它,因为每个补丁在作为补丁处理时都会干净利用(不尝试三向合并)。但有时补丁不会干净利落,我们必须回归三方合并。

(顺便提一下,您可以停用此后备广告。请参阅the git-am documentation中的--3way--no-3wayam.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

我们将挑选提交ABCA = aaaaaaa等)以便我们获取这个结果,最后:

              A - B - C   [abandoned]
            /
... - o - *           A' - B' - C'   <-- branch
            \       /
              G - H       <-- origin/branch

让我们仔细看看A的第一个樱桃选择。

将(差异)A与其父级进行比较,即提交*,并尝试将生成的差异应用于提交H

然而,

提交H已从提交*稍微偏离。实际上,我们可以在AH之间找到合并基础,它是...... commit *。这实际上是一个相当不错的合并基础,尽管Git可以按原样应用补丁,而不必回退到三向合并代码。

因此,提交*是将A挑选到H时的合并基础。合并完成后,我们得到新的提交A'。 (例如,它的新SHA-1 ID可能是aaaaaa1。可能不是;我们只需将其称为A'。)

现在我们挑选B。这会使B与其父A进行区分,并尝试将差异应用于A'

然而,

提交A'已从提交B稍微偏离。事实上,我们可以找到BA'之间的合并基础,即...再次提交*。不幸的是,这是一个可怜的合并基地。幸运的是,如果补丁不能按原样应用,Git只会依赖它,通常它可以。但如果它不能, Git会将*B* vs A'区分开,并尝试合并这两个差异。请注意,* vs B包含我们在A中所做的所有更改,但* vs A'也包含所有相同的A更改,所以如果我们很幸运,Git会注意到已经合并的更改并且不会复制它们。 编辑 Git作弊。 (此代码最近在版本2.6中已更改,但总体策略保持不变。)

当用于显示从提交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。但是对于将要出现的和你必须解决的冲突,它们是相同的。