git 交互式 rebase squash 进入下一个提交

Posted

技术标签:

【中文标题】git 交互式 rebase squash 进入下一个提交【英文标题】:git interactive rebase squash into next commit 【发布时间】:2017-10-27 21:20:18 【问题描述】:

在 Git 中,我可以使用交互式 rebase 重写历史记录,这非常棒,因为在我的功能分支中,当我探索不同的重构和完成它的方法时,我使用部分工作代码进行了大量提交。

我想在将分支变基或合并到 master 之前将很多提交压缩在一起。

一些按从第一个(顶部)到底部(最后一个)的顺序组成的提交

1. Initial commit on feature branch "Automatic coffee maker UI"
2. Add hot chocolate as product
3. Add tea as product. Products are now generic
4. Create in memory data store for adapter tests
5. Cry because I can't get entity framework to create a composite key. Integration tests broken.
6. Implemented composite key!!
7. All tests green and feature done!

假设我想保留提交 3、4 和 7。

使用 rebase 我想“压缩”提交

1 和 2 进入 3。 4 次入住 5 和 6 进入 7

理想情况下在我会做的交互式变基中

1. squash
2. squash
3. pick (contains the work of 1 & 2)
4. pick 
5. squash
6. squash
7. pick (contains the work of 5 & 6)

但这是倒退的,因为 squash 将提交与之前的提交合并。我不知道如何让它向前挤压。

我是不是很难,我是否应该接受它不起作用(我宁愿让它起作用),还是有办法做到这一点?

我正在调用这个命令

git checkout My-feature-branch
git rebase master -i

然后我正在编辑出现的提交列表,并尝试通过保存文件并编辑编辑器来完成它,这通常对我有用。

【问题讨论】:

我不完全确定您正在经历什么。 squash 完全符合您的要求;将 1 和 2 融合到 3 中,将 5 和 6 融合到 7 中。留下的将是(表面上)提交、3、4 和 7。您是否遇到了这些命令的问题?请注意,您可以在本地存储库上尝试这些,但只有当您有远程存储库时,您才能重置回 我收到一条消息“错误:如果没有先前的提交就不能'squash'”,这让我陷入了一个循环,让我认为 #1 正在尝试压缩到不存在的 #0而不是#2 你是如何调用它的?向我们展示该命令。 @Makoto 你错了,恰恰相反,正如乔希在他的问题中所说的那样。 ;-) 此外,由于默认情况下存在 reflog,因此您始终可以轻松地撤消您对 rebase 所做的操作,而无需任何远程存储库。 ;-) 好问题。我正是需要这个东西,因为重新排序提交会给你带来冲突。 @Vampire 的回答是一个很好的解决方案,不会导致任何额外的工作。 【参考方案1】:

确实可以在交互式变基期间将提交压缩到以下提交中,并完全保留第二个提交的身份(包括作者、日期等)。

然而,该方法有些涉及,因此仍然感谢git rebase -i 的原生支持。

我将演示三个提交 aaaaaaa Abbbbbbb Bccccccc C,我们希望将 A 折叠成 B 并保留 B 的身份:(该方法很容易推广到更多提交)

git rebase -i aaaaaaa^ 修改编辑脚本在提交B后停止并退出编辑器:
pick aaaaaaa A
edit bbbbbbb B
pick ccccccc C
当 rebase 在B 之后停止时,恢复最新的提交两次,然后继续:
git revert HEAD
git revert HEAD
git rebase --continue
因为还原还原会还原以前的状态,所以所有后续提交都将干净地应用。 BRevert "B" 的顺序一起没有影响,但是这些提交中的第一个带有提交 B 的完整标识(实际上,在这个阶段它仍然提交 B) . BRevert "B" 可以压缩在一起形成一个无操作提交,该提交带有一个单独的rebase -i --keep-empty aaaaaaa^ 转发 B 的身份。这非常有用,建议防止虚假合并冲突,尤其是在更复杂的情况下。但是,我们将在此处跳过该步骤,并将提交保持在一起。 现在重要的提交是 Revert "Revert "B"",它将携带最初由 B 所做的所有更改。 现在rebase -i aaaaaaa^ 再次移动A 越过BRevert "B",同时将B 挤压到Revert "B"A 中:
pick   bbbbbbb B
squash b111111 Revert "B"
squash a222222 A
squash b333333 Revert "Revert "B""
pick   c444444 C
因为BRevert "B" 的顺序没有任何影响,所以您可以将任何提交移过它,同时只创建可以解决的合并冲突。 当 rebase 因合并冲突而停止时,将 git mergetool 与可自动解决琐碎冲突的工具一起使用,并让这些工具执行此操作。 Rebase 将再次停止,让您编辑压缩提交的提交消息。根据您的喜好进行编辑。 git rebase --continue 就完成了。

【讨论】:

这应该是答案。它正确地解决了这个问题。 我同意,尽管我的观点可能有偏见。不确定我们是否可以说服 @JoshR 在四年后打扰,但我很高兴您发现我的回答很有帮助。【参考方案2】:

你想要的是:

1. pick
2. squash
3. fixup
4. pick
5. squash
6. squash
7. fixup

然后,当编辑器中出现新的组合提交时,删除所选提交的行并保留修复。生成的功能补丁应该包含该系列中最后一次提交的提交消息。

【讨论】:

【参考方案3】:

Vampire's answer 是对的,但我想提供不同的观点。我认为这是你让自己过度紧张的地方:你开始:

假设我想保留提交 3、4 和 7。

然后添加:

1 和 2 进入 3。 4 次入住 5 和 6 进入 7

但这意味着您希望保留 1、2、3、4、5、6 和 7 的(内容)所有 ...而不是作为单独提交。交互式 rebase squash 并不意味着“扔掉”,甚至也不是“扔掉提交消息”,它只是意味着“组合”。如果动词是“组合”或“融合”或“混合”或类似的,可能会更好。所以顺序(pick、squash、squash)意味着:保留所有三个,同时按顺序应用它们,然后从它们中做出一个大提交。

如前所述,一旦 Git 从它们中做出一个大提交,Git 会给你另一个机会将三个组合的提交消息编辑成一个大提交消息。

rebase 完成后,您还没有保留 任何 的原始提交。相反,您进行了 new 提交。你的新的第一次提交是如何从各个部分组装的并不重要,重要的是最终的来源是什么,以及你在一个大的提交信息中放了什么。

【讨论】:

叫我挑剔,但虽然完全正确,但这并不能回答 OP 的问题,而只是告诉他他使用了错误的措辞,不是吗?如果是这样,它应该是对问题的评论,而不是答案。 @Vampire:是的,可能应该是评论。评论限制(空间和格式)使它不太适合。而且,它不一定只是措辞:提交更改ID,因此根本不是原始提交的想法很重要,因为其他分支可能会通过其原始ID保留原始提交。 @JoshR 如果你放弃了提交,你也会放弃你在其中所做的更改。 对:你不想drop它们,你想把它们总结成一个新的提交 1a,就源更改而言等于 1 + 2 + 3 (由三个git cherry-pick -n ops 组装)。 或者在交互式 rebase 中进行压缩或修复,这正是他想要做的。他只是混淆了这些节的工作顺序。使用cherry-pick 可能会起作用,但如果您可以简单地进行交互式变基,则会非常麻烦。【参考方案4】:

如果可行,您还需要对提交进行重新排序,以便待保留的提交位于待压缩的提交之前。

如果这不可行,因为你会遇到不想解决的冲突,那就去做吧

1. pick
2. squash
3. squash
4. pick 
5. pick
6. squash
7. squash

压缩完成后,您可以编辑提交消息以包含您希望最终提交包含的消息。易如反掌。 :-)

你甚至可以做到

1. pick
2. fixup
3. squash
4. pick 
5. pick
6. fixup
7. squash

然后我认为应该只启动一次提交消息编辑器,就像 fixup 一样,只需在不启动编辑器的情况下获取先前的提交消息。

当提交消息编辑器触发时,在 squash 上,您还会同时获得两个提交消息,即来自 to-be-squashed-into 和 to-be-squashed 提交的消息,因此您可以简单地删除您没有提交的提交消息不想保留。

【讨论】:

但它会保留第一次提交的日期 - 我如何让它使用最后一次提交的日期? @MichaelHewson 我想没有简单的方法;我会手动更改日期。

以上是关于git 交互式 rebase squash 进入下一个提交的主要内容,如果未能解决你的问题,请参考以下文章

在 Git 中,如何在不挑选新分支的情况下对历史中具有多个合并提交的分支进行 rebase + squash

sh git squash提交,rebase soft

Git Rebase 从带有 Squashed 合并的分支

GIT rebase讲解

git merge squash 和 rebase 区别

git merge时merge/squash merge/rebase merge的区别