在双向合并 git 个分支时跳过文件

Skip files while merging git branches in both directions

我的 git 存储库中有两个分支。有一个配置文件在两个分支中应该是不同的。我将 config merge=ours 添加到 .git 属性以在合并时保留配置文件。 从 B 分支合并到 A 分支时,配置文件不会合并。见下图

*---*---*---*--*(A)(HEAD) //config file in branch A remains
    \         /
     *---*---*(B)

但是,当我从 A 分支合并到 B 时,我的配置文件也被合并

*---*---*---*--*(A)
    \         /  \
     *---*---*----*(B)(HEAD) //config file in B gets merge with A

有人能告诉我是什么原因吗?从两个方向合并时如何保留文件? 尽管我发现其他讨论仅在一个方向合并时保留文件,但我找不到关于这种特定情况的讨论。

合并驱动程序.gitattributes 文件中定义只有当所有三个输入文件都不同时才使用

这意味着设置merge=ours(并定义ours合并驱动程序)经常会失败。我建议不要为此烦恼:只需定义您自己的流程来处理这些问题。

更多详情

首先要记住 git merge 并不总是进行真正的合并:

  • -s / --squash 指示它根本不进行真正的合并,但它仍将使用 merge-as-a-verb 操作(并禁止 fast-forwarding).

  • --no-ff 将禁止 fast-forward not-a-merge 操作,强制对这种情况进行真正的合并。

  • --ff-only 如果 fast-forward not-a-merge 不可能,将导致命令失败(并且不合并)。

在查看本文的其余部分时请记住以下几点:

  • 我们这里只考虑解析和递归策略;章鱼合并和-s ours完全不同。

  • 其中一个输入提交总是 HEAD。另一个是您在命令行上命名的提交。 git merge 命令将自行定位合并基础提交;这是第三次输入提交。如果有多个合并基础提交,-s resolve 将随机选择一个并且 -s recursive 将首先合并合并基础——这是内部递归合并——然后进行新的提交以用作外部合并的合并基础。

  • 如果合并基础提交是 HEAD 提交,则 fast-forward 是可能的。如果允许,Git 将不会进行合并,而只会检查命令行中指定的其他提交,调整当前分支以指向该提交。

  • 如果合并基础提交是另一个提交,则不需要合并:当前分支是最新的并且没有任何反应。

剩下真正的合并案例:涉及三个不同的提交。这些提交中的每一个都有许多文件的完整快照,并且 Git 将:

  • 将合并库中的每个文件与 HEAD 中的每个文件进行比较,看看我们更改了什么;
  • 将合并库中的每个文件与另一个提交中的每个文件进行比较,以查看它们更改了什么;和
  • 合并这些更改。

为了合并这些更改,Git 将使用 low-level 合并驱动程序。您可以使用 merge=<em>name</em> .gitattributes 设置来定义这种驱动程序。 (注意:recursive/resolve 合并代码将执行自己的 high-level 合并以处理 newly-created 文件、删除的文件和重命名的文件,然后再到达 low-level 合并处理程序。此 high-level 合并可能会产生冲突,这将导致合并停止,无论 low-level 驱动程序可能做什么。)

这就是问题所在:high-level 合并代码处理任何 high-level 冲突, 不关心 运行 low-level 完全合并代码 如果可以跳过它。只要 to-be-merged 文件的原始哈希 ID 在多个提交中匹配,此 high-level 代码就会跳过 low-level 代码。

也就是说,假设 我们 更改了文件 F 他们没有 。然后合并基础中 F 的散列匹配他们提交中 F 的散列,但是 我们的 提交中 F 的散列不同。 Git 假定正确的操作是获取我们的文件:它甚至从未 运行s low-level 合并驱动程序。使用默认的 low-level 驱动程序,这很好。使用 ours 合并驱动程序,这仍然很好:Git 占用了我们的 F.

但是假设我们没有改变F而他们改变了。然后合并基础中 F 的散列匹配我们提交中 F 的散列,但不匹配他们提交中的散列。 Git 假定正确的操作是拿走他们的文件。它永远不会 运行 成为 low-level 驱动程序,即使它是 ours 合并驱动程序。

如果 Git 有一个 .gitattributes 设置强制它为每个文件 运行 low-level 驱动程序,这将解决问题。但事实并非如此。