如何使用 git rebase 压缩围绕合并的提交?

How to use git rebase to squash commits around a merge?

围绕与 git 的合并变基的最简单方法是什么?

在我的一个分支中,我一直在推测地玩弄一堆不同的东西,有大量失败和部分失败的尝试。现在我已经可以正常工作了,我想回去压缩一堆猜测和检查提交,使用交互式变基。

复杂的是,在这个过程中,我合并了另一个分支。我想按原样保留该合并分支(不重写任何提交),并将合并提交保留在我分支中的相同相对位置。 (因为我的分支中的功能有一些 before/after 依赖性。)我尝试直接 git rebase -i,但我合并的分支从我上次分歧的点开始有相当多的提交,当它们出现在交互式 rebase 文件中时,它们使事情变得复杂。

图表可能会有所帮助。我现在拥有的:

#-#-#-#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* My branch
 \                   /      
  #-#-#-#-#-#-#-#-#-#-#-#-# The merged branch

我想要的:

#-#-#-#-----*-------*-*----*------* My branch
 \                   /      
  #-#-#-#-#-#-#-#-#-#-#-#-# The merged branch

(“#”是我想保持不变的提交,SHA1 方面,两个合并提交的文件系统内容应该相同。)

我还没有推送任何我想变基到远程服务器的提交。 (虽然我合并的分支在远程服务器上是。)

您可以采用手动方法,只需在从 MyBranch 到 TheMergedBranch 的第一个分支提交之间对 MyBranch 进行交互式变基,以压缩您的内容。然后在 TheMergedBranch 中合并。然后使用上游和提交从原始 MyBranch 中选择的上游和提交来进行交互式变基 - 到您的新分支。

是正确的——记住 rebase 只是 copying 提交——但是执行起来有点棘手。您需要保存两个特定的提交 ID,甚至可能使用另一个分支名称。

我实际上会用一个新的分支名称和 git cherry-pick 以及一个新的 git merge,我自己来做到这一点。这是方法,使用您的图表,稍微注释,以及一些实际的 Git 命令。你有:

A-#-#-B-*-*-*-*-*-*-C-M-D-*-*-*-*-*-*-E My branch
 \                   /      
  #-#-#-#-#-#-#-#-#-#-#-#-# The merged branch

你想要:

A-#-#-B-----*-------*-m----*------* My branch
 \                   /      
  #-#-#-#-#-#-#-#-#-#-#-#-# The merged branch

您可以从:

开始
git checkout -b rebased B

这会让你得到一个指向提交 B 的新分支名称。现在您可以:

git cherry-pick -n B..C

(或使用原始 ID)复制 B 之后的所有 * 提交,包括 C,然后 git rebase -i B 并挤压一些以获得你的两个* 提交。

现在您可以合并了;所以 git merge M^2 与来自 The merged branch 的提交 M 的第二个父级合并。如果失败(或者如果你想修改它,添加 --no-commit 现在你在它停止的地方),你可以通过直接从提交 M 检查文件来修复合并,假设你想要相同的合并你在那里的分辨率。

(如果您启用了 git rerere,此时大部分情况会自动发生。)

提交或合并完成后,您将获得新合并提交的 ID m

现在您已准备好从 DE 之间挑选提交,所以:

git cherry-pick D^..E

(我们在这里需要 D^ 来包含 D — 或者您可以使用提交 M 的原始 SHA-1 ID,原始合并)。然后 git rebase -i m 仅对 D-through-E 的新副本进行变基,而不备份新合并 m.

当你全部完成后,你就有了你想要的图表。分支的名称是 rebased 而不是 My branch1,并且您有分支 My branch 指向提交 E。所以 git checkout My branch 然后 git reset --hard rebased 将旧名称指向新的 branch-tip-commit,之后你可以删除名称 rebased.


1显然这不是实际名称,因为在分支名称中有 space 是不好的。

不要为此使用变基,使用移植物。请记住,提交是 full-content 个快照,您想要的结果是 content-identical 与现有提交的提交,您只想重新连接亲子关系。这就是移植物的用途。

首先,在沙盒存储库中执行此操作,这样如果您不喜欢正在发生的事情,就可以放弃并擦除它:

git clone -s . "${other=`mktemp -dt`}"
cd "$other"

设置你想要的亲子关系

echo > .git/info/grafts $(git rev-parse M B M^2)
echo >>.git/info/grafts $(git rev-parse E M)

(我想你还有一两个你想要的提交,只需列出你真正想要 parents 的提交来代替你已经拥有的提交,git的复习行走将跟随你的脚步)

然后

git filter-branch -- --all

如果您喜欢结果,请将它们推回去。