重写历史
本教程共 26 篇 · 第 20 篇 · 更新于 2026-07-29 · 约 6 分钟阅读
20. 重写历史
本节目标:学会安全地修改提交历史,掌握 amend、rebase -i 和 filter-branch 三种工具的适用场景。
什么时候该重写历史
Git 允许你修改已经发生的提交。这听起来有点危险,确实如此。
重写历史的黄金法则:只改你本地的、还没推送给别人的提交。
一旦提交推送到远程仓库,别人可能已经基于它继续工作了。你强行改写历史,会让他们的仓库陷入混乱。
Warning已推送的提交不要轻易重写。如果必须改,要用
git push --force强制推送,并通知所有协作者。团队协作中这属于”大动作”。
修改最后一次提交:git commit —amend
这是最常用的历史重写。适用场景:刚提交完发现忘了加文件、提交信息写错了、代码有个小笔误。
假设你提交后发现少了一个文件:
git add forgotten_file.py
git commit --amend
这会打开编辑器让你修改提交信息。如果只想补文件不改信息:
git commit --amend --no-edit
Note
amend不是”修改”原提交,而是用一个新提交替换原提交。新提交会有不同的 SHA-1 哈希值。
修改多个提交:git rebase -i
想改更早的提交,或者合并多个提交、删除某个提交、重排提交顺序,就需要交互式变基。
交互式 rebase 的详细命令(reword/edit/squash/fixup/drop)如 §11 所述。这里聚焦安全实操步骤。
Note清单顺序和
git log相反。最旧的提交排在最上面,因为 Git 是从上往下依次重演每个提交的。
批量重写:git filter-branch
amend 和 rebase -i 适合少量提交。如果要改几百个提交(比如”把所有提交里的邮箱从旧域名换成新域名”),就需要 filter-branch。
批量替换作者邮箱
git filter-branch --env-filter '
if [ "$GIT_AUTHOR_EMAIL" = "old@example.com" ]; then
export GIT_AUTHOR_EMAIL="new@example.com"
fi
' -- --all
批量删除文件(从所有历史中)
git filter-branch --tree-filter 'rm -f secret.txt' HEAD
Warning
filter-branch功能强大但也很危险。它会重写所有匹配的提交,生成全新的历史。执行前建议先备份分支。Git 官方更推荐使用git filter-repo(需要单独安装)替代filter-branch。
安全实操:重写历史的步骤
- 确认要改的提交还没推送:
git status
- 如果已经推送,先备份远程分支:
git branch backup-main origin/main
-
执行重写(amend 或 rebase -i)
-
如果必须更新远程:
git push --force-with-lease origin main
Note
--force-with-lease比--force更安全。如果远程分支在你操作期间被别人推送过,它会拒绝推送,防止你覆盖别人的工作。
三种工具对比
| 工具 | 适合场景 | 难度 |
|---|---|---|
git commit --amend | 改最后一次提交 | 简单 |
git rebase -i | 改最近几个提交(合并/删除/重排) | 中等 |
git filter-branch | 批量重写大量提交 | 复杂且危险 |
记忆口诀:改最后用 amend,改几个用 rebase -i,改全部用 filter-branch(但要谨慎)。
这章学到了什么
- 重写历史的黄金法则:只改本地未推送的提交,已共享的历史不要轻易动
git commit --amend:修改最后一次提交,适合补文件、改提交信息git rebase -i:交互式修改多个提交,支持 reword、squash、fixup、drop、editgit filter-branch:批量重写大量提交,功能强大但危险,推荐用 filter-repo 替代- 安全操作:改之前先备份,强制推送用
--force-with-lease比--force更安全
下一章,我们学习如何在仓库中嵌入另一个仓库——子模块。