在没有完全采用任何一方文件的情况下,解决Git合并冲突并支持我们或他们。

4
在Stack Overflow上有很多关于如何解决合并冲突并支持一方或另一方的问题。但我发现最常见的答案是使用“git checkout --ours”或“git checkout --theirs”。
问题在于“git checkout”将从任一分支获取整个文件。文件的某些部分可能已经成功地合并,我们不想删除这些部分。
对于处于冲突状态的文件,有没有一种方法可以优先选择“我们”的或“他们”的方式来移除冲突标记,而不需要将此策略应用于整个合并或从“我们”的或“他们”的获取整个文件?
这绝对可以通过一个简单的脚本来实现,但我想知道是否存在我没有找到的现有解决方案。

1
不需要选择整个文件的'ours'或'theirs'。事实上,这是相当罕见的情况。任何一个半好的区分工具都可以为你完成。https://help.github.com/en/articles/resolving-a-merge-conflict-using-the-command-line - msanford
@msanford,我不明白你的评论与我的问题有什么关联。在冲突的情况下,我想要一种快速自动解决单个文件中冲突的方法,通过采取一个分支的更改来解决冲突。 - Shinigami
你想要对所有剩余的冲突都执行此操作,是吗?解决所有剩余的冲突,就好像在原始合并时指定了“-X ours”或theirs一样? - jthill
“这个绝对可以用一个简单的脚本实现”是指你已经找到了git merge-indexgit-merge-one-file吗? - jthill
@jthill 是的,但我想针对单个文件。我之前没有听说过你提到的git工具。我会去了解一下。 - Shinigami
3个回答

6

如果您想解决文件中所有未解决的冲突并采用我们或他们的代码,可以使用简单的sed命令,具体取决于您想要采用哪一方和您的冲突风格。

对于大多数代码文件而言,冲突标记通常非常明显,因此您不必担心识别它们,在这种情况下,您只需轻松输入命令即可:

sed -si '/^<<</,/^>>>/ {/^<<</,/^===/d;/^>>>/d}' thefile  # keep theirs
sed -si '/^<<</,/^>>>/ {/^<<</d;/^===/,/^>>>/d}' thefile  # keep ours

为了做到这一点,

sed -si $yourpattern $(git ls-files -m | uniq)

为了更加小心处理冲突标记,最好制作你提到的简单脚本,并且diff3冲突报告样式也需要进行一些更改。通常,冲突符号计数为7,为了最大安全性,请将sed范围设置为/^<<<<<<< ours$/,/^>>>>>>> theirs$/,并且使用diff3报告时,“保留ours”删除范围以^||||||| base$开头,而不是^=======$

在Mac上使用sed会被破坏,拒绝进行内联编辑而不创建备份文件。你需要使用例如-si.bak,然后再删除备份。


我们可以这样做吗:grep -lr '<<<<<<<' . | xargs sed -si $yourpattern?因为$(git ls-files -m | uniq)并没有涵盖所有有冲突的文件。 - alper
@alper 是的,你可以这样做,但是 git ls-files -m 确实包含了所有有冲突的文件,它没有找到的标记可能被添加为测试、玩笑或者错误的正确解决方案,而不一定是为了这个合并。 - jthill
我可以同时应用两种方法来确保安全吗? - alper

1

正如马库斯在评论中提到的那样git mergetool可以针对单个文件进行调用。通过正确的merge.tool-t参数和mergetool.<tool>.cmd设置,您可以使其调用一个小脚本,该脚本:

  • 将文件的--ours版本放入工作树副本中,然后
  • 使用该副本以及文件的基础和--theirs版本作为参数来运行git merge-file --oursgit merge-file --theirs

这个“编辑器”的退出代码是可信的(请参见merge.<tool>.trustExitCode),这允许git mergetool为您运行git add。因此,您可能可以缩短脚本。

我从不这样做,实际上我从不使用git mergetool。如果我真的在使用git mergetool,我会想要亲自检查结果,就像Marcus在他的答案中建议的那样


虽然应该有一行命令可以做到这一点,但目前还没有,您可以自己编写脚本。

可以完成此任务的命令是git merge-file。但在运行git merge-file之前,需要 git merge所看到的三个输入文件。

这三个文件确实存在;问题在于如何提取它们。 git mergetool 命令解决了这个问题,但不幸的是,它的解决方式对您来说并不特别有用。 您想为一个文件解决此问题,但git mergetool会将其重复应用于所有未合并的文件(然后在这些文件上运行您选择的合并工具,通常是某种三向编辑器,逐个进行处理)。

git mergetool 的作用是使用带有 --stage 选项的 git checkout-index。某个文件 file.ext 的三个版本都在索引中,作为第一阶段(合并基础版本)、第二阶段(--ours 版本)和第三阶段(--theirs 版本)。使用 git mergetool 时,脚本将它们分别提取到 file.ext.BASEfile.ext.LOCALfile.ext.REMOTE 中。你必须做更多或更少相同的事情,尽管你可能想选择不同的名称。有关代码,请参见 git mergetool 脚本(如果您喜欢,还有其主要 助手脚本,但这里只需要主要的脚本——我已经直接链接到提取文件的行,而实现该功能的函数在主文件的其他位置)。
一旦您拥有了这三个输入文件,只需使用正确的参数在它们上面运行git merge-file即可。请参阅git merge-file文档以查看这些参数是什么。检查结果,如果需要,可以将其放入原有的冲突标记工作树文件的位置,并使用git add消除三个高级别文件并将合并后的文件写入第零阶段,准备提交。

您可以按照自己的方式解决剩余未解决的文件。


1嗯,也就是说,这解决了大多数情况。对于重命名/重命名冲突,git mergetool会感到困惑:这里索引中的信息有点不足。


git mergetool接受文件参数。只需运行“git mergetool -- path/to/file”。 - Marcus
@Marcus:啊哈,原来是这样。 (我从来没有真正“使用”过它!)这大大简化了工作,因为您可以让git mergetool为您运行git merge-file - torek

1
我认为你不应该以这种方式工作。相反,你应该安装一个适合的合并编辑器(例如KDiff3),运行git mergetool(它会遍历冲突文件并运行已安装的合并编辑器),并手动审查更改。
合并工具将打开文件的三个版本,自动接受所有可解决的更改,就像你希望的那样,并突出显示剩余的冲突。如果你真的不想逐个审查它们,选择菜单项合并为每个未解决的冲突选择xxx即可。
如果你真的确定不想使用可视化合并工具,可以尝试以下 提到的其他选项
如果你想在一个合并过程中将此策略应用于每个有冲突的文件,请使用标准合并策略的"ours"或"theirs"子选项:
git merge -Xours ...

不要将其与选择整个合并策略的 -s ours 混淆,该策略只是从我们自己选择所有东西,而不尝试进行合并。

如果你想在每次合并时将这种策略应用于某些特定文件,可以定义一个自定义合并驱动程序(在 .gitconfig 中)并将其应用于那些文件(在 .gitattributes 中)。这听起来比实际更加复杂。定义合并驱动程序仅意味着定义一个命令行,它需要三个输入文件并创建一个输出文件,例如,您可以像 Torek 所解释的那样使用 git merge-file。一旦定义,您可以通过在存储库根目录中创建 .gitattributes 文件并提交它来将此类合并驱动程序分配给任何文件或模式。了解有关此的详细信息,请参阅gitattributes


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接