git 合并冲突:哪个提交是共同的祖先?

git merge conflicts: which commit was the common ancestor?

我想知道 git 合并冲突解决期间“共同祖先”提交的身份。

换句话说:当我在 git merge 期间解决冲突时,我想知道从 提取 BASE 版本的 修订的散列。

希望有一个命令可以告诉我这些信息?

为什么我想知道

  • 我(很不幸)正在做一个非常复杂的合并,有很多冲突。
  • 我希望能够可视化这两条变化路径(BASE -> LOCALBASE -> REMOTE),以便为我提供更多有关这两组变化如何发生、谁做出的、何时发生的背景信息什么分支等等...

有帮助(?)的相关信息

  • 回想一下,对于任何特定的冲突文件,

    • 一个 BASE 版本 (git show :1:<path>),来自共同祖先提交(其身份是我问题的答案)
    • 本地(我所在的分支:git show :2:<path>)版本和
    • REMOTE(我要合并的分支:git show :3:<path>)版本
  • 我知道我可以使用 git ls-files -u 获取 BASE 文件本身的 SHA 哈希值,它给出的输出类似于

$ git ls-files -u | grep "<path>"
100644 <SHA of BASE file> 1 <path>
100644 <SHA of LOCAL file> 2 <path>
100644 <SHA of REMOTE file> 3 <path>
  • 我正在使用 git mergetoolgvimdiff3 查看冲突。此工具显示每个冲突文件(带有 "<<<"">>>""|||" 冲突标记,以及其他三个供参考的文件:LOCAL、BASE 和 REMOTE。一切都很好.

  • 我的 BASE 文件有时会有冲突标记 (!),看起来像这样:

<<<<<<<<< Temporary merge branch 1
<snip>
||||||||| merged common ancestors
=========
<snip>
>>>>>>>>> Temporary merge branch 2

When there is more than one common ancestor that can be used for 3-way merge, it creates a merged tree of the common ancestors and uses that as the reference tree for the 3-way merge.

  • 我想我所看到的是“共同祖先”是多个提交的合并混合体。尽管如此,合并后的混合体必须以某种方式生成,必须具有 SHA,并且必须具有 parents 我想知道的身份。

如果您处于由 git merge 触发的冲突中(例如:不是 cherry-pickrebase):

  • 您当前的提交仍然是 HEAD
  • 合并的提交存储在一个特殊的引用中.git/MERGE_HEAD

所以你可以得到:

  • 您当前的提交(“我的”):git rev-parse HEAD
  • 另一个提交(“他们的”):git rev-parse MERGE_HEAD
  • 基本提交:git merge-base MERGE_HEAD HEAD

您实际上可以使用 HEADMERGE_HEAD 作为指向这些提交的有效名称,因此:

  • git diff HEAD...MERGE_HEADgit difftool -d HEAD...MERGE_HEAD 将显示自分叉点以来“他们”引入的更改,
  • git diff MERGE_HEAD...HEADgit difftool -d MERGE_HEAD...HEAD 将显示自分叉点以来“我”中引入的更改。

关于三点符号 git diff A...B 的注释,引用 the docs :

git diff A...B is equivalent to git diff $(git merge-base A B) B


解决单个文件的冲突:

  • kdiff3 3 路合并默认显示 4 个窗格,这允许您在同一屏幕上查看:
    • LOCALBASE
    • 的区别
    • BASEREMOTE
    • 的区别
    • 您要在底部窗格中保存的结果

我不熟悉 vimdiff,但肯定有一种方法可以拥有这个 4 窗格视图,或者可以在 3 路合并和两个差异中的任何一个之间切换的命令。

  • 我使用 meld 作为图形差异查看器,我知道这个有:
    • a --auto-merge 标志,在 3 路合并视图中,自动合并不冲突的 diff 块,
    • 一种从命令行打开多个差异视图的方法,所以我在单独的选项卡中打开 LOCAL BASEBASE REMOTE 差异

这是我添加到我的 gitconfig 的配置设置:

git config --global mergetool.meld3.cmd \
    'meld --auto-merge $LOCAL $BASE $REMOTE -o $MERGED'\
    ' --diff $LOCAL $BASE --diff $BASE $REMOTE'

git config --global merge.tool meld3

我很确定其他差异查看器(例如 vimdiff3kdiff3 具有等效的 --auto-merge 选项,但我会让您扫描文档以了解它的命名方式以及如何使用它。

这个很棘手,而且有点讨厌。你在这里完全正确:

I guess what I am seeing is that the "common ancestor" is a merged hybrid of several commits. Nevertheless, that merged hybrid must have been generated somehow, must have a SHA, and must have parents whose identities I want to know.

一样,当 Git 因合并冲突而停止时,您确实可以使用 HEADMERGE_HEAD

您可以通过以下方式找到合并基(复数)的哈希 ID:

git merge-base --all HEAD MERGE_HEAD

由于您使用的是合并递归,Git 所做的是:

  • Select两个合并基地
  • 运行 git merge-recursive 在他们身上。 (这本身可能会找到两个以上的合并基础;如果是这样,请参阅此过程。)
  • 提交结果。这是目前为止的合并基础。 (此提交有一个哈希 ID。)
  • 如果有两个以上的碱基,选择下一个合并碱基,并将其与merge-base-so-far合并;这是目前新的合并基础。
  • 重复直到用完所有合并基数。

此过程的最终输出是提交哈希 ID。 此哈希 ID 不会保存或显示在任何地方。当然,您可以从 git merge-base --all 获取此过程的所有 输入

通常,当合并发生冲突时,Git 会停止并让您修复它们。但是当合并合并基础产生冲突时,Git 只是继续并提交冲突的合并基础。这个不好。 (我并不是说它 不好 在这里,只是说它 不好: 它变得非常混乱。新的 merge-ort 不这样做,我想,但我还没有准确地消化它的作用。)这些冲突标记确实是你所看到的。

这里的工具 Git 并不能胜任这项工作,但是使用 git merge-base --all,您至少可以检查每个输入。