Git fixup 与 squash 整理提交
Git fixup 与 squash 整理提交
在功能开发或 Code Review 修修补补时,往往会产生很多「修 typo」「补漏测」「按 review 改一版」之类的小提交。fixup / squash 用来把这些变更合并进更早的某次提交,让 git log 更干净,而不必手动 git reset 再重提。
与「调整提交顺序」相关的交互式 rebase 总览见 Git 提交记录顺序调整。
核心概念
| 方式 | 合并时是否保留「被合并提交」的说明 |
|---|---|
| squash | 保留,rebase 时会打开编辑器让你编辑合并后的 message |
| fixup | 丢弃被合并提交的 message,只保留前一个提交的说明 |
两者都会把变更内容(diff)并入正上方的那条提交(pick 下面的第一条有效提交)。
pick a1b2c3 feat: 添加 UART 驱动 ← 保留这条的 message(fixup 时)
fixup d4e5f6 fix: 修编译警告 ← 内容并入上一条,本条 message 丢弃
fixup 789abc fix: 改波特率默认值 ← 同上
方式一:git commit --fixup / --squash(推荐日常)
先找到要「并入」的目标提交的 hash(或相对引用):
git log --oneline -5
# a1b2c3 feat: 添加 UART 驱动
# d4e5f6 fix: 修编译警告
在改完工作区后,不直接 git commit,而是:
# 生成一条「待 fixup 到 a1b2c3」的提交(message 以 fixup! 开头)
git add .
git commit --fixup=a1b2c3
# 或:待 squash 到 a1b2c3(message 以 squash! 开头,合并时可编辑总 message)
git commit --squash=a1b2c3
生成的提交信息形如:
fixup! feat: 添加 UART 驱动
或:
squash! feat: 添加 UART 驱动
fixup! / squash! 前缀后的文字应与目标提交的 subject 一致(至少能匹配),这样 --autosquash 才知道挂到哪条提交下面。
自动合并:git rebase -i --autosquash
把栈里所有 fixup! / squash! 提交自动排到对应目标提交之后,并执行 fixup/squash:
# 整理最近 N 条(含 fixup 提交)
git rebase -i --autosquash HEAD~5
# 从某分支点之后全部整理
git rebase -i --autosquash main
也可设成默认行为,少打 --autosquash:
git config --global rebase.autoSquash true
# 之后:git rebase -i HEAD~5 会自动排列 fixup/squash 行
建议流程(本地分支修 PR):
git commit --fixup=<目标hash> # 可多次
git rebase -i --autosquash HEAD~10
git log --oneline # 确认只剩一条 feat + 干净历史
方式二:交互式 rebase 里手写 f / s
不事先 commit --fixup 时,在 git rebase -i 编辑器里把后面提交的命令改成 fixup 或 squash:
git rebase -i HEAD~4
pick a1b2c3 feat: 添加 UART 驱动
fixup d4e5f6 fix: 修编译警告
fixup 789abc fix: 改波特率
pick fedcba0 docs: 更新 README
fixup简写f:并入上一条,不要本条 messagesquash简写s:并入上一条,保存后会弹出编辑器合并 message
多条 fixup 会按顺序依次并入同一个 pick 提交。
与 git commit --amend 的区别
--amend |
--fixup + rebase |
|
|---|---|---|
| 作用对象 | 最近一次提交 | 任意历史中的某次提交 |
| 是否改 hash | 改掉当前 HEAD 的 hash | rebase 后整段历史 hash 可能都变 |
| 典型场景 | 刚提交发现漏文件、改 message | 要把补丁并进 3 条之前的 feat |
# 只改「上一笔」:补文件、改 message
git add .
git commit --amend --no-edit
若目标不是 HEAD~0,用 fixup 更合适。
squash 合并 message 的编辑
squash 或 commit --squash 后,rebase 结束时会打开编辑器,例如:
# This is a combination of 3 commits.
# The first commit's message is:
feat: 添加 UART 驱动
# This is the 2nd commit message:
fix: 修编译警告
# This is the 3rd commit message:
fix: 改波特率
删掉不需要的行,整理成一条 Conventional Commits 即可(参见 约定式提交)。fixup 不会弹出这一步。
其他相关 rebase 动作(简表)
| 命令 | 简写 | 说明 |
|---|---|---|
pick |
p |
保留该提交 |
reword |
r |
保留提交,只改 message |
edit |
e |
停在该提交,便于 git commit --amend 拆改 |
squash |
s |
并入上一条,合并 message |
fixup |
f |
并入上一条,丢弃本条 message |
drop |
d |
删除该提交 |
已推送提交时的注意
- fixup / squash 会重写历史,合并后提交的 hash 全部变化。
- 未推送的个人分支:随意使用。
- 已推送且他人可能基于该分支开发:需要
git push --force-with-lease(比--force安全),并和团队确认;共享的main/master上一般禁止 force push。
git push --force-with-lease origin feature/uart
冲突时:
git status
# 解决冲突后
git add .
git rebase --continue
# 放弃本次 rebase
git rebase --abort
常见问题
Q: autosquash 没有自动排列 fixup 行?
- 确认
fixup!后的 subject 与目标提交 title 一致(大小写、空格要能对上)。 - 目标提交必须在本次
rebase -i的范围内。 - 是否启用了
rebase.autoSquash;旧版 Git 需显式加--autosquash。
Q: 想合并到「不是上一行」的那条提交?
用 git commit --fixup=<hash> + --autosquash,不要只靠手写顺序;autosquash 会把 fixup 提交移到正确目标之后。
Q: 和 git merge --squash 一样吗?
不一样。merge --squash 是把整个分支压成一次新提交再并入当前分支;本文的 fixup/squash 是在同一条线性的提交历史里整理已有 commit。
命令速查
# 创建待合并的补丁提交
git commit --fixup=<commit>
git commit --squash=<commit>
# 自动执行合并
git rebase -i --autosquash HEAD~N
git config --global rebase.autoSquash true
# 只改最近一次
git commit --amend
# 查看结果
git log --oneline -10
git show HEAD
小结
- fixup:补丁并进指定提交,丢掉补丁自己的 commit message,适合 review 小改、修编译。
- squash:同样合并 diff,但保留各条 message 供你编辑成一条。
- 日常推荐:
git commit --fixup=<hash>→git rebase -i --autosquash,比纯手改 rebase 列表不易排错。 - 合并前保证工作区干净;已推送分支慎用,必要时
--force-with-lease并协调团队。
- 感谢你赐予我前进的力量

