为什么三路合并比两路合并更有优势?

256
维基百科说,三路合并比两路合并更少出错,并且通常不需要用户干预。为什么会这样呢?
提供一个三路合并成功而两路合并失败的例子会很有帮助。
4个回答

374
假设您和朋友都检出了一个文件并对其进行了更改。您删除了开头的一行,而您的朋友在结尾添加了一行。然后他提交了他的文件,您需要将他的更改合并到您的副本中。
如果您进行的是双向合并(换句话说,是差异),则工具可以比较两个文件,并查看第一行和最后一行有何不同。但是它如何知道如何处理这些差异?合并版本应该包含第一行吗?应该包含最后一行吗?
通过三向合并,它可以比较两个文件,但也可以将每个文件与原始副本(在您或您的任何人更改之前)进行比较。因此,它可以看到您删除了第一行,您的朋友添加了最后一行。并且它可以使用该信息生成合并版本。

8
“但它如何知道如何处理这些差异?”如果它已经能够看到两个文件的不同之处(无需参考原始文件),为什么不能按照文件时间戳的递增顺序连续应用两个更改呢?也就是说:它从我的朋友提交的副本开始,将其视为(新的)原始文件(在顶部添加了一行),然后在此基础上应用我的本地更改(在底部删除一行)。 - Harry
64
原文有三行(ABC),以我的朋友的副本(ABCD)作为起点,并将其与我的副本(BC)进行比较。如果没有看到原始文件,可能会认为我删除了A和D两个字符,因此最终结果应该是BC。请注意,翻译保持原意并尽可能通俗易懂。 - JW.
2
@Harry 如果每个文件都有自共同祖先以来的时间戳更改列表,那么您将进行三方合并。您所描述的方法需要将文件倒回到共同祖先,以便按时间顺序应用差异。换句话说,“在没有参考共同祖先的情况下,在两个文件之间进行时间戳差异”可能没有明确的含义。 - Jake Stevens-Haas

124

这张幻灯片 来自Perforce的一个演示,内容很有趣:

slide image

三方合并工具的基本逻辑很简单:

  • 比较基准文件、源文件和目标文件
  • 识别源文件和目标文件中的“块”:
    • 与基准不匹配的块
    • 与基准匹配的块
  • 然后,组合合并结果,包括:
    • 在所有3个文件中都匹配的块
    • 在源文件或目标文件中与基准不匹配但不是同时不匹配的块
    • 与基准不匹配但相互匹配的块(即,在源文件和目标文件中以相同方式更改)
    • 冲突块的占位符,由用户解决。

请注意,这张插图中的“块”是纯粹象征性的。每个“块”都可以代表文件中的行、层次结构中的节点,甚至是目录中的文件。这完全取决于特定合并工具的功能。

您可能会问3向合并相对于2向合并有什么优势。实际上,不存在所谓的2向合并,只有能够比较两个文件并允许您通过选择一个文件中的块或另一个文件中的块来“合并”的工具。
只有3向合并才能让您知道一个块是否对源文件作出了更改以及更改是否冲突。


“是否有冲突变更。”- 不进行双向合并(diff)也会显示冲突(尽管冲突来源的信息丢失了)。 - Vlad
3
在Git中,通常会出现基础不同的四方合并,但是三方合并和两方合并更好。 - Wernight
@Wernight,有五路合并吗? - Pacerier
1
@Pacerier 我不知道有没有这样的情况,但这确实是在进行 git cherry-pick 或 rebase 时发生的事情。 - Wernight
1
@Qwerty,这更多是关于配置合并冲突的“可视化”。合并本身已经是Git的三方合并:https://dev59.com/Fabja4cB1Zd3GeqPhIHu#47115936 - VonC
显示剩余3条评论

35

三路合并是指两个变更集在应用时被合并,而不是先应用一个变更集,然后将结果与另一个变更集合并。

例如,在同一个位置添加了一行的两个更改可能被解释为两个添加,而不是一行的更改。

例如,文件a已被两个人修改,一个人添加了moose,另一个人添加了mouse

#File a
    dog
    cat

#diff b, a
    dog
+++ mouse
    cat

#diff c, a
    dog
+++ moose
    cat

现在,如果我们在应用更改集时合并它们,我们将得到(三方合并)

#diff b and c, a
    dog
+++ mouse
+++ moose
    cat

但是,如果我们应用 b ,然后查看从 b 到 c 的更改,它看起来就像我们只是将一个“u”更改为一个“o”(两路合并)。

    #diff b, c
    dog
--- mouse
+++ moose
    cat

-1

enter image description here

从AWS CodeCommit借鉴:开发人员工具 > CodeCommit > 存储库 > 存储库名称 > 拉取请求 > 拉取请求名称 > 合并


在图片中,哪一行代表“源分支”,哪一行代表“目标分支”? - JP Zhang
源是黑点,蓝点是正在添加的内容,白点是合并后的结果。 - jumping_monkey
这个答案与回答“为什么三路合并优于二路合并?”的问题根本不相关。 - hlovdal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接