Git如何决定冲突?

29

当我从GitHub拉取代码时,如果团队中的其他成员修改了某个文件,Git命令会显示存在未解决的冲突,而Git无法解决。我打开Perforce或Visual Studio 2012来解决冲突,图形工具会显示没有冲突,并快速执行合并操作。

Git是否具有内置的冲突检测/合并工具,与您设置的mergetool/difftool无关?

还是它使用您配置的工具自动合并和检测不可解决的冲突?

我的情况很奇怪,Git显示它无法自动合并,但图形工具却没有问题。


你的问题并不是独一无二的,我也经常遇到这种情况,但从未花时间研究为什么会发生这种情况。 - user456814
1
我自己从未遇到过这种情况 - 如果您能在公共git主机(例如Github)上发布发生此情况的情况,那将非常有帮助。 - Chronial
VonC的回答中,他解释了Git使用的“经典的三方合并算法”。有关详细信息,请参见该答案。 - user456814
4
我认为这种情况可能会发生在人们混合使用自动换行符(autocrlf)但不一致地使用其中之一时。 - Andrew T Finnell
1
@AndrewFinnell 非常好的一点,可视化差异工具可以配置为不将行尾更改显示为重要,这种情况下,文件可能会显示为相同,而实际上已经发生了行尾更改。 - user456814
我想@AndrewFinnell有一个非常好的观点,但在这种情况再次发生之前,我无法确定。下面的答案给了我所需要的一切。 - Tamim Al Manaseer
4个回答

15
Git有一个内部合并系统,与difftool无关。无论如何,您可以使用手册中的-s选项指定合并策略。
-s <strategy>
--strategy=<strategy>

使用给定的合并策略;可以多次提供以指定它们应该尝试的顺序。如果没有 -s 选项,则使用内置的策略列表代替(在合并单个头时使用 git merge-recursive,在合并多个头时使用 git merge-octopus)。
从简单的快进到合并许多树的章鱼策略,策略是多种多样的。请查看此 answer 以了解不同的合并策略如何工作。
无论如何,您的情况很奇怪。也许您的 IDE 正在使用不同的合并策略,因此当它打开时会以正确的方式进行合并。

13
正如Atropo在他的答案中所述

Git有一个独立于difftool的内部合并系统。

因此,Git决定何时发生冲突的变更是自主完成的,而不是使用您正在使用的任何外部diff或merge工具(这些工具可能会使用其自己的冲突检测和解决策略)。
根据VonC对Git中合并冲突的定义的回答(我强调): 我认为合并算法在Git中没有什么特别之处:它是一种经典的3路合并算法不是Codeville的那种),可以使用多种策略(默认为递归、解决或章鱼合并)。 结果是一个相当简单的合并过程在这里描述
任何可视化需求都会被委托给第三方合并/差异工具。
他提到的维基百科文章这样解释3-way merge(重点是我的):
在自动比较文件'A'和文件'B'之后,同时考虑这两个文件的共同祖先,进行三方合并。虽然是一种粗略的合并方法,但非常适用,因为它只需要一个共同的祖先来重建要合并的更改。三方合并使用已更改文件的祖先来识别内容块,在派生版本中未更改、仅更改或两者都更改的情况下。未更改的块保持不变。仅更改一个衍生版本的块使用该更改版本。如果一个块在两个衍生版本中都被更改,如果两侧的内容相同,则使用更改的版本,但如果更改不同,则标记为冲突情况,并留给用户解决。三方合并由无处不在的diff3程序实现,并且是允许从基于文件锁定的版本控制系统转换到基于合并的版本控制系统的核心创新。它被Concurrent Versions System(CVS)广泛使用。
文章还讨论了递归三方合并,我在Git中经常看到它的使用:
广泛使用基于三方合并的版本控制工具已经出现了一些情况,在这些情况下,简单的三方合并会导致虚假的冲突,并且有时会默默地重新执行已撤销的更改。这种情况的例子包括交叉合并和三方重命名冲突。
三方合并的问题出现在两个派生状态没有一个唯一的最新公共祖先(LCA)的情况下。为了解决这些问题,递归三方合并通过首先合并非唯一祖先来构建虚拟祖先。这种技术被Git版本控制工具使用。
递归三方合并只能在工具具有派生品的总谱系图(有向无环图)的知识的情况下使用。因此,在派生品或合并没有完全指定其父项的情况下,它无法使用。

2
是的,许多IDE和合并工具都会在git自身的基础上进行自动化冲突解决。
git help merge-file所示,git对于文本文件的库存冲突解决算法相当简单:diff块冲突当且仅当它们更改相邻行。如果git无法以这种方式自动解决冲突,它将放置一个文本冲突标记,该标记已成为自RCS时代以来的标准格式,IDE可以使用该标记并进行操作(即使不知道git):
<<<<<<<< 版本标识符1
Foo
=======
Bar
>>>>>>>> 版本标识符2
我不使用Visual Studio,但是快速搜索告诉我它有一个自动冲突解决功能。(我自己使用kdiff3,它有自己的自动冲突解决算法)

1

这里的帖子是我在git合并策略方面看到的最佳答案。是的,你可以使用-s选项选择想要的策略。

但你的问题是为什么其他工具如Visual Studio可以合并而git不能。Visual Studio可能有一种相当激进的合并策略 - “自动合并功能通常不会使用您旧版本中的任何内容,因为自动合并功能的默认设置偏爱最近的文件”见这里获取更多信息。

要了解有关与VS 2012一起提供的Merge Tool的更多信息,请参见此帖子,其中Brian Harry深入探讨了他们如何考虑“合并时最小冲突”作为一个关键特性。

关于Perforce,我怀疑他们有类似的合并策略 - git到p4连接器必须使用perforce合并策略来解决问题,我相信它们更积极。
"ours"可能是git中类似的积极合并策略。
但长话短说,每个产品都在使用自己的合并策略,有些更积极,有些不那么积极。这取决于您的用例和需求,哪些是可以接受的。

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