搜索 K
主题
合并 PR 时本想选 Squash,却误点了 Merge,主分支上多出一个合并提交和一串零碎的中间提交。本文记录如何把这次 merge 改造成 squash 效果。
把主分支 reset 回合并前,用 git merge --squash 重新合入,再强制推送覆盖远端。
* 6471e6f 合并来自 chore/xxx 的合并请求 #3 ← 误操作产生的 merge commit
|\
| * aa06d11 fix: 中间提交 9
| * ... fix: 中间提交 2~8
| * 82884e7 ci: 中间提交 1
|/
* 919dfd0 fork 点(合并前的 main)期望的结果是 919dfd0 之上只有一个 squash 提交。
# 1. 先留备份分支,指向原 merge commit,随时可回退
git branch backup/pre-squash 6471e6f
# 2. 把 main 重置回合并前
git reset --hard 919dfd0
# 3. squash 合入原分支的最终状态(aa06d11 是分支 tip)
git merge --squash aa06d11
# 4. 提交(见下文「提交信息」)
git commit
# 5. 强制推送覆盖远端
git push --force-with-lease origin main推送前可用 git diff <原merge提交> HEAD 验证:输出为空说明文件内容与原合并结果完全一致,只是历史形态变了。
git merge --squash 执行后会生成 .git/SQUASH_MSG,其中自动汇总了被压缩的所有提交信息(即平台网页 squash 时预填的那份列表)。
git commit(不带 -m):编辑器会加载这份模板git commit -m "...":模板被覆盖,正文丢失--ff / --no-ff 只决定是否产生 merge commit,不影响提交信息内容,且与 --squash 互斥。
事后想补正文,用 git commit --amend 即可,中间提交的标题列表可以这样取回:
git log <fork点>..<分支tip> --format="- %s" --reverse强推之后远端又合入了新 PR 并被本地 pull 下来。此时再执行 git commit --amend 改的是新 PR 的提交而不是 squash 提交,直接把人家的提交信息覆盖掉了。
恢复方法(提交内容都还在,靠 reflog 找回):
git reset --hard <squash提交> # 回到 squash 提交
git commit --amend -F msg.txt # 只改写它的提交信息
git cherry-pick <新PR提交> # 把新 PR 原样接回来
git push --force-with-lease origin main参见 Git cherry-pick。
--force-with-lease 而不是 --force,远端有预期外的新提交时会拒绝推送git log -1 确认 HEAD 就是要改的那个提交