git rebase -i:把零散提交压成一条 + 改提交信息 + 拆提交

写代码时 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 addgit rebase --continue 报错
    "no changes"。
  • 用 GUI 工具做 rebase:很多 GUI 让操作太容易,新手秒搞砸主分支。
    建议命令行做 rebase,至少有一道心理门槛。
  • git push --force 主分支:CI 测试基于旧 SHA 的 deploy 全失效,
    其他 dev 拉不下来。绝对禁止,用 GitHub branch protection 锁住。
  • rebase 改 message 时含中文,OS / 编辑器编码不一致会变乱码。
    git config i18n.commitEncoding utf-8
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。