首页 / Git 入门教程 / 重写历史

Git 入门教程

重写历史

本教程共 26 篇 · 第 20 篇 · 更新于 2026-07-29 · 约 6 分钟阅读

GitGit 入门教程amendrebasefilter-branch重写历史交互式变基

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

amendrebase -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

安全实操:重写历史的步骤

  1. 确认要改的提交还没推送:
git status
  1. 如果已经推送,先备份远程分支:
git branch backup-main origin/main
  1. 执行重写(amend 或 rebase -i)

  2. 如果必须更新远程:

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、edit
  • git filter-branch:批量重写大量提交,功能强大但危险,推荐用 filter-repo 替代
  • 安全操作:改之前先备份,强制推送用 --force-with-lease--force 更安全

下一章,我们学习如何在仓库中嵌入另一个仓库——子模块。