防止 Git 快进自动合并

Posted

技术标签:

【中文标题】防止 Git 快进自动合并【英文标题】:Prevent Git Fast Forward Automatic Merge 【发布时间】:2021-12-05 15:03:31 【问题描述】:

问题上下文示例:

    一个分支 B 是从 A 分支创建的 在分支 B 中创建了一个名为 XPTO.txt 的文件并对其进行了多次编辑(多次提交) 之后又从分支B创建了另一个分支CBC 分支中,XPTO.txt 被同时编辑了多次(多次提交) 分支 C 必须合并回 A 从分支 C 到分支 A 的拉取请求 (PR) 不得有之前在分支 BXPTO.txt 中所做的更改C 分支已创建

假设删除 B 分支代码的这些更改(提交)已正确完成并且不会影响项目构建等。

我应该如何正确管理存储库以便:

合并分支 B 代码删除提交到分支 A 在分支 B 打开 PR 以合并到分支 A 时(尽可能)避免合并冲突 最重要的一点:隐式避免,如果可能,Git 快进可能发生的情况

将示例扩展到多个文件和多个更改(提交)。

到目前为止,我认为唯一的答案是强制从分支 B 到分支 A 的非 FF 合并,但有时仍会得到 Already up to date!

谢谢。

【问题讨论】:

无论您描述的具体工作流程如何,即使可以进行快进,也可以使用 --no-ff 强制合并提交。 感谢@RomainValeri 我编辑了这些问题,为您提供更多背景信息:) 【参考方案1】:

鉴于这些步骤:

    一个分支 B 是从 A 分支创建的 在分支 B 中创建了一个名为 XPTO.txt 的文件并对其进行了多次编辑(多次提交) 之后又从分支B创建了另一个分支CBC 分支中,XPTO.txt 被同时编辑了多次(多次提交)

您的存储库如下所示:

                               (B)
                                |
                                v
                      <--b3 <--b4
                    /
a1 <--a2 <--b1 <--b2
       ^            \
       |             <--c1 <--c2
      (A)                      ^
                               |
                              (C)

小写标签是单独的提交,通过它们的“父”引用(向后的箭头)链接在一起。大写标签是分支,在 git 模型中只是指向特定提交的指针,可用于引用该提交及其所有祖先。

请注意,提交 b1 和 b2 是最初在分支 B 上创建的,但就 git 而言,它们只是分支 C 历史的一部分。

那么现在:

从分支 C 到分支 A 的拉取请求 (PR) 不得在创建分支 C 之前对分支 B 的 XPTO.txt 进行更改

没有直接的方法告诉 git - 它不知道哪些提交“属于”分支 B 或“之前”分支 C。如果您要求将分支 C 合并到 A,它会回顾直到它找到一个共同的祖先,即 a2,因此要合并的提交是 b1、b2、c1 和 c2。

为了“删除”这些提交,您需要创建在历史记录中没有它们的新提交。这就是“git rebase”命令的用途。

在这种情况下,您需要将“b2”之后的提交重新设置为“A”,因此命令将是git rebase b2 C --onto A。结果将如下所示:

                               (B)
                                |
                                v
                      <--b3 <--b4
                    /
a1 <--a2 <--b1 <--b2
      ^ \           \
      |  \           <--c1 <--c2
     (A)  \
           <--c3 <--c4
                     ^
                     |
                    (C)

现在提交 b1 和 b2 不再是 C 历史的一部分。

提交 c3 和 c4 将分别由基于 c1 和 c2 的 rebase 命令创建,但不要以任何方式链接到它们。如果没有其他分支或标签指向提交 c1 和 c2,它们最终将作为孤立数据被“垃圾收集”。


如果您想要部分从提交 b1 和 b2 中获得更改,则需要手动将它们添加回来作为新提交。这可能会或可能不会导致以后发生冲突,这取决于合并算法是否可以弄清楚您要做什么。但这只是生活中的事实:对同一个文件的两个并行更改存在冲突的风险。


请注意,这与恢复更改(使用“git revert”或手动撤消它们)非常不同,后者会在历史记录中创建附加提交:

                               (B)
                                |
                                v
                      <--b3 <--b4
                    /
a1 <--a2 <--b1 <--b2
       ^            \
       |             <--c1 <--c2 <--rb1 <--rb2
      (A)                                   ^
                                            |
                                           (C)

这里,“rb1”撤消了“b1”的更改,“rb2”撤消了“b2”的更改,但是所有四个提交都是 C 历史的一部分。一旦你合并到 A,它们也都将成为 A 历史的一部分,所以当你合并到分支 B 时,只有 b3 和 b4 将是“新的”。

解决此问题的唯一其他方法是重新设置分支 B 以创建提交的新副本以在还原后合并 。这会导致混乱的历史,但有时是摆脱混乱的方法。

                               (B)
                                |
                                V
          <--b5 <--b6 <--b7 <--b8
        /
       |
       |             <--b3 <--b4
       |            /
a1 <--a2 <--b1 <--b2
       ^            \
       |             <--c1 <--c2 <--rb1 <--rb2
      (A)                                   ^
                                            |
                                           (C)

这里的 b5、b6、b7 和 b8 是 rebase 命令重新创建的 b1、b2、b3 和 b4 的版本。

【讨论】:

到目前为止,我只阅读了第二张图表。但我建议将git rebase b2 C --onto A 更改为git rebase --onto A b2 C,因为我认为这是 far 更常见的语法用法? (我不知道我是否曾经在结尾看到过 --onto,很多人用“第一”、“第二”参数等来指代 --onto。)另外,也许在第二个图c3和c4是c1和c2的replay? 顺便说一句,关注 cmets 的人有一篇关于它的精彩文章:tanzu.vmware.com/content/blog/git-rebase-onto @TTT 你说得对! @TTT 我倾向于这样写,因为这就是您用英语所说的顺序:“将提交从 b2 重新定位到 C 到 A”。最后,它没有任何区别,因为实际上只有 A 附加到“--onto”。

以上是关于防止 Git 快进自动合并的主要内容,如果未能解决你的问题,请参考以下文章

Git:如何防止特定的提交被合并到另一个分支中?

Git hook prepare-commit-msg 以防止合并禁止的分支 - 将 Ruby 转换为 Bash

sh Git快进合并和拉动

Git Extensions 使用小结

git -- Fast forward(快进模式)和 no-ff 区别

git merge的使用