我在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文件出现合并冲突的原因是什么?可以采取什么措施避免将来发生这种情况?
答案 0 :(得分:4)
当存在多个合并库 时发生这些情况,合并合并库产生合并冲突。合并基础候选者是您选择要合并的提交的LCA,位于提交引起的子图中:
定义3.1。假设 G =(V; E)为DAG,而 x; y ∈ V 。令 G x; y 是由 x 和 y 的所有共同祖先集合诱发的 G 的子图。将 SLCA(x; y)定义为 G x中的出度0节点(叶)的集合; y 。 x 和 y 的最低共同祖先是 SLCA(x; y)的元素。
(请参见https://www3.cs.stonybrook.edu/~bender/pub/JALG05-daglca.pdf)。这里 G 是由存储库中的提交形成的有向无环图,而 x 和 y 是您要合并的两个提交。我实际上更喜欢定义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不会停止并从您(用户)那里获得帮助。它只是提交冲突的结果。这成为外部git merge
(您直接要求的输入)的输入:
...--o--o--*---o--o--o <-- branch-C
\ /
M <-- temporarily-committed merge result
/ \
...--o--o--*---o--o--o <-- branch-D
临时提交实际上不是在图中 ,但出于外部合并的目的,也可能是这样。
现在,我们了解问题的产生方式,以及避免问题的方法更加清晰-至少不是完全清晰的 clear ,而是清晰的 er :>
方法1:避免纵横交错。
通过避免任何会产生多个合并基础的事情,您根本不会陷入困境。这种交叉合并是在这种情况下通过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 —运行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
,定时炸弹被放置。直到 you 之后,它才开始生效,后来试图合并提交内容,但这就是它的设置。
方法2:如果您做进行此类合并,请小心使用它们。编辑:这实际上无济于事:Git仍在使用 提交M1
和M2
之前的提交,并重新合并它们。尽管如此,还是值得考虑一下。我将其中一些文本保留下来。
合并一般都是对称的。请注意,当个人C和D运行两个git merge
命令时,他们具有相同的提交,形成了相同的图。他们从相同的合并库开始,具有相同的两个分支提示提交。通过比较合并基础与每个分支尖端而产生的两个差异是相同的差异。
因此,人员C和D必须解决一些冲突,就像您在自己的合并合并库中看到的一样。他们可能会自动执行此操作:例如,对于您在git merge -X ours
中发现的冲突文件,人C可能已经运行M1
倾向于他的提示提交更改,而人D可能还会 运行git merge -X ours
来更喜欢M2
中的她提示提交更改。
这些非对称结果是无害的,除了定时炸弹本身以及您以后进行递归合并的事实。您会看到冲突。再次由您决定解决这个问题,但是这一次,如果对C和D人有用,那么我们/他们的绝招绝非易事。
方法3(简单但可以说是错误的):使用不同的合并策略。
递归合并策略-s recursive
是采用此图的策略,找到 all 个合并基础提交候选者,如果有多个,则将其合并并使用结果作为您的合并的输入。有一种名为 resolve (-s resolve
)的策略,几乎与 recursive 策略共享其所有代码。区别在于,在(或之后)计算所有合并基准时,仅需要多个基准中的一个。
在这种情况下,具有两个合并候选M1和M2,-s resolve
显然只是随机地选择M1或M2(实际上不是随机的,无论哪个首先出现,但都不能以受控的方式进行:没有明显的理由选择一个与另一个)。 Git将使用该一次提交作为合并基础。
这里的问题很明显:通过使用一个提交(人C或人D),您就忽略了其他人选择的冲突解决方案。两个人这次留下了炸弹,而您用-s resolve
的答案是让炸弹摧毁两个结果之一,而我没有看到谁是正确的,还是应该做些更好的事情。
无论如何,都没有一个正确的答案,这与任何合并冲突都是一样的。问题是您现在正在解决一个冲突,该冲突可能在过去的某个时候已经解决了,这是在进行这两个相互冲突的合并时。