git filter-repo是官方推荐替代git filter-branch的唯一安全方案,因其基于fast-export流式处理、自动GC、默认处理全部引用、支持子模块及路径/大小过滤,且命令语义清晰(如--invert-paths控制删除)。

git filter-branch 已被 Git 官方明确弃用,继续用它清理历史等于在悬崖边开车——不是“能不能用”,而是“出问题只是时间问题”。真正安全、高效、可维护的方案只有一个:git filter-repo。
为什么必须换掉 git filter-branch?
官方文档写得非常直白:WARNING: git-filter-branch has a plethora of pitfalls...。它本质是靠 shell 脚本逐个检出、修改、提交,对大仓库极其缓慢,且容易因环境差异(比如 Windows PowerShell 默认编码、空格路径、符号链接) silently mangling 历史。而 git filter-repo 是基于 fast-export 流式解析,所有操作原子化、可预测,还自带自动 GC 和空提交修剪。
-
git filter-branch需手动指定分支范围(如HEAD或--all),漏写就只改了一半历史 -
git filter-repo默认处理全部引用(refs/*),包括 remote-tracking branches 和 tags,不用额外操心 -
git filter-branch不会自动重写.gitmodules或修正子模块路径;git filter-repo通过--submodule-rename等参数原生支持
删文件、删目录、删后缀:核心命令怎么写?
记住一个铁律:--invert-paths 才是“删除”的开关,不加它就变成“只保留匹配项”,极易误删整个仓库。
- 删单个文件:
git filter-repo --path docs/manual.pdf --invert-paths - 删多个文件:
git filter-repo --path a.bin --path b.zip --invert-paths - 删整个目录(含子目录):
git filter-repo --path assets/ --invert-paths(末尾/强烈建议带上,避免匹配到同名文件) - 删所有
.log文件:git filter-repo --path-glob '*.log' --invert-paths - 删大于 10MB 的 blob:
git filter-repo --strip-blobs-bigger-than 10M(这个参数不依赖路径,直接按对象大小过滤)
注意:--path 后的路径必须是相对于仓库根目录的,不能以 / 开头;--path-glob 支持通配符但不支持正则,要正则请用 --path-regex。
强制执行和编码问题怎么绕过?
Windows 用户常卡在 UnicodeDecodeError 或 Working tree is dirty。这不是 bug,是 git filter-repo 的安全护栏。
- 工作区不干净?先
git stash或git checkout .清理,否则它会直接中止 - PowerShell 报编码错误?加
--force参数跳过校验,但前提是确认你没在中文路径里混入非法字节 - 不确定当前状态是否适合操作?运行
git status和git rev-parse --verify HEAD双重确认
--force 不是万能钥匙,它只是绕过预检;真正出错(比如路径写错、Python 环境缺失)时仍会报具体异常,别指望它兜底。
重写完必须立刻做的事
git filter-repo 只动本地仓库,远程和协作者完全无感。你本地 history 是干净了,但别人 git pull 还是拉到旧的臃肿历史。
- 推送到远程前,必须用
git push --force-with-lease --all强制更新所有分支 - 再加一条
git push --force-with-lease --tags,否则 tag 还指着旧 commit - 通知所有协作者:他们必须
git clone新仓库,或彻底重置本地分支(git fetch && git reset --hard origin/main),git pull会失败
最易忽略的一点:git filter-repo 默认清空 reflog,一旦执行,git reflog 里再也找不到旧 commit —— 备份不是可选项,是必选项。


















