Git冲突标记的语法/规则是什么? 此工作树文件是您的新工作树文件该怎么办

问题描述

我对Git冲突标记的规则感兴趣?他们堆叠了吗?打开(>>>>>>)的规则是什么?我尝试搜索Git文档,但是没有明确的信息,我发现的唯一例子是简单,单一,非堆叠式冲突。

上下文: 我需要同步部分具有相同目录结构的两个独立分支。我正在从一个创建补丁并将其应用到另一个分支。冲突是可能的,但是我总是想接受“我们的”。

对于应用补丁,我使用: 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>

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

注意:我还通过在线调试器逐个迭代手动运行了regex,它与ok匹配。

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

解决方法

冲突标记不会堆叠。

当Git执行文件级合并时,有三个输入文件。 1 Git称第二个文件为“我们的”,第三个文件为“他们的”,并且实际上没有适当的名称。第一个的名称。但是,第一个来自合并基础。当您运行git merge时,Git在 commit 的基础上工作,每种情况的三个文件来自Git称为merge base的三个 commits 。我们的”提交和“他们的”提交。


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


但是您正在使用git apply,它一次将每个步骤构造一个文件:

  • “我们的”文件是您的工作树或索引中的文件,具体取决于您为git apply赋予的标志。我将在这里假设您正在使用工作树文件(使用索引副本时,其他方面的差异足以表明您没有使用索引副本)。

  • “其”文件不是直接可见的:实际上,它尚不存在。

  • “基本”文件也可能不存在。如果它不存在,并且补丁不能很好地应用,那么我们甚至都无法解决这一问题。如果它确实存在,则由补丁中的index行提供,其中包含原始哈希ID。

因此,在这一点上,由于正在应用补丁 ,并且 Git正在对该文件进行三向合并,因此Git did 查找合并基础文件。由于补丁文本是从此基础文件中派生的,因此该补丁很容易将应用至该基础文件,从而生成“其”文件。 Git现在的工作是将他们的更改(即补丁)与我们的更改(基本文件与我们的文件)合并。

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

在我们和他们触及同一行或触碰相邻行的地方(例如,如果我们更改了第55行而他们更改了第56行),Git声明了合并冲突。它将写入工作树文件:

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

,然后继续未更改的行。如果我们将merge.conflictStyle设置为diff3,则Git会在这两个版本之间包含行的 base 版本,并用七个|字符标记。

此工作树文件是您的新工作树文件

您现在要继续运行 second git apply,需要进行三向合并。您的第二个git apply按原样处理工作树文件,并且假定这就是我们认为的文件外观。 (当然不是!但是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的位置是当差异再次“失去同步”并击中文件中的=======时所在的位置,除非,这很幸运,{{ 1}}实际上是同步的一部分。

该怎么办

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

(可选)考虑使用git apply,以便您可以更改冲突标记。