如何在git中找到合并提交之前两个分支的共同祖先?

3

背景

我正在尝试编写一个脚本,使用git简化我的回溯过程。

修复当前版本中的错误的一般流程如下:

  1. master分支创建一个bug修复分支,例如bugfix/abc
  2. bugfix/abc上进行所有修复错误的提交
  3. bugfix/abc合并到master(不要快进合并)

现在,当我想将修复应用于例如版本1(分支v1)时,我会执行以下操作:

  1. bugfix/abc创建一个新分支,例如bugfix/def
  2. 然后手动找到合并提交之前的master提交,例如f4d399238f
  3. 现在我使用rebase:$ git rebase --onto v1 f4d399238f bugfix/def

这很好用(在弄清楚我必须使用合并前的提交作为上游之后)。

问题

如何在合并提交之前找到两个分支的共同祖先?(回溯过程的第2步)。

尝试过的

  1. git merge-base bugfix/abc master
    • 由于合并已经完成,这只返回bugfix/abc头部的提交
  2. 结合#1的结果,使用git log获取该提交的子级

更新

这个问题与Find common ancestor of two branches的关键区别在于这两个分支已经被合并。正如我所提到的,作为合并的分支的头部返回了最佳提交。我需要合并提交之前的共同祖先。

假设修复错误并合并到主分支后的分支如下:

A---B v1
     \
      C---D---F---H master
           \     /
            E---G bugfix/abc

运行$ git merge-base master bugfix/abc将返回G,但我需要获得D(甚至F也可以用于使用rebase --onto的目的)。

一旦我获得D,我会执行以下操作:

$ git branch bugfix/def bugfix/abc
$ git rebase --onto v1 D bugfix/def
$ git checkout v1
$ git merge bugfix/def

为了获得期望的结果:
      E'---G' bugfix/def
     /      \
A---B--------I v1
     \
      C---D---F---H master
           \     /
            E---G bugfix/abc

可能是查找两个分支的共同祖先的重复问题。 - Martin G
这里的关键区别在于我需要合并提交之前的提交。那个问题明确要求最近的提交,但我实际上需要第二个最近的提交。 - steinybot
2个回答

4
我如何在合并提交之前找到两个分支的共同祖先?(用于回溯过程的第2步)。
有几种可用的选项:

git merge-base

git merge-base是您要查找的命令。

git merge-base找到两个提交之间最佳的公共祖先,用于三方合并。如果后者是前者的祖先,则一个公共祖先比另一个公共祖先更好。没有更好的公共祖先的公共祖先是最佳公共祖先,即合并基础。请注意,一对提交可能有多个合并基础。

手动从日志中找到它

git log --decorate --graph --oneline --all 这将显示日志中的分叉点,以便您可以跟踪分支的提交ID。

谢谢,但我已经解释了为什么 git merge-base 不起作用。如果您知道如何使其正常工作,请详细说明一下?我已经更新了内容以使其更清晰。此外,手动步骤不是我想要的。目的是编写一个脚本来完成这个任务,我已经可以手动完成,那是容易的部分。 - steinybot

0

对于我展示的这种情况,我找到了一个合理的解决方案:

git rev-list --first-parent master | gawk -v bugfix=bugfix/abc '{
    commit = $1
    branchCmd = "git branch --contains \"" commit "\" \"" bugfix "\""
    if (branchCmd | getline) {
        print commit
        exit
    }
}'

这将打印D

事实证明,如果像这样将master合并到bugfix/abc中,则此操作将失败:

      E'---G'---h' bugfix/def
     /           \
A---B-------------J v1
     \
      C---D---F-------I master
           \   \     /
            E---G---H bugfix/abc

在这种情况下,它将打印F,但这仍然不是我们想要的。
如果只有branch --contains有一些选项可以限制父级,例如像rev-list中的--first-parent那样...
解决方法是修改脚本如下:
git rev-list --first-parent master | gawk -v bugfix=bugfix/abc '{
    commit = $1
    branchCmd = "git branch --contains \"" commit "\" \"" bugfix "\""
    if (branchCmd | getline) {
        timestampCmd = "git show -s --format=%ct \"" commit "\""
        if (timestampCmd | getline timestamp) {
            revlistCmd = "git rev-list --first-parent --max-age=" timestamp " \"" bugfix "\""
            while (revlistCmd | getline line) {
                if (commit == line) {
                    print commit
                    exit
                }
            }
        }
    }
}'

这里的想法是,我们取每个可能的结果,并检查是否可以从bugfix/abc到达,但只遵循第一个父级。 --max-age表示我们只回溯到必要的程度。如果无法到达,则必须是从master合并到bugfix/abc。如果可以到达,则打印提交哈希并退出。

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