这个例子中展示的(带有Temporary merge branch
标记的)是一个由交叉式合并冲突引起的diff3结果。我将通过一系列定义来解释这个过程。
定义
- 合并基底(merge base):两个合并分支最近分叉的提交。当出现合并冲突时,不同的更改被应用于这些分支中的相同行。合并基底包含了这些行在任何一个分支修改前的状态。
- 合并的共同祖先(merged common ancestors):diff3会输出一个额外的“中间”部分,显示它们在合并基底中的状态。这是两个分支的起始点。
- 交叉式合并(criss-cross merge):指两个分支以无法进行快进合并的方式合并到彼此。下面举例说明。在交叉式合并情况下,存在多个合并基底。
- 临时合并分支(Temporary merge branch):当存在多个合并基底时,diff3尝试将它们(使用临时合并分支)合并在一起,形成一个单一的共同祖先,以显示在diff3的中间部分。当没有冲突时,这个过程很顺利,但是当出现冲突时,在合并的共同祖先部分中会看到临时合并分支的冲突标记。
交叉式合并冲突示例
每当两个分支在不同时间点合并到彼此时,就会发生交叉式合并。
m3 *
|\
| \
| * B1
| |
m2 * * B0
|\/|
|/\|
m1 * * A
| /
|/
m0 *
考虑以下事件序列:
m0
存在于 origin/master 分支上
- 我创建了一个名为
feature-A
的功能分支,并提交了一次提交 A
- 别人将
m1
提交到 master 分支上
- 我又开始了一个建立在
A
基础上的新功能分支 feature-B
- 我将
origin/master
(m1
) 合并到 feature-B
分支。发生了冲突,我解决了它。合并提交是 B0
- 我实现了 feature-B 并将其作为提交
B1
提交
feature-A
准备好进行发布,有人将其合并到了 master
分支。发生了冲突。他们解决了冲突,但他们的解决方案与 B0
中的解决方案不同。合并提交是 m2
feature-B
准备好进行发布,有人将其合并到了 master
分支。git 尝试确定合并基础,但是 m1
和 A
同样都符合合并基础的条件。git 在一个临时的合并分支中合并了 m1
和 A
,导致冲突。我们在“merged common ancestors”部分看到 diff3 输出,类似于 OP 的问题。
阅读输出结果
如果 diff3 关闭,则此合并冲突会简单地显示如下:
<<<<<<< HEAD
aaaaaa
=======
hhhhhh
>>>>>>> mybranch
首先,通过所有额外标记,您需要确定实际冲突的行,以便将其与diff3公共祖先输出区分开来。
aaaaaahhhhhh,这好多了。;-)
在两个冲突解决方案相互冲突的情况下,aaaaaa和hhhhhh是两个解决方案。
接下来,检查合并的公共祖先的内容。
对于这种合并历史,存在多个合并基地,需要多个临时合并分支,然后将它们合并在一起。当有许多合并基地和冲突时,结果可能会变得非常混乱且难以阅读。有人说不要管它,只需为这些情况关闭diff3。
还要注意,git内部可能会决定使用不同的合并策略来自动解决冲突,因此输出可能很难理解。如果可以理解,则可以理解,但要知道它并不是针对人类消费而设计的。在这种情况下,在合并mybranch到Temporary merge branch 1之间的bbbbbb和cccccc之间发生了冲突。dddddd行在临时合并分支之间没有冲突。然后,在将Temporary merge branch 2合并到HEAD时发生了独立的冲突,有多个公共祖先。 HEAD通过将ffffff和gggggg合并为eeeeee来解决了冲突,但Temporary merge branch 2通过删除(或移动)该行来解决了相同的冲突(因此没有======和Temporary merge branch 2之间的行)。
如何解决这样的冲突?虽然可能进行技术分析,但通常最安全的选择是返回并审核所有涉及冲突的分支中的历史记录,并根据您的理解手动制定解决方案。
避免所有这些
这些冲突是最糟糕的,但是有一些行为可以帮助防止它们。
1.避免交叉合并。在上面的示例中,feature-B将origin / master合并为B0。可能不需要此合并以保持与主机同步(尽管有时确实需要)。如果从未将origin / master合并到feature-B,则不会出现合并交错,并且m3将成为仅具有A作为唯一合并基地的普通冲突。
m3 * m3 *
|\ |\
| \ | \
| * B1 | * B1
| | | |
m2 * * B0 VS m2 * |
|\/| |\ |
|/\| | \|
m1 * * A m1 * * A
| / | /
|/ |/
m0 * m0 *
在冲突解决方面保持一致性。在这个例子中,临时合并基础冲突只发生在m2
和B0
的冲突解决方案不同的情况下。如果他们以相同的方式解决了冲突,m3
将是一个干净的合并。然而需要意识到,这只是一个应该具有相同决策的简单十字路口合并。在其他情况下可能会有不同的处理方式。当存在超过2个合并基础和多个提交点之间的合并点时,事情变得更加复杂。也就是说,如果你在十字路口的情况下有意不一致地处理冲突,那么后面可能会遇到问题。
<<<<<<
吗? - Ciro Santilli OurBigBook.com