为啥 git mergetool 显示没有冲突,即使合并后文件中存在冲突标记?

Posted

技术标签:

【中文标题】为啥 git mergetool 显示没有冲突,即使合并后文件中存在冲突标记?【英文标题】:Why does git mergetool show no conflicts, even though there are conflict markers present in files after a merge?为什么 git mergetool 显示没有冲突,即使合并后文件中存在冲突标记? 【发布时间】:2020-07-21 09:08:26 【问题描述】:

我已在 feature/my-branch 上签出并运行 git merge dev。添加到文件中的冲突标记是:

<<<<<<< HEAD
    let foo = "foo"
    let bar = "bar"
||||||| merged common ancestors
    let baz = "baz"
    let bar = "bar"
=======
    let baz = "baz"
    let qux = "qux"
>>>>>>> dev

然后我运行git mergetool。我将p4mergetool 设置为我的合并工具,它似乎正在工作。我的.gitconfig

[merge]
    tool = p4mergetool
    conflictstyle = diff3
[mergetool "p4mergetool"]
    cmd = /Applications/p4merge.app/Contents/Resources/launchp4merge $PWD/$BASE $PWD/$REMOTE $PWD/$LOCAL $PWD/$MERGED
    trustExitCode = true

git mergetool自动解决上述冲突(工具中显示0个冲突)为:

let foo = "foo"
let qux = "qux"

这种说法是有道理的:即使 HEAD 和 dev 发生冲突,我们也可以看到一个分支更新了一行,而另一个分支更新了另一行。所以我们可以大概假设我们想要什么。

我的问题是:

    有没有办法专门运行/配置 git-mergetoolp4mergetool 以不做出这种假设并仍然显示冲突?

    我需要同时运行这两个命令吗:

    git merge dev
    git mergetool
    

    让这个冲突自动解决? IE。产生输出:

    let foo = "foo"
    let qux = "qux"
    

换一种说法:有没有一个 git 合并策略/参数可以用来简单地运行merge 命令来产生:

let foo = "foo"
let qux = "qux"

【问题讨论】:

【参考方案1】:

这种说法是有道理的:即使 HEAD 和 dev 发生冲突,我们也可以看到一个分支更新了一行,而另一个分支更新了另一行。所以我们可以大概假设我们想要什么。

完全正确。

    有没有办法专门运行/配置 git-mergetoolp4mergetool 以不做出这种假设并仍然显示冲突?

git mergetool 没有做这个假设,所以我们可以得出结论,p4mergetool 一定是做这件事的人。不过我没有p4mergetool,所以我不知道它是否有一个配置旋钮来改变它。

    我是否需要同时运行这两个命令...

是的:Git 自己的合并有点愚蠢,只是注意到,天哪,左边的这个变化范围邻接(接触)或重叠右边的另一个变化范围,所以让我们称之为冲突并让用户处理它。在这种情况下,左边的变化(从 baz 到 foo)就在右边的变化(bar 到 qux)之前,所以两个范围在边缘接触并且 Git 本身声明了冲突。如果中间有一条线,git merge 会自行合并这些更改,而不会称其为冲突。

(请注意,使用-X ours-X theirs 将通过丢弃一方的更改并仅使用另一方的更改来解决冲突。同样,您甚至永远不会达到让p4mergetool 做自己的事情的地步。)

git mergetool 所做的是提取三个文件——合并基础、左或本地或--ours,以及某个文件的右或远程或--theirs 版本——并在这三个文件上运行其他一些非 Git 程序文件。当该程序完成时,git mergetool 可以信任其退出代码来决定工具本身是否正确合并文件,或者运行一些文件比较,或者直接询问您:文件的工作树副本现在是否正确合并复制?

如果工作树副本现在是正确的最终结果(或至少 git mergetool 相信这一点),git mergetool 运行 git add,这标志着冲突已解决。1否则,它会留下冲突。

(我倾向于只在编辑器中解决合并冲突。)


1如果一个文件在任何非零索引槽中有任何副本,则该文件未合并。如果一个文件有一个副本,则该文件被合并,位于插槽 0(正常的插槽号)中。除了使用 Git 的一些低级诊断命令(git ls-index --stage,真的),您实际上无法看到这些暂存槽编号,但 git status 调用文件 unmerged 并告诉您它是在所有三个插槽中(UU)还是仅在左侧或右侧(分别为UDDU)。我不确定git status 对插槽 1 中的文件所说的内容,它表示具有重命名/重命名冲突的文件的合并基础副本。

通常我们只使用git addgit rm 来覆盖编号较高的插槽。在想要将已解决的文件恢复到冲突状态的极少数情况下,git checkout -m 可以做到这一点。

【讨论】:

谢谢@torek!即将接受答案。只是好奇:“不过,我没有 p4mergetool,所以我不知道它是否有一个配置旋钮来改变它。”您是否知道 任何 具有此配置的合并工具? 不——我不使用它们中的任何一个。我已经将 vimdiff 作为合并工具启动了一两次,只是为了看看它的外观,而且我曾经见过一个非 Git 合并工具在运行。我已经运行git mergetool 并指出了一些事情,但从未尝试过 kdiff3 或任何 KDE 合并工具......

以上是关于为啥 git mergetool 显示没有冲突,即使合并后文件中存在冲突标记?的主要内容,如果未能解决你的问题,请参考以下文章

如何配置git mergetool配置

markdown 如何使用`git mergetool`来解决冲突

markdown 如何使用`git mergetool`来解决冲突

git merge为啥会覆盖

如何解决与“git mergetool”的多个冲突而不必关闭文件之间的编辑器?

02_创建Git仓库,克隆仓库,git add,git commit,git push,git pull,同行冲突,不同行冲突的结局方案,git mergetool的使用