git 合并单个文件....和一个真正的三向合并,而不仅仅是我们的与他们的不考虑共同祖先的结帐

git merge a single file....and a TRUE 3-WAY MERGE, not just a checkout of ours vs theirs which doesn't consider common ancestor

我希望能够在具有 git 的单个文件上使用不同的合并策略,该策略实际上使用典型合并处理的共同祖先提交的知识来执行合并。不幸的是,这个问题的普遍答案是使用 git checkout,它实际上并没有执行真正的 3 路合并,而是简单地选择 'ours' 或 'theirs'。让我们看一个例子。

关于使用 git checkout --oursgit checkout --theirs 合并单个文件的 SO 和博客上散布了很多错误信息。这只会有效地执行一个简单的补丁,选择一个或另一个。这不是真正的 3 向合并,使用共同祖先作为基础文件!未选择的一侧的非冲突更改将丢失!

我的存储库根目录下有一个配置文件 (config.toml),需要通过选择 'theirs'(或者有时 'ours',具体取决于其他条件)来自动解析。从 'main' 分支开始,第一个条目成为公共提交。

path = "some/path"
description = "this is a description"
owner = "username"
foo = "bar"
log = "some stuff"

然后我签出一个新分支,比如 'newbranch',关闭此提交并添加一行并更改日志行。

path = "some/path"
description = "this is a description"
owner = "username"
new = "line"
foo = "bar"
log = "new branch"

然后我回到我开始的 'main' 分支,只更改日志行。

path = "some/path"
description = "this is a description"
owner = "username"
foo = "bar"
log = "main updates"

我回到'newbranch'分支。如果我执行 git merge -Xtheirs main,一切都会如您所愿。

path = "some/path"
description = "this is a description"
owner = "username"
new = "line"
foo = "bar"
log = "main updates"

唯一真正的冲突是日志行,它通过选择 'theirs'(来自 'main' 分支的那个)自动解决。添加的行 new = "line" 应按原样保留,因为知道分支分歧的共同祖先提交。

但这显然适用于所有文件,我无法以这种方式自动解决所有冲突。我希望它只应用于这个文件。

这个问题的常见答案是使用

git checkout --theirs main -- config.toml

这实际上并没有执行三向合并,而是简单地选择文件的版本。并且添加的 new = "line" 被删除。同样,

git checkout --patch main -- config.toml 也不使用共同祖先提交的知识,并再次将 new = "line" 条目视为应该删除的内容。

我看到 .gitattributes 建议作为提供每个文件合并策略的解决方案,但我没有运气让它真正起作用。而且会显得有点死板,无法在合并的时候选择'theirs'还是'ours'

有没有办法在考虑共同祖先的单个文件上执行真正的三向合并?我有点震惊,没有更明显的答案.谢谢!

更新:我认为一种解决方案是将文件的共同祖先、'main' 版本和 'newbranch' 检出到一个临时目录中。然后在提交之前使用 'diff3' 手动执行 3 向合并,覆盖 'newbranch' 版本。

UPDATE2:似乎我实际上可以使用 'diff3' 自动解析,所以这确实不是一个可行的选择。似乎最简单的做法是首先 git merge -Xtheirs --no-commit main 将我想与 git merge -Xtheirs 合并的单个文件复制到 tmp 目录中。 git merge --abort 退出。 git merge --no-commit main 进行合并。自然会与那些文件发生冲突。将这些单独的文件复制回回购协议。 git add <those_files>。到目前为止,我完全以编程方式执行此操作。如果还有其他文件需要解析,则由用户手动完成。否则git commit -m "merged 'main' into 'newbranch'完成合并。

Git 已经对所有合并的文件执行了真正的 three-way 合并。这是标准的 built-in 并且会自动发生。

但是,如果您询问是否有一种方法可以从命令行执行自定义合并策略 per-file,那么没有。 Git 确实提供了一种使用 git merge-one-file 合并单个文件的方法,它执行 three-way 合并,但它不支持自定义合并策略:它实际上只是执行标准 three-way合并。

然而,一般来说,尝试将配置文件与 --ours--theirs 的等价物交替合并的目标表明您存储配置的方式 anti-pattern文件。通常这是因为人们在不同的分支上有不同版本的单个配置文件(例如,用于开发、暂存和生产)。解决此问题的最简单方法是使用脚本基于模板生成文件(例如,从环境或 command-line 选项中读取正确的选择),或者仅将所有三个文件存储在每个分支中并将正确的复制或符号链接到位。

出于类似的原因,人们过去曾要求提供与您在 Git 列表中作为 .gitattributes 选项所要求的功能类似的功能,并且通常会得到上述建议。

虽然有点迂回,但看起来最简单的就是下面的

  1. git merge -Xtheirs --no-commit main 这让我通过使用 'theirs'

    对那些具有 auto-resolving 冲突行的文件进行了 3 向合并
  2. 将我想要与 'theirs' auto-resolution 进行 3 路合并的文件复制到 /tmp 目录

  3. git merge --abort 退出合并

  4. git merge --no-commit main 在没有 auto-resolution 的情况下开始合并。那些特定的文件自然会有冲突

  5. 将 /tmp 目录中的合并版本复制回 repo

  6. git add <those_files>

  7. 如果其他文件有冲突,需要用户手动解决

  8. git commit -m "merged 'main' into 'newbranch'" 完成合并。或者可以使用git merge --continue

无赖 git 没有提供更直接的工具来 3 向合并单个文件,但是这个可以。