为什么会发生合并冲突?

8
例如:
  • 我有一个主分支。
  • 我创建了一个名为file.txt的文件,并将Master branch写入其中,然后提交了它。
  • 我创建了另一个名为fix20的分支,将file.txt的文本修改为This is fix20 branch。然后我提交了它。
  • 我切换到主分支,然后创建一个名为hotfix的分支并切换到它。
  • 我将file.txt修改为This is the HotFix。之后我提交了更改。
  • 现在我切换回主分支并与热修复合并。这个文件(没有问题)改变了它的文本This is the HotFix (它们基本上被合并)。

现在我与fix20分支合并,并出现了合并冲突。 file.txt有fix20分支的文本和合并HotFix后我的文本。此时,我应该打开编辑器并决定应该留下什么,应该删除什么。

问题是:为什么第二次会发生合并冲突?为什么当我合并主分支和热修复时没有决定要留下哪个文本?为什么当我与fix20分支合并时文件没有被合并(为什么文本不像热修复一样被替换)?为什么基本上第二次发生这种冲突而不是第一次?


1
分支fix20的提交历史与主分支不同,主分支没有这个历史记录,git(或任何其他版本工具)无法知道您想保留哪个文本或只想保留其中之一。 - Nikos M.
@NikosM。那么,当我将它与热修补合并时,为什么关于我想要保留什么的问题没有出现呢? - MyOwnFan
因为主分支更改了某个提交,但fix20也有另一个提交,所以工具无法知道。 - Nikos M.
@NikosM。这就像需要重新定位的情况吗? - MyOwnFan
除非您想要重新定位分支,否则不需要这样做。例如,您可能只需要将内容添加到分支中,而不是重新定位分支。 - Nikos M.
2个回答

12

问题在于你知道自己的意图,但Git并不知道。

这是Git看待你所做的事情的方式:你对同一行进行了两次独立的更改。当你合并第一个更改时,你的意图很明确:你想让这个更改成为主分支的一部分。由于自从你创建分支以来主分支本身没有任何更改,因此没有问题:如果只有一个合并的一侧更改了代码,Git就可以直接合并一段代码。

当你尝试合并第二个更改时,情况就不再如此:通过合并第一个分支,你引入了不直接与第二个分支中的更改“兼容”的主分支更改。Git称之为:“分支已经分叉”。Git不知道你是想保留从第一个分支合并的内容还是从第二个分支合并的内容。

在你的情况下,这对你来说很明显,因为第二个分支是相同事物的新修复版本......但许多合并涉及到代码的更复杂更改,也许合并第二个分支的正确方法是将来自两个合并的更改组合起来:通常,如果两个合并的更改触及相同的代码行,都必须对其进行标记。

示例原始代码:

send_request("bake cookies")

分支1:

send_request("bake cookies", additions: ["chocolate"])

第二分支:

send_request("bake cookies", flour: "wheat")

正确的合并这两个的方法可能不是逐字采用其中一行,而更像是这样:

send_request("bake cookies", flour: "wheat", additions: ["chocolate"])

因为您比Git更了解代码的意图,所以在这种情况下Git永远不会做出自动决策。


1
我想补充一下,像 Git 这样的版本控制工具都是使用差异(diffs)来工作的,差异描述了什么改变成了什么。在主分支的情况下,差异很明显,只有一行从一个状态变为了另一个状态,但在分支的情况下,差异不能直接应用(需要注意的是,所有版本控制工具都是这样,因为它们使用差异模式,不仅仅是 Git)。每个工具中的版本历史记录只是一系列差异(按顺序应用),Git 在其他方面与其他工具不同,但在这方面并没有区别。 - Nikos M.

3

阅读这个答案以获取完整细节。它是您所遇到的同样问题,但在稍微不同的情况下。


简而言之:

答案是存在冲突,因为这两个分支没有任何merge-base提交。


以下是您描述的步骤

enter image description here


我使用3-diff作为差异算法

以下是该文件的内容:

<<<<<<< HEAD
This is hotfix branch
||||||| merged common ancestors
Master branch
=======
This is fix20 branch
>>>>>>> fix20

我尝试生成一些关于你的问题的图形展示,但这需要太多时间。

我会在这里尽可能清晰地回答。

由于你有几个分支,每当你进行合并时,Git都会尝试找出分割点(即你派生并打开新分支的提交),然后从这个点生成差异,并尝试合并文件。

我们的情况如下:

master            master  master
   \----- hotfix --- /     |
   |                       |
   \----- fix20  ----------/


// To view the patch that will be created for the merge you can run
// this command and you will see the commits that will be merged.
git log ^master fix20 

在你的情况下,你会发现它试图合并更新了同一个文件的提交(两个分支都是从相同的提交[合并基础提交]创建的),这会导致冲突。


什么是merge-base?顺便说一句,我看到这篇文章和我的很像,但我仍然不明白为什么会发生这种情况。 - MyOwnFan
写下为什么,需要我几分钟时间。 - CodeWizard
请问您能否至少解释一下“merge-base”是什么意思?问题基本上是:“为什么会发生这种情况”,而您只是描述了它正在发生。 - MyOwnFan
稍等,我还在为您撰写完整的答案。再等几分钟。 - CodeWizard
1
根据合并基础的通用定义,这里肯定有一个,即提交c4d6cfb(根提交)。 - Jan Krüger

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