git rebase -i如何将最近3个提交压缩成一个?

文章导读
把最近 3 个提交压成 1 条,在提交历史还没推送出去时是很常见的整理动作。下面是我自己会走的流程,其中每一步都会说明为什么这么做,以及做完怎么确认。
📋 目录
  1. A 先确认这个分支能不能动
  2. B 改 pick 为 squash,保存退出
  3. C 压缩后检查 log 和 status
  4. D 操作时最容易踩的几个坑
  5. E 出错后怎么退回原状
A A

把最近 3 个提交压成 1 条,在提交历史还没推送出去时是很常见的整理动作。下面是我自己会走的流程,其中每一步都会说明为什么这么做,以及做完怎么确认。

先确认这个分支能不能动

rebase -i 会重写这些提交的哈希,所以操作前先确认这个分支没有推送过,或者推送后没有其他人基于它工作。可以用 git log --oneline -5 查看提交,确认需要压缩的确实是最近 3 个提交。如果当前分支已经推送到远程,最好放弃压缩,或只在个人功能分支上操作。如果确认只由你一个人使用,后面可以用 git push --force-with-lease 同步,否则历史不一致会让其他人拉取时冲突。

改 pick 为 squash,保存退出

操作本身不复杂,只需要改两个单词。下面这段是核心动作:

运行git rebase -i HEAD~3,会打开一个文本编辑器,列出最近的3条提交记录,每行格式为pick+commit hash+提交信息。把第2行和第3行的pick改为squash,保留第1行为pick,然后保存退出。Git会依次应用第1个提交,并把后两个提交的修改合并到前一个,接着弹出另一个编辑器让你合并提交信息。可以保留多条提交信息,或者删除所有内容改为一条完整描述。

编辑时如果用的是 Vim,先按 i 进入插入模式,改完按 Esc 输入 :wq 保存。第二个编辑器打开的是提交信息合并页面,Git 会把三条提交信息都列出来,默认保留所有。我一般只保留第一条,或者删掉前面的内容,换一行写清楚这次合并带来的完整改动。

git rebase -i如何将最近3个提交压缩成一个?

压缩后检查 log 和 status

压缩的确认步骤很简单:

压缩完成后,用git log --oneline -3检查提交列表。正常情况下应该只剩1条提交,而不是3条。再用git status确认工作区是干净的,没有出现rebase in progress提示。如果中间改动了同一个文件的同一处,Git可能会暂停并标记冲突,这时需要用git status查看冲突文件,手动解决后执行git add,再继续git rebase --continue。整个过程没有冲突会直接完成。

如果压缩过程中被冲突打断,git status 会明确显示 “rebase in progress” 和冲突文件列表。解决完冲突后,先 git add,再 git rebase --continue,Git 会用同一个编辑窗口再次让你确认提交信息。如果发现自己处理不了,git rebase --abort 可以立即放弃这次操作,回到运行前状态。

操作时最容易踩的几个坑

在 squash 语法上栽跟头的人不少,这里列几个常见的:

git rebase -i如何将最近3个提交压缩成一个?

最常见的坑是把pick改成squash时拼写错误,比如写成squashs,Git会立刻报错并中止rebase。另一个坑是忘记保存第一个pick,导致全部提交都被合并成一条空记录。还有人在编辑提交信息时直接退出编辑器,没有保存,Git会取消整个压缩操作。此外,如果最近3个提交中有merge commit,rebase -i会额外展开,压缩逻辑会变复杂,建议先不要处理merge commit,或者用git rebase --rebase-merges先了解结构。

出错后怎么退回原状

压缩过程中乱了也不要急着重建分支。Git 在多数情况下会留下可恢复的记录。用 git reflog 能看到 rebase 之前 HEAD 的位置,那个哈希就是压缩前的提交。确认无误后执行 git reset --hard <原哈希> 就能回到压缩前。但 reset 会丢弃压缩过程中产生的新提交,所以执行前可以先记录一下当前状态。

也可以在执行 rebase 之前打一个备份分支,例如 git branch backup-before-rebase,这样如果看不惯 reflog,直接切回备份分支就行。不管用哪种方式,压缩操作本身不会改变文件内容,所以备份分支里的内容应该和压缩后完全一致。

把最近 3 个提交压成一个,适合在功能分支还没合入主干前做。一旦提交已经进入共享远程分支,或者团队里其他人已经拉过,压缩反而会给协作添乱。我通常只在本地分支上整理,推远程之前先压缩,推完之后不再动历史。