Git 合并错误地识别块中的冲突

Git Merge Erroneously Identifies Conflicts in Blocks

我的存储库中有一个文件 data.csv,它表示一个 CSV 格式的数据库。为了举例,我们假设 data.csv 的内容是

1,2,3
2,3,4
4,5,6

本来我只有master分支,我创建了A、B两个分支,我自己修改data.csv。我注意到有时,三向差异算法识别出在我看来根本不应该是冲突的冲突。比如A修改文件为

1,4,5
2,3,4
4,5,6

B修改文件为

1,2,3
2,6,7
4,5,6

当我从分支 B 发出 git merge A 时,它没有自动合并这些版本,而是报告了以下冲突:

<<<<<<< HEAD
1,2,3
2,6,7
=======
1,4,5
2,3,4
>>>>>>> A
4,5,6

但在我看来,实际上这些版本应该可以逐行自动与三向差异逻辑合并,因为 A 只修改第一行,而 B 只修改第二行。

我的问题:为什么会这样?有没有办法强制 Git 进行更细粒度的差异(例如逐行)? (或者,是否有任何方法可以强制 Git 意识到这些更改实际上是可自动合并的?)

当你合并两个修改了同一个文件的分支时,总是会发生合并冲突。在示例中,您遇到了合并冲突,因为分支 A 修改了 data.csv,分支 B 也修改了 data.csv。要解决此冲突,您必须决定在 <<<<<<< HEAD 和 >>>>>>>> A 之间要保留哪些行以及要删除哪些行。此外,您还必须删除 <<<<< << HEAD, =======, and >>>>>>> A.
之后 运行 git 添加 data.csv 命令来解决冲突然后 运行 git commit 结束合并。

正如我,你今天可以处理这个问题的方法是编写一个合并驱动程序。编写一个好的合并驱动程序并非易事,但您将能够对其进行试验,并将其仅应用于特定文件。

如果您不自己定义合并驱动程序,Git 将使用它自己的内置驱动程序。这个内置的与 the git merge-file command 基本相同。 (它可能与它完全相同,因为它们是从 Git 中的各种共享源文件构建的。请注意 ll-merge.c 中内置的“低级”合并驱动程序是选择 运行使用配置的合并驱动程序,或使用内置代码,实际上会发生。)

请注意,您的合并驱动程序至少需要三个输入(您最多可以给它五个输入):

  • 驱动程序可以在其中找到文件的 merge base 版本的路径名;
  • 驱动程序可以在其中找到文件的 当前 (--ours) 版本的路径名,并且驱动程序必须将最终合并版本写入其中文件的;和
  • 一个路径名,驱动程序可以在其中找到文件的另一个 (--theirs) 版本。

驱动程序的工作是读取三个输入版本,无论它如何选择,然后将获得的正确合并结果写入这三个路径名的中间一个。路径名将是临时文件的名称:不要假设这三个文件名中的任何一个都有意义或与正在合并的文件的历史名称有任何关系。

您可以传递给您自己的程序的额外数据包括用户所需的冲突标记大小(默认为 7)和合并结果最终将复制到的路径名。也就是说,假设我们正在合并一个文件,其在合并基础中的名称为 orig.wrongsuffix,其在 --ours 提交中的名称为 ours.csv,其名称在 --theirs 提交是 renamed-wrongly.csv。这三个输入文件可能具有 .git-tmp-1234567 或类似形式的文件名。考虑到现有的 recursiveresolve 策略,驱动程序的输出最终将在名为 ours.csv 的文件中结束,尽管因为存在 rename/rename 冲突(我们修复了名称,他们试图修复名称),即使我们的合并驱动程序能够产生合并结果,合并也会因冲突而停止

为了表明合并成功——即合并不必因您自己的合并驱动程序发现的冲突而停止——您的合并驱动程序在终止时应该 return 成功退出状态。换句话说,从 C 代码调用 exit(0);来自 Python,使用 sys.exit(0) 或等价物;在 Go 中,使用 os.Exit(0);等等。为了表明,尽管您的驱动程序尽了最大努力,您的代码仍无法产生正确的合并结果——因此可能会或可能不会在其输出文件中留下合并冲突标记——提供一个非零退出状态(最好是一个小的非零值,例如1;在 125-127 附近有一些特殊值用于 git bisect 之类的东西,这些值在 Git 的其他部分也可能被特殊对待;出于传统 Unix 编程的原因,值不应超过 127 ).

要告诉 Git 使用 您的合并驱动程序,您需要做两件事:

  • 创建一个 .git/config$HOME/.gitconfig 或其他 定义 驱动程序的条目,告诉 Git 如何 运行 它;
  • 创建一个 .gitattributes 条目(如果需要,首先创建文件)告诉 Git 在这个特定的 .csv 文件上使用 你的驱动程序, 例如.

定义这些的说明在the gitattributes documentation

重叠或邻接规则是有原因的。你可以找到不需要它的情况,但是,对于 dvcs 来说,如果你拉出 linux 历史并使用 automerge-abutting-changes 规则重新运行过去十五年的所有合并,你会发现它产生在很多情况下,结果非常糟糕。没有什么规则是完美的,你必须在某处划清界线,重叠或邻接是一种在实践中几乎不会犯应受指责的错误而引起最少不必要大惊小怪的规则。