如何在 Git 中找到向后合并?

How to find a backwards merge in Git?

我公司有发布分支。在合并回发布分支之前,必须在自己的分支中创建更改。典型的工作流程可能如下所示:

    A---F----------M------ V.1
     \ / \        /           
      E   H------L            

在合并回版本 V.1 分支之前,在 E、H 和 L 中添加了更改。

如果我们同时处理 V.2,那么对 V.1 的任何更改也必须 "merged forward" 到 V.2:

          C--D                
         /    \               
        B---G--J---K---N-- V.2
       /   / \    /   /       
      /   /   I--'   /        
     /   /          /         
    A---F----------M------ V.1
     \ / \        /           
      E   H------L            

这可确保应用到 V.1 的任何错误修正都被带入 V.2。

偶尔,开发人员会不小心合并到错误的发布分支。假设从 V.2 分支出来的提交 'I' 已经向后合并到 V.1:

          C--D                
         /    \               
        B---G--J-------N-- V.2
       /   / \        /       
      /   /   I      /        
     /   /     \    /         
    A---F-------K--M------ V.1
     \ / \        /           
      E   H------L            

在这种情况下我们称K为"backwards merge"。它导致 V.2 中的所有新功能都合并到 V.1 中,这是非常糟糕的。

发生这种情况时,我们会尝试找到向后合并 K,以便我们可以恢复更改。不幸的是,每天有数百次提交,很难通过目视检查图表找到 K。

我们如何以编程方式找到向后合并 K?

因为 "backwards merge" 的想法不是 git 所知道的,所以您需要编写一些自定义脚本。 (我能想到的最接近真正帮助这里的是 git bisect - 但它不会完全起作用,因为它认为它正在寻找引入某些行为的个人提交,而你想找到某些行为的地方( s) 被合并到一个特定的分支。)

所以...

您首先需要的是一种测试提交是否包含来自 v2 的更改的方法。您可以查看最早的 v2 提交(示例图中的 B)并在那里创建一个标签

git tag v2-root

然后你可以检查一个提交,看看 v2-root 是否是 "reachable";有几种方法,但最直接的可能是

git merge-base --is-ancestor v2-root <some-commit>

接下来您需要要测试的提交列表。您可以使用 git rev-list。一般情况下可以使用

git rev-list --first-parent v1.0

这假设对于添加到发布分支的每个合并,第一个父项是发布分支的先前提交,这通常是正确的。有一些方法可以在不正确的情况下创建合并。 (最简单的是将 V1.0 合并到 另一个分支,然后快进 V1.0 到合并。)

如果您担心会发生这样的事情,那么您必须省略 --first-parent 选项,然后您的脚本需要一种方法来整理分支拓扑。现在我假设没有必要。

所以现在你只需要一个脚本来沿着提交列表工作,运行 每次提交的测试(git merge-base ...),直到它找到一个测试 return 是假的。如果第一个(最近的)提交 returns false,则没有向后合并;否则,最后提交 return true 是向后合并。