通过合并修改历史中的提交而不展平它
Amending commit in history with merges without flattening it
假设我有类似的历史。
Develop定期合并到master中。
a---b---c---d---i---j---k---o master
\ / /
e---f---g---h---l---m---n develop
如何在不完全拉平历史的情况下修改 b?
如果我以交互方式变基并修改 b,我将得到如下所示的历史记录:
(大写表示提交已更改)
a---B---C---E---F---G---H---J---K---L---M---N master
\
e---f---g---h---l---m---n develop
如何保留历史结构,并使其看起来像这样:
a---B---C---D---I---J---K---O master
\ / /
e---f---g---h---l---m---n develop
从技术上讲,您无法更改任何现有提交。
这意味着从 "change" b
到 B
,你实际上必须制作一个新的和改进的 B
。旧的 b
将继续存在(多长时间,完全是另一个问题)。常规 git rebase
,交互式或非交互式,通过将提交复制到新的和改进的提交来工作。如您所述,常规变基还 删除 合并和扁平化。
从 Git 2.18 开始,git rebase
有一个名为 --rebase-merges
的模式。这保留了合并——或者更准确地说,重新创建它们作为新的合并提交,字面意思是再次 运行ning git merge
。在 2.18 之前,git rebase
有 -p
,它使用交互式 rebase 机制并以其他方式保留合并(通过重复它们)。与更漂亮的新版本相比,它有些缺陷,但我认为(尚未测试!)应该适用于这种情况。
因此,请像使用 git rebase -i
一样使用 git rebase -i --rebase-merges
。如果您缺少 --rebase-merges
,请更新您的 Git 版本,或使用 git rebase -i -p
并非常小心不要打乱各种操作的顺序,或手动构建您的新链。
要手动构建它,运行 git checkout
提交 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
在提交 h
、i
的 second parent 的散列上,创建新的合并 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
使用了一种新的交互式指令 sheet 格式,它可以让你指定新图表在您移动提交时不会中断。
假设我有类似的历史。
Develop定期合并到master中。
a---b---c---d---i---j---k---o master
\ / /
e---f---g---h---l---m---n develop
如何在不完全拉平历史的情况下修改 b?
如果我以交互方式变基并修改 b,我将得到如下所示的历史记录:
(大写表示提交已更改)
a---B---C---E---F---G---H---J---K---L---M---N master
\
e---f---g---h---l---m---n develop
如何保留历史结构,并使其看起来像这样:
a---B---C---D---I---J---K---O master
\ / /
e---f---g---h---l---m---n develop
从技术上讲,您无法更改任何现有提交。
这意味着从 "change" b
到 B
,你实际上必须制作一个新的和改进的 B
。旧的 b
将继续存在(多长时间,完全是另一个问题)。常规 git rebase
,交互式或非交互式,通过将提交复制到新的和改进的提交来工作。如您所述,常规变基还 删除 合并和扁平化。
从 Git 2.18 开始,git rebase
有一个名为 --rebase-merges
的模式。这保留了合并——或者更准确地说,重新创建它们作为新的合并提交,字面意思是再次 运行ning git merge
。在 2.18 之前,git rebase
有 -p
,它使用交互式 rebase 机制并以其他方式保留合并(通过重复它们)。与更漂亮的新版本相比,它有些缺陷,但我认为(尚未测试!)应该适用于这种情况。
因此,请像使用 git rebase -i
一样使用 git rebase -i --rebase-merges
。如果您缺少 --rebase-merges
,请更新您的 Git 版本,或使用 git rebase -i -p
并非常小心不要打乱各种操作的顺序,或手动构建您的新链。
要手动构建它,运行 git checkout
提交 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
在提交 h
、i
的 second parent 的散列上,创建新的合并 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
使用了一种新的交互式指令 sheet 格式,它可以让你指定新图表在您移动提交时不会中断。