首页 > 解决方案 > git merge:我是如何在 BASE 文件中遇到冲突的?

问题描述

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

<<<<<<<<< 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

我现在已经对文件进行了毫无根据的合并,所以没有未解决的问题,但我想了解可能出了什么问题。

我已经检查了由 标识的 BASE 提交git merge-base,它不包括所显示的合并冲突,因此已排除。这也是第一次遇到这种情况,尽管在此之前已在此存储库中进行了许多合并。

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

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

标签: gitmerge

解决方案


行话回答

当有多个合并基础并且合并合并基础会产生合并冲突时,就会发生这些情况。合并基础候选者是您选择合并的提交的 LCA,在提交引起的子图中:

定义 3.1。设G = (V; E)为 DAG,设x; yV。 _ 设G x; y是由xy的所有共同祖先的集合诱导的G的子图。定义SLCA(x; y)为G x中出度为 0 的节点(叶子)的集合;的。xy的最低共同祖先是SLCA(x; y)的元素。

(见https://www3.cs.stonybrook.edu/~bender/pub/JALG05-daglca.pdf)。这里G是由存储库中的提交形成的有向无环图,xy是您选择合并的两个提交。我实际上更喜欢定义 3.2,虽然它使用了posets,而且可能更专业:感觉更相关(实际上用于家谱,这就是 Git 正在做的)。

递归合并策略,-s recursive使用所有合并基础,合并每个合并基础 - 并提交结果,完成合并冲突 - 并将此临时提交用作新的合并基础。这就是问题第一部分的答案(“什么可能导致基础文件......发生合并冲突”)。

为避免这种情况,您有多种选择,但首先让我们更容易理解地描述问题。

长但更有用的答案

你提到你使用git merge-base. 默认情况下,git merge-base选择最佳合并库提交候选者之一并打印该人的哈希 ID 如果您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 不会停止并从您(用户)那里获得帮助。它只是提交冲突的结果。这成为 external 的输入git merge,即您直接要求的输入:

...--o--o--*---o--o--o   <-- branch-C
            \ /
             M   <-- temporarily-committed merge result
            / \
...--o--o--*---o--o--o   <-- branch-D

临时提交实际上并不图中,但出于外部合并的目的,它也可能是。

如何避免问题

既然我们看到了问题是如何出现的,那么避免它的方法就更清楚了——不完全清楚,但至少比以前更清楚了:

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

    通过避免任何会创建多个合并基础的事情,您一开始就不会陷入这种情况。这种纵横交错的合并是由在某种情况下创建的,branch-C并且branch-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
    

    然后,某人——可能是其他人,他们的 branch-C;上没有新的提交。让我们称这个人为 D-ran 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:如果您确实进行了此类合并,请小心处理 编辑:这实际上无济于事:Git 仍在使用提交和 之前的提交并重新合并它们。不过,还是值得考虑一下。我会留下一些文字。M1M2

    合并通常是对称的。请注意,当人员 C 和 D 运行他们的两个git merge命令时,他们具有相同的提交,形成了相同的图。他们从相同的合并基础开始,具有相同的两个分支提示提交。通过将该合并基础与每个分支尖端进行比较而产生的两个差异是相同的差异。

    所以 C 人和 D 人必须解决一些冲突,就像你在自己的合并合并库中看到的一样。他们可能已经自动这样做了:例如,对于您在 in 中找到的冲突文件,人 C 可能已经运行git merge -X ours以更喜欢他的M1提示提交更改,而人 D 也可能已经运行git merge -X ours更喜欢在 中的提示提交更改M2

    这些不对称结果是无害的,除了定时炸弹本身以及您后来运行递归合并的事实。你会看到冲突。由你来解决它——再一次——但这次我们/他们的把戏并不容易,如果这对 C 和 D 人有效的话。

  • 方法 3(简单但可以说是错误的):使用不同的合并策略。

    递归合并策略-s recursive是采用此图的策略,它找到所有合并基本提交候选者,如果有多个,则合并它们并将结果用作合并的输入。有一种称为resolve ( ) 的策略,它与递归-s resolve策略共享几乎所有的代码。不同之处在于,当(或之后)计算所有合并基时,它只需要多个基中的一个。

    在这种情况下,有两个合并候选者 M1 和 M2,-s resolve只会随机选择 M1 或 M2(实际上不是随机的,只是先弹出一个,但不是以一种控制良好的方式:没有明显的理由选择一个 vs另一个)。Git 将使用该提交作为合并基础。

    这里的问题很明显:通过使用一个提交——C 人或 D 人——你忽略了另一个人选择的冲突解决方案。这两个人给你留下了这个定时炸弹,你的回答-s resolve让炸弹摧毁两个结果之一,而不看我看谁是对的,或者是否应该做更好的事情

无论如何,没有一个正确的答案,这与任何合并冲突相同。问题是您现在正在解决一个冲突,该冲突可能在过去的某个时候应该已经解决,当时进行了这两个冲突的合并。


推荐阅读