git合并:为什么我的BASE文件出现了冲突?

10

我在 git merge 的 BASE 文件中有以下内容:

<<<<<<<<< Temporary merge branch 1
    - _modifiedTimeWeak = 4.25.2019::11:41:6;
    - _lastID = 3;
    - weakCGTime = 4.25.2019::11:42:20;
    - strongCGTime = 1.2.1990::0:0:0;
=========
    - _modifiedTimeWeak = 5.1.2019::8:52:36;
    - _lastID = 3;
    - weakCGTime = 5.1.2019::8:52:36;
    - strongCGTime = 3.20.2019::17:13:20;
>>>>>>>>> Temporary merge branch 2

我现在已经对文件进行了无端合并,因此没有未解决的问题,但我想了解可能出了什么问题。

我已经检查了由git merge-base识别的BASE提交,它并没有包含所呈现的合并冲突,因此可以排除这种情况。尽管在此之前,在这个存储库中进行了许多合并,但这也是第一次遇到这种情况。

值得注意的是,我使用了git merge merge-tool

在执行合并时,什么会导致BASE文件出现合并冲突,并且可以采取哪些步骤来避免将来发生这种情况?

1个回答

17

术语解释

当有多个合并基础(merge bases),且将这些合并基础进行合并产生合并冲突时,就会出现这种情况。合并基础候选是您选择合并的提交的LCA,在由提交构成的子图中:

定义3.1:设G = (V;E)为DAG,而x、y ∈ V。令Gx; y为G中由x和y的所有共同祖先组成的子图。定义SLCA(x; y)为Gx; y中出度为0的节点(叶子)的集合。x和y的最低公共祖先是SLCA(x; y)的元素。

(参见https://www3.cs.stonybrook.edu/~bender/pub/JALG05-daglca.pdf)。这里,G是您的存储库中提交形成的有向无环图,而x和y是您选择合并的两个提交。实际上,我更喜欢定义3.2,虽然它使用了poset,可能更加术语化:但它更容易理解(实际上也用于家谱研究,这正是Git在做的事情)。

“递归”合并策略(-s recursive)使用所有合并基础,合并每个合并基础——并提交结果,同时包括合并冲突,并将此临时提交用作新的合并基础。所以这就是对第一个问题的回答(“什么可能导致基文件...有合并冲突”)。

为了避免这种情况,您有几个选择,但首先让我们更加易懂地描述一下问题。

长但更有用的回答

您提到使用了git merge-base。默认情况下,git merge-base选择了一个最佳合并基础提交,并打印该提交的哈希值。如果您在两个分支末端提交上运行git merge-base --all,则会发现存在多个最佳合并基础提交候选项。

在典型、简单的分支和合并模式中,我们有:

             o--o--o   <-- branch-A
            /
...--o--o--*
            \
             o--o--o   <-- branch-B

根据引用论文中3.1或3.2的方法找到的共同合并基础(使用3.2即可从两个分支尖端向后遍历,直至找到在两个分支上都存在的提交)当然是提交*,Git只需要将*与分支A和分支B的两个尖端提交进行差异比较。

但不是所有的图形都那么整齐简单。获得两个合并基础的最简单方式是在历史记录链中有一个十字形合并,如下所示:

...--o--o--*---o--o--o   <-- branch-C
            \ /
             X
            / \
...--o--o--*---o--o--o   <-- branch-D

注意,两个带星标的提交都在两个分支上这两个提交与两个分支的末尾同样接近。运行git merge-base --all branch-C branch-D将打印出这两个提交的哈希ID。Git应该使用哪个提交作为合并基础?

Git的默认答案是:让我们全部使用它们!实际上,Git会运行:

git merge <hash-of-first-base> <hash-of-second-base>
作为一个递归合并的(内部)合并。此合并可能存在冲突!
如果合并存在冲突,Git 不会停下来向您这个用户寻求帮助。它只会提交存在冲突的结果。这将成为外部 git 合并的输入,也就是您直接请求的那个。
...--o--o--*---o--o--o   <-- branch-C
            \ /
             M   <-- temporarily-committed merge result
            / \
...--o--o--*---o--o--o   <-- branch-D

临时提交实际上不在图表中,但是为了外部合并的目的,它可以视为在图表中。

如何避免问题

现在我们看到问题出现的原因后,避免它的方法变得更加清晰,虽然不是完全清晰,但至少比之前清晰:

  • 方法1:避免交叉合并。

    通过避免创建多个合并基础的任何操作,您就不会首先遇到这种情况。这种交叉合并是通过在某一时刻将 branch-Cbranch-D 处于以下状态而创建的:

           o--o--o   <-- branch-C
          /
    ...--o
          \
           o--o--o   <-- branch-D
    

    某人,比如C人,运行了git checkout branch-C; git merge branch-D来获取:

           o--o--o---M1   <-- branch-C
          /         /
    ...--o         /
          \       /
           o--o--o   <-- branch-D
    

    之后,可能是其他人(没有新提交的他们自己的分支-C),让我们称其为D,运行git checkout branch-D; git merge <commit that was the previous tip of branch-C before the new merge was just added>,以获取:

           o--o--o   <-- branch-C
          /       \
    ...--o         \
          \         \
           o--o--o---M2   <-- branch-D
    

    当C和D两人分享了他们的新提交后,结果如下:

           o--o--o---M1   <-- branch-C
          /       \ /
    ...--o         X
          \       / \
           o--o--o---M2   <-- branch-D
    

    时间炸弹已经设置。 这只有在您之后尝试将提交合并到线路中时才会生效,但这是设置的原因。

    方法2:如果进行此类合并,则要小心处理它们。 合并通常是对称的。 人C和D运行了两个git merge命令,并且采用了相同的提交,形成相同的图形。 他们从相同的合并基础开始,具有相同的两个分支末端提交。 与每个分支提示相比较的两个差异是相同的差异。 因此,他们必须解决一些冲突,您可能已经在自己的合并合并基础中看到了相同的冲突。

    方法3(简单但可能错误):使用不同的合并策略。 递归合并策略(-s recursive)找到此图形,找到所有合并基础提交候选项,如果有多个,则合并它们并将结果用作输入。您的合并。 有一个名为resolve(-s resolve)的策略,与递归策略共享几乎所有代码。


哇,比我预想的答案要详细得多。谢谢! - Baldrickk
重新阅读后,方法2实际上并没有帮助...我会进行一些编辑。 - torek
嗯...我使用一个包装脚本来调用不同的diff/merge工具。我在想是否可以先对多个父级调用git-merge以解决BASE文件中的冲突。即通过手动调用git mergetool <hash-of-first-base> <hash-of-second-base>来绕过git的git merge <hash-of-first-base> <hash-of-second-base>。发现的冲突是因为我们使用的工具对时间戳进行更改,而不是因为重叠的编码,所以应该很简单。 - Baldrickk
Rebase 复制(一些)提交。在复制完成后,rebase 移动分支名称,该名称原来指向原始(pre-copy)最顶端的提交,以便它指向 post-copy 最顶端的提交。如果您已经合并了某些提交,使得它们可以从其他分支顶端到达,则会得到两个提交的副本。这确实可能会改变复制提交的合并基础,但可能会让您面临不同的(也许更难处理的)问题。 - torek
也许不是最理想的解决方案,但如果您可以修改时间戳工具和其他软件,使时间戳内容保存在Git提交中,或者将其放入一个独立的文件中,您可以自由地擦除和重新生成,这也可以避免这个问题。 - torek

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