Rebase 一个合并的分支

Rebase a merged branch

我在 git 回购中有如下分支。

                         
                 _d_e_f__(branch A)
(Master)_a_b_c__/        
                \_g_h_i__
                         (branch B)

之后,从 A 创建一个新的分支 C,并将 B 合并到 C git checkout A && git checkout -b C && git merge B

                         
                 _d_e_f__(branch A)
(Master)_a_b_c__/        \__________(branch C)
                \_g_h_i__/
                         (branch B)

现在,假设主分支添加了一个新提交,我想将所有分支(A、B、C)变基到主分支中的新提交。这样做的正确方法是什么?我尝试了以下方法,一切正常,除了当我检查图表时,但是,从分支 B 到 C 的合并在变基后似乎没有集成。

git checkout A
git rebase master
git checkout B
git rebase master
git checkout C
git rebase --onto A f

从图表中我可以看出,一个分离的提交(m)是为将B合并到C而创建的。

                 _m__C
            A___/
master_____/
           \B

TL;DR:Consider using Graphite。我自己没有使用过 Graphite,但请阅读下面的文字,看看它是否符合您的目标,然后查看链接的项目,可能 是一种开箱即用的方法这个。

您可以使用 (1) 新奇的 --rebase-merges 选项和 (2) 随后的分支名称更新来实现您的目标。 这取决于您的最终目标,在我撰写此答案时,根据您的问题,我并不完全清楚这个目标。

执行此操作的命令序列为:

git checkout branch-C
git rebase --rebase-merges master
git log --graph            # to find hash IDs below
git branch -f master <hash1>
git branch -f branch-A <hash2>
git branch -f branch-B <hash3>

只有 运行ning git log git rebase --rebase-merges 完成后才能发现这两个哈希 ID。

这里有几点需要注意:

  • Rebase 通过 复制 提交来工作,就像 git cherry-pick 一样。 (--rebase-merges 确实在内部使用 git cherry-pick 的变基类型;其他一些人有时会使用替代命令,这些命令可能 运行 更快,但在某些情况下效果不佳。)

    每个复制的提交都与每个原始提交“做同样的事情”,并重新使用其原始提交的日志消息,但有一个新的、不同的哈希 ID。

  • cherry-pick 命令不能 复制合并提交,因此--rebase-merges 选项甚至不会尝试这样做。相反,将作为输入的提交复制到原始合并后,rebase-merges 代码 运行 是一个全新的 git merge 命令来创建新的合并。

    因为这是一个全新的合并,所以在 原始 合并期间采取的任何特殊操作都将丢失。当且仅当您启用了 git rerere 时,才会保留以前的冲突解决方案(Git 的即将发布的版本就是这种情况,但大多数现有版本和旧版本都不是这种情况)。您使用的任何特殊标志,例如 -X ours-X theirs,都将丢失。

这意味着我们开始说:

        D-----E-----F   <-- branch-A
       /             \
A--B--C   <-- master  M--N--O   <-- branch-C (HEAD)
       \             /
        G-----H-----I   <-- branch-B

其中 branch-A 指向提交 Fbranch-C 指向提交 Obranch-B 指向提交 Imaster 指向提交 C.

我们check out branch-C在开始rebase之前,使用--rebase-merges,这样Git会产生一组指令列表要复制/合并的提交。这套说明是这样写的:

  1. 移至 C 作为独立 HEAD。
  2. 复制 DEF
  3. 保存新副本的哈希 ID F'
  4. Return 到起点 C.
  5. 复制 GHI
  6. 保存新副本的哈希 ID I'
  7. 检查 F' 并执行 I' 的合并(使 M')。
  8. 复制 NOP

有一个隐含的最终指令将名称 branch-C 移动到指向此处所做的最后一次提交。1 如果您将 --interactive 添加到此变基,您将看到说明 sheet 的文本版本,Git 将允许您对其进行编辑。如果不是,Git 就开始工作,使用指令 sheet,即使没有 --interactive 它也会生成和解释该指令,因为 rebase 可能 停止在中间。如果是这样,您可能稍后会恢复它,为此它需要保存这些说明。 (rebase 还保留了一个 运行ning“已经完成的事情和接下来要做的事情”,以配合待办事项说明;这里的确切机制取决于 rebase 代码,并且随着时间的推移而改变。)

如果一切顺利,所有指令——包括最终和隐含的“移动分支名称”步骤——执行后的最终结果是:

        D-----E-----F   <-- branch-A
       /             \
      C   <-- master  M--N--O   ???
     / \             /
    |   G-----H-----I   <-- branch-B
    /
A--B
    \
    |   D'----E'----F'
     \ /             \
      C'              M'-N'-O'  <-- branch-C (HEAD)
       \             /
        G'----H'----I'

也就是说,这个git rebase --rebase-merges已经完成所有需要的复制,因为masterbranch-Abranch-B只需要在复制完成后指向提交C'F'I'即可。

不幸的是,Git 本身并没有内置任何东西来处理所有这些。 Graphite 可能。


1我在写这篇文章的时候突然想到,最好把它作为一个明确的说明。这仍然可以在将来完成 Git.