写代码时 commit 颗粒度往往很碎:"修了个 typo"、"WIP"、"忘了 import"。
推到主分支前用 rebase -i 重写历史,让 PR 干净。
1. 启动
git rebase -i HEAD~5 # 重写最近 5 个提交
# 或:
git rebase -i origin/main # 重写从 main 分叉以来的所有提交
打开编辑器:
pick a1b2c3d Add user model
pick e4f5g6h Add tests
pick i7j8k9l fix typo
pick m1n2o3p more tests
pick q5r6s7t WIP
# Rebase 1234567..q5r6s7t onto 1234567 (5 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log
# d, drop = remove commit
2. 最常用:squash + 改 message
pick a1b2c3d Add user model
fixup e4f5g6h Add tests # 合到上一条,丢弃 message
fixup i7j8k9l fix typo
pick m1n2o3p more tests
fixup q5r6s7t WIP
保存退出。Git 把这 5 个提交重写成 2 个:
- 第 1 个:a1b2c3d 的 message + 包含 a/e/i 的代码
- 第 2 个:m1n2o3p 的 message + 包含 m/q 的代码
fixup vs squash:squash 让你重新写合并后的 message,fixup 直接丢弃。
有改 message 需求用 squash,没需求用 fixup。
3. 调整顺序
直接把行换位置即可:
pick m1n2o3p more tests # 原本第 4 个
pick a1b2c3d Add user model # 原本第 1 个
...
Git 会按新顺序重新 cherry-pick。换序时如果两提交改动了同样的文件,
可能 conflict —— 解决然后 git rebase --continue。
4. 拆一个提交
想把一个"什么都改了"的大提交拆成几个小的:
edit a1b2c3d Big mess of changes
保存后 git 停在那个提交,你可以:
git reset HEAD~ # 把改动放回工作区
git add file1.py
git commit -m 'add file1'
git add file2.py
git commit -m 'add file2'
git rebase --continue
5. 改某个提交的内容
发现某个旧提交少改一行:
edit e4f5g6h Add tests
停下来后:
# 改你要改的文件
vim test_user.py
git add test_user.py
git commit --amend --no-edit
git rebase --continue
6. autosquash:标记 fixup 提交
写代码时直接打 fixup 标签:
git commit --fixup=a1b2c3d # 标记这是给 a1b2c3d 的修补
# 后续可以多个 fixup 提交
git commit --fixup=a1b2c3d
git commit --fixup=m1n2o3p
然后:
git rebase -i --autosquash origin/main
--autosquash 自动把每个 fixup! 提交移到对应原提交后面,并标 fixup。
你只需要确认 + 保存。这是我个人 99% 的 rebase 用法。
可以让 autosquash 默认开:
git config --global rebase.autosquash true
7. 安全推
rebase 后本地历史和远端不一致,普通 push 会被拒绝。要 force push:
git push --force-with-lease origin feature-branch
--force-with-lease 比 --force 安全:如果远端有别人新推的提交(你不知道),
会拒绝。--force 会盲目覆盖,可能毁掉同事的工作。
8. 出错回滚
rebase 把事情搞乱了?
git rebase --abort # 进行中的 rebase 直接放弃
git reflog # 看历史所有 HEAD 移动
git reset --hard HEAD@{5} # 回到 5 步前的状态
reflog 是 Git 的 undo 神器,rebase 出错 90% 都能从这里救回来。
默认保留 90 天。
9. 不要 rebase 的场景
- 已经推到共享分支(main / develop)的提交:rebase 后 force push
会让其他人 pull 时混乱 - 多人共同开发的 feature 分支:同理
- 安全的规则:"只 rebase 自己的本地分支或单人 feature 分支"
10. merge --squash 的替代方案
如果 feature 分支提交特别乱,懒得 rebase -i,直接:
git checkout main
git merge --squash feature-branch
git commit -m 'feat: add user system'
会把整个 feature 分支压成一个 staged 改动,由你写一条 commit message。
这是"合并时清整历史"的快捷做法,但失去了原始提交的颗粒度。
踩过的坑
- rebase 时 conflict 改完忘记
git add→git rebase --continue报错
"no changes"。 - 用 GUI 工具做 rebase:很多 GUI 让操作太容易,新手秒搞砸主分支。
建议命令行做 rebase,至少有一道心理门槛。 git push --force主分支:CI 测试基于旧 SHA 的 deploy 全失效,
其他 dev 拉不下来。绝对禁止,用 GitHub branch protection 锁住。- rebase 改 message 时含中文,OS / 编辑器编码不一致会变乱码。
git configi18n.commitEncoding utf-8。
登录后参与评论。