使用 git 和 meld 进行交互式变基的 3 路合并中的三个文件是啥?
Posted
技术标签:
【中文标题】使用 git 和 meld 进行交互式变基的 3 路合并中的三个文件是啥?【英文标题】:What are the three files in a 3-way merge for interactive rebasing using git and meld?使用 git 和 meld 进行交互式变基的 3 路合并中的三个文件是什么? 【发布时间】:2016-08-27 21:09:51 【问题描述】:假设我使用git rebase -i
进行交互式变基。如果出现一些冲突,我可能会遇到合并冲突并被要求进行 3 路合并。使用meld
,我看到了三个窗口:LOCAL
(左)、???
(中)和REMOTE
(右)。这里???
的意思只是meld
没有提供一些特殊的名称来附加到文件中。
在正常合并期间,这是有道理的,因为中间是共同的祖先,而您正在将本地和远程更改合并到该祖先。然而,在交互式 rebase 期间,情况似乎并非如此 - 不清楚每个文件代表什么。
在交互式变基期间,3 路合并中的这些文件各自代表什么?而在编辑这些文件时,我的目标是什么?
更新:基于我看到的 cmets 和实验:
左 (LOCAL
):提交重播序列中此时文件的本地版本。
右 (REMOTE
):在最初应用当前提交之后的文件状态。
中间:原始提交序列中右的父级。
因此,我的任务是确定从中间到右侧的增量,然后将此增量应用于左侧。 Middle 应该反映在新的提交序列中应用当前提交增量后文件的状态。
请注意,这种配置似乎是特定于融合的,至少在某种程度上是这样。 Git 的 3 路合并行为可能因其他编辑器而异。
【问题讨论】:
【参考方案1】:中间版本是合并基础,就像git merge
一样。
(名称“other”可能比“remote”更合适,因为没有要求合并的另一端是远程的,并且由于 Mercurial 始终使用名称“other”,而不是 Git 需要匹配 Mercurial,但有些一致性可能会很好。请注意,Git 在这里也使用名称“我们的”和“他们的”,所以我们永远不会从 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
和 *
与 A'
并尝试合并这两个差异。请注意,*
与 B
合并了我们在 A
中所做的所有更改,但 *
与 A'
也合并了所有相同的 A
更改,所以如果我们幸运的话,Git 会注意到已经- 合并更改并且不复制它们。
当用于显示从提交A
到提交B
的更改时,请考虑git diff
的实际输出。这包括index
行:
diff --git a/foo b/foo
index f0b98f8..0ea3286 100644
左边的值是提交A
中文件foo
版本的(缩写)哈希。右边的值是提交B
中文件版本的哈希值。
Git 只是从左侧哈希中伪造了一个合并基础。换句话说,提交A
中的文件版本成为伪造的合并基础。 (Git 将 --build-fake-ancestor
传递给 git apply
。这要求特定的文件 blob 对象在存储库中,但它们是因为它们在提交 A
中。对于通过电子邮件发送的补丁,Git 使用相同的代码,但 blob 可能或可能不存在。)
请注意,Git 实际上在选择提交 A
时也会这样做,但这次合并基础文件是来自提交 *
的版本,这实际上是 合并基础。
最后,我们挑选C
。这区分了B
与C
,就像我们上次区分A
与B
一样。如果我们可以按原样应用补丁,那很好;如果不是,我们退回 再次使用提交 和以前一样,假装*
作为合并基础。它又是一个非常糟糕的合并基础。B
中的版本是公共基础。
顺便说一句,这也解释了为什么你会一遍又一遍地看到相同的合并冲突:我们每次都使用相同的合并基础。 (启用git rerere
会有所帮助。)
【讨论】:
我现在正在做一个变基,中间不是左右的共同祖先 - 它只是右边(远程)的祖先,并且似乎是左边的继承者(当地的)。如果它是一个合并基地,这怎么可能?请注意,在我的 rebase 中,我正在从历史记录中删除一个提交,中间表示应用删除的提交时文件的状态。 嗯,究竟如何?我将在此处注意您正在查看一个文件,这不是一个提交(因此我们不能使用文件的 ID 来派生提交的 ID,除非该文件的特定版本对于一个特定的提交是唯一的)。某些合并工具也可能会做一些不同的事情以提供帮助。例如,Git 中有一些代码可以提取两个提交提示文件的公共部分,一些工具可能会在此过程中使用这些部分。特别是 p4merge 使用来自 git-sh-setup 的create_virtual_base
。我对meld
一无所知。
啊哈,我对此很好奇,所以我翻遍了git am
源代码,发现我对这里的合并基数有误。除了 git 作弊之外,这将是答案!
@CodeWizard,杰克:更新了答案。它实际上是来自父提交的版本(在这种特殊情况下,但不适用于只有 blob ID 而没有实际父提交 ID 的电子邮件补丁)。
有趣!感谢您写出如此详尽的答案,并在此基础上进行了额外的研究。这确实帮助我理解了合并/变基过程:)【参考方案2】:
在这方面,合并和变基是相同的。合并和变基之间的唯一区别是,使用变基的历史看起来更好(更线性)。但是对于即将出现的和你必须解决的冲突,它们是相同的。
【讨论】:
你能澄清一下吗?合并涉及两个分支。交互式变基涉及重新排序或修改一个分支中的提交。所以他们不一样。因此,不清楚本地、远程和共同祖先是什么。以上是关于使用 git 和 meld 进行交互式变基的 3 路合并中的三个文件是啥?的主要内容,如果未能解决你的问题,请参考以下文章