git 将整个 cpp 源文件内容标记为冲突,而大部分内容是相同的

git marking the entire cpp source file content as conflicted while most of the content is identical

this 问题后,我的 windows 机器上现在有两个本地 git 存储库,我想合并它们。当我使用 WinMerge 工具比较存储库中的文件时,可以正确显示差异。但是当 git 尝试合并整个文件时,它被标记为冲突。

重要的一点是还有其他文件是相同的并且被正确处理了。还有一点,问题与core.autocrlf无关,原因有二: 1- 我测试了 true 和 false 设置 2- WinMerge 显示与关闭忽略行尾选项没有区别

你建议我们如何调查此事?

[edit] 我升级了 git 但没用。

当Git 执行正常合并时,它只考虑三点:要合并的两个头和合并基,通常是最近的共同祖先。如果你要合并不相关的历史,这个共同的祖先就是空树;也就是没有文件的状态。

合并采用合并基础和每个头之间的差异应用结果的总和。

如果在合并时文件两边相同(即 blob 具有相同的对象 ID),则合并的结果只是将该 blob 作为该文件在合并期间的值合并。双方都有相同的变化,因此没有必要进行任何花哨的分析。这就解释了为什么在这种情况下相同的文件对您来说效果很好。

但是,如果文件不相同并且您有不相关的历史记录,Git 认为这是一个 add/add 冲突:也就是说,一方添加了一些行和另一边添加了另一组线。可能是这些行中有很多是共通的,但是逻辑上的冲突是添加了两组不同的行,所以整个文件冲突。

这就是为什么合并同一代码库的独立历史往往不是一个好主意。您可以手动解决冲突,也可以对存储库的两个相同状态执行合并,这将导致无冲突合并,然后在第二次合并中再次合并您的更改。在后一种情况下,您的合并基础将不是空树,而是相同的合并状态,因此,Git 将能够在没有冲突的情况下更好地解决更改。

WinMerge 实现了人们有时称之为 "two-way merge" 的东西。这是用词不当,因为双向 "merge" 根本不是合并:它只是一个差异,然后是该差异的引导应用程序。参见 Why is a 3-way merge advantageous over a 2-way merge?

WinMerge 还实现了 3 向合并。 Git 在其合并算法(git mergegit merge-file)中实现了 3 向合并。即必须是公共基础文件

因此,您将不得不使用 Git 以外的其他东西来实现文件级合并,或者想出一个通用的基础文件:

  • 您不需要共同祖先 commit(如 ),但拥有一个会使事情变得容易得多,因为 Git 本身然后将完成所有工作。公共基础文件来自公共合并基础提交。
  • 确实需要一个通用的基础文件,但是,如果你想使用Git的工具。

也就是说,如果你愿意,你可以:

  • 使用 git merge --allow-unrelated-histories 开始合并。
  • 让 Git 停止有关 CONFLICT (add/add) 的消息。
  • 对于处于此冲突状态的每个合并结果:
    • 挑选出你的两三个文件。找到的两个Git很容易搞定。寻找或创建一个共同的合并基础是你自己的问题; Git这里没有答案。
    • 使用您选择的任何工具生成合并结果。这可以是 git merge-file、WinMerge 或您喜欢的任何内容。
    • 告诉Git:这个合并结果是合并的正确结果。 Git 将接受您提供的任何内容作为正确的合并结果。
  • 既然您已经解决了所有冲突,请完成合并。

它相对容易,例如使用 bash 脚本来自动化 "for each conflicted file" 部分。 git status 告诉您哪些文件处于 UU(未合并)状态,这样就为您提供了一组文件 names。然后,您可以在每个文件名上使用 git ls-files --stage <name> 来验证该名称确实存在 slot-2 和 slot-3 索引条目,并且没有 slot-1 条目,即这是一个 "add/add" 冲突。然后,您可以使用 git checkout-index 的一种对用户不友好的变体,将两个冲突的输入文件放入临时文件中。

事实上,git mergetool 为您完成以上所有操作,然后 运行 执行您选择的任意命令。如果可以 运行 WinMerge 直接来自 git mergetool 的那些文件,这可能是解决这个问题的一种方法。

换句话说,git mergetool 完成了 "for each conflicted merge, get the three files from Git" 部分工作。由于 Git 没有找到通用的合并基础文件,git mergetool 提供了一个 文件作为您选择的工具的合并基础。您可以在知道(或检查)该文件为空的情况下,简单地丢弃该文件并执行您的“双向合并”——实际上,引导差异应用程序——以产生正确的结果。

(我不知道如何 运行 WinMerge 本身,无论是作为从 git mergetool 调用的合并工具还是任何其他方式。所以那部分,你需要找到或弄清楚靠自己。)