变基以更改合并提交的父级

Rebasing to change parent of a merge commit

假设我有以下历史记录,其中最上面一行是 master 分支,下面一行是与 master 在某一点合并的功能分支,D 只是恢复 C(这意味着 BD 中的工作目录相同)。

A---B---C---D           master
 \   \
  E---F---G             feature

我想在 F 合并之前将 C 及其还原 D 添加到历史记录中,如下所示:

A---B---C---D           master
 \           \
  E-----------F'--G'    feature

我不想更改 E(实际上是一长串提交)。

git rebase --onto D B(建议 here)导致合并冲突(有或没有 --preserve-merges)。

有没有办法实现我想要的?

大多数方法都会有些痛苦。有一个使用 git filter-branch 的适度无痛版本,除了 filter-branch 本身是痛苦的。 :-)(您将过滤提交 FG 并编写一个 commit-filter 替换您想要的 F' 的新 parents,然后让filter-branch 操作替换 G 的出身。)

我认为不求助于低级命令的最简单方法就是进行新合并,然后将 G 变基到新合并上。新合并可能有冲突,但我们不在乎,我们只想获取旧合并的树,我们可以这样做:

$ git checkout <sha1>  # Use D or E's sha-1 here.
                       # Note: whichever you use will
                       # be the first parent of our new
                       # merge; choose based on that.
$ git merge --no-commit <sha1>  # use the remaining sha-1 here
[ignore resulting stuff]
$ git rm -rf .         # Note: assumes you're in top dir of work tree
$ git checkout <sha1-of-F> -- .
$ git commit           # Create merge commit F'

第一个 checkout 使用一个 SHA-1 使您进入分离的 HEAD,merge --no-commit 开始与另一个 SHA-1 的合并过程,git rm -rf . 丢弃合并的树和任何冲突,并且 git checkout <id> -- . 填充上一个合并的索引和 work-tree。最后的 git commit 使用与合并 F 相同的树创建合并 F',但 parents.

此时(仍然使用分离的 HEAD)您可以变基(或 cherry-pick)提交 G(或许多提交 G),然后强制您的分支指向新图表的顶端。我建议使用 git rebase ... --onto HEAD 但我没有用分离的 HEAD 测试过它,并且至少有一种方法可能会出错(将 HEAD 解析为 ID 为时已晚)。

低级git commit-tree命令实际上可能更简单。 ,虽然你必须用 git update-ref 拼出分支名称。 [编辑:可能不太正确,你想要的两个 parents 是 DE,而不是 DB。同样,将您想要的 first-parent 放在第一位。]

使用更熟悉的命令的优点 (?) 是它们更熟悉。