Git 冲突标记 syntax/rules 是什么?

What are the Git conflict markers syntax/rules?

我想知道 Git 冲突标记的规则是什么?它们是堆叠的吗?打开(<<<<<<<)和关闭(>>>>>>>)的规则是什么?我尝试搜索 Git 文档,但没有关于这些的明确说明,我找到的唯一示例是简单的、单一的、非堆叠的冲突。

上下文: 我需要同步两个部分具有相同目录结构的独立 b运行ches。我正在从一个创建补丁并将其应用到另一个 b运行ch。冲突是可能的,但我总是想'ours'。

为了应用我使用的补丁: git apply --3way --ignore-whitespace --whitespace=fix <patchfile>

然后对于每个包含冲突标记的文件,我这样做: perl -i -0777 -pe '<{7} ours\r?\n((?:(?!<{7})(?!={7})(?!>{7}).*\r?\n)*?)={7}\r?\n(?:(?!<{7})(?!={7})(?!>{7}).*\r?\n)*?>{7} theirs\r?\n'

基本上我假设标记是堆叠的(从我注意到的)并尝试从内到外(最内到最外)解决它们。这是应用补丁后文件的样子:


<<<<<<< ours
<<<<<<< ours
<<<<<<< ours
{chunk of text}
<<<<<<< ours
=======
{chunk of text}
>>>>>>> theirs
=======
{chunk of text}
>>>>>>> theirs
{chunk of text}
<<<<<<< ours
=======
=======
>>>>>>> theirs
{chunk of text}
<<<<<<< ours
>>>>>>> theirs
=======
>>>>>>> theirs
=======
>>>>>>> theirs

{rest of file}

问题是现在我发现一个文件在 2 次替换迭代后达到这个状态:


<<<<<<< ours
<<<<<<< ours
{text chunk}
<<<<<<< ours
=======
=======
>>>>>>> theirs
{text chunk}
<<<<<<< ours
>>>>>>> theirs
=======
>>>>>>> theirs
=======
>>>>>>> theirs

<rest of file>

...我不知道该怎么做。这似乎没有堆叠。我该如何解决这些冲突?

注意:我还在在线调试器中 运行 手动迭代正则表达式,它匹配正常。

编辑澄清: 当我写这篇文章时,我没有提到我正在使用 git format-patch 生成补丁,这实际上生成了一个补丁每次提交都是为了保留元数据,因此有多个冲突标记。 @torek 在没有正确信息的情况下将其钉在他的答案中。

冲突标记不叠加。

当Git进行file-level合并时,有三个输入文件。1 Git称第二个文件为“我们的”第三个是“他们的”,第一个并没有真正的名字。然而,第一个来自合并基地。当你 运行 git merge, Git 在 commit 的基础上工作时,每个案例的三个文件来自三个 commit Git 调用合并基础、“我们的”提交和“他们的”提交。


1当您使用 git merge 时,这些规则特定于特定的 合并策略 ,但您将使用,在 work-tree 文件中产生冲突标记,请遵循这些规则。当您使用 git apply --3way 并且它必须执行真正的合并时,它会调用相同的代码。


但是您使用的是 git apply,它每次构建一个文件的每个步骤:

  • “我们的”文件是您 work-tree 或索引中的文件,具体取决于您给 git apply 的标志。我将在这里假设您正在使用 work-tree 文件(当使用索引副本时,还有很多其他不同之处,很明显您没有使用索引副本)。

  • “他们的”文件不是直接可见的:事实上,它还不存在。

  • “基础”文件可能也不存在。如果它不存在,并且补丁不能很好地应用,我们甚至不会走到这一步。如果它 确实 存在,则由补丁中的 index 行提供,其中包含原始哈希 ID。

所以,在这一点上,由于补丁正在被应用Git正在做一个three-way 合并文件,Git did 找到合并基础文件。因为补丁文本是从这个基本文件派生的,所以补丁很容易应用到基本文件,从而产生“他们的”文件。 Git 现在的工作是将 他们的更改 (即补丁)与 我们的更改 (基础文件与我们的)合并.

Git 现在进行第二次比较,从基本文件到我们的文件,以查看我们更改了什么。如果我们在第 43 行之前的第 42 行之后添加一行,并且他们没有触及第 42-43 行周围的行,Git 可以占用我们的额外行。如果他们更改了第 50 行,Git 可以更改基本文件的第 50 行(这是我们文件的第 51 行,因为我们添加了一行)。无论我们和他们触及文件中的不同行,这种组合都会发生。

虽然我们和他们接触了同一条线,或者接触了邻接的线(例如,如果我们更改了第 55 行,他们更改了第 56 行),Git 声明 合并冲突 。它写入 work-tree 文件:

<<<<<<< ours
our version
=======
their version
>>>>>>> theirs

然后继续使用未更改的行。如果我们将 merge.conflictStyle 设置为 diff3,Git 包括 base 版本的行,在这两个版本之间,用七个 [=21] 标记=]个字符。

此 work-tree 文件是您的新 work-tree 文件

您现在继续 运行 宁 second git apply 需要进行 three-way 合并。您的第二个 git apply 按原样使用 work-tree 文件,并且 假设这是我们认为文件应该看起来的样子 。 (当然不是!但是 Git 假定它是。)

因此,Git 现在找到文件的合并基础版本,并且 运行 是一个 git diff 以查看 我们 更改了什么。显然,我们 所做的更改是插入这些巨大的 <<<<<<< ours=======>>>>>>> theirs 标记以及我们和他们的更改。

与此同时,Git 将文件的基本版本与他们的文件版本(即您正在应用的补丁)进行比较,现在 Git 尝试将我们的更改与他们的更改结合起来。 有时,你会得到:

<<<<<<< ours
[the entire block from the first `git apply`]
=======
[theirs]
>>>>>>> theirs

正确嵌套,但有时,我们或他们的更改将“对齐”:两个差异将同步并且Git 实际上会说:啊哈,我们的文件版本与他们这里的相同。所以我们会得到:

<<<<<<< ours
[part of the block from ours]
=======
[part of the diff from theirs]
>>>>>>> theirs
the synchronized part
<<<<<<< ours
=======
...

第二个 <<<<<<< ours 是当差异再次“失去同步”并命中我们文件中的 ======= 时产生的——除非,也就是说,我们非常(不幸)幸运并且=======实际上是同步的一部分。

怎么办

运行宁git apply后,不要继续应用更多补丁。先解决问题

可选地,考虑使用 git merge-file 以便您可以更改冲突标记。