从技术上讲,您无法更改任何现有提交的任何内容。
这意味着要“更改”b
为B
,您实际上必须制作一个新的和改进的B
. 旧的b
将继续存在(多久,完全是另一个问题)。常规git rebase
的、交互的或非交互的,通过将提交复制到新的和改进的提交来工作。正如您所指出的,常规 rebase 还会删除合并和展平。
从 Git 2.18 开始,git rebase
就有了一个名为--rebase-merges
. 这保留了合并——或者更准确地说,通过再次运行,将它们重新创建为新的合并提交git merge
。2.18 之前的git rebase
has -p
,它使用交互式 rebase 机制,否则也会保留合并(通过重复它们)。与更高级的新版本相比,它有些缺陷,但是——我认为(尚未测试!)——应该适用于这种情况。
因此,请使用git rebase -i --rebase-merges
您将使用的方式git rebase -i
。如果您缺少--rebase-merges
,请更新您的 Git 版本,或者使用git rebase -i -p
并非常小心不要扰乱各种操作的顺序,或者手动构建您的新链。
要手动构建它,请git checkout
在 commit 上运行B
。HEAD
如果您愿意,您可以在此处分配一个新的分支名称,或者只是以分离的方式执行整个操作,如下所示git rebase
:
git checkout -b temp-rebuild <hash-of-b>
例如,使用新的临时分支名称。然后使用git commit --amend
(可能先使用其他东西)来制作新的B
:
B <-- temp-rebuild
/
a---b---c---d---i---j---k---o <-- master
\ / /
e---f---g---h---l---m---n <-- develop
现在git cherry-pick
在提交的哈希上运行c
以将其复制到新的提交C
,并重复d
:
B---C---D <-- temp-rebuild
/
a---b---c---d---i---j---k---o <-- master
\ / /
e---f---g---h---l---m---n <-- develop
现在git merge
在 commit 的第二个父级的哈希上h
运行以进行新的 merge i
,如果需要解决任何合并冲突:I
B---C---D------I <-- temp-rebuild
/ /
a---b---c---d--/i---j---k---o <-- master
\ |/ /
e---f---g---h---l---m---n <-- develop
使用更多的cherry-pick和merge命令来完成这个过程:
B---C---D------I---J---K--O <-- temp-rebuild
/ / /
a---b---c---d--/i---j---k-|-o <-- master
\ |/ |/
e---f---g---h---l---m---n <-- develop
现在构建了新的提交,强制名称master
指向最终提交,并停止绘制旧b-c-d-i-j-k-o
链,你就得到了你想要的。这是做什么git rebase --rebase-merges
和git rebase -p
做的:-p
只使用一个脆弱的算法,同时--rebase-merges
使用一种新的交互式指令表格式,它允许您以一种在移动提交时不会中断的方式指定新图形。