别碰 git filter-branch,它易出错、极慢、留坑;优先用 git-filter-repo(官方推荐、功能全面)或 BFG Repo-Cleaner(Java 环境友好、命令直白)。

直接说结论:别碰 git filter-branch,它容易重写错历史、极慢、还留坑;优先用 git-filter-repo 或 BFG Repo-Cleaner,前者更现代、官方推荐,后者对 Java 环境友好、命令更直白。
怎么快速定位历史里真正占空间的大文件
很多人的误区是只看当前工作区有没有大文件,但 Git 仓库胖的主因是「已被删掉、却还活在历史里的大文件」。必须从对象层扫描:
-
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sed -n 's/^blob //p' | sort -n -k 2 | tail -10 | cut -c 1-12,41- | $(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B—— 这条命令输出的是真实 blob 大小(含路径),不是压缩后大小,最准 - 注意输出中带
.so、.a、.dll、.zip、.pdf的行,这些基本就是「僵尸脂肪」,尤其.so文件往往被多次提交、每次编译都生成新哈希,体积叠加极快 - 别信
du -sh .git的表面数字——它包含 pack 文件、reflog、未清理的 dangling 对象,得结合git count-objects -vH看size-pack和count比值,如果松散对象数量远高于打包对象,说明垃圾没清干净
用 git-filter-repo 清理指定大文件或目录
这是目前最稳妥的方案,但要注意它会重写所有提交哈希,所以操作前必须备份整个项目文件夹(不是只 git clone):
- 安装:
pip install git-filter-repo(Python 3.8+),macOS 可用brew install git-filter-repo - 进项目根目录后,不要在已有远程 origin 的仓库里直接跑——
git-filter-repo默认拒绝非裸仓库操作,加--force强制,但更推荐先git clone --mirror出一个裸仓库再处理 - 删单个文件:
git-filter-repo --path path/to/bigfile.so --invert-paths --force(注意--invert-paths是保留除该路径外的所有内容,即“剔除”) - 删整个目录:
git-filter-repo --path docs/ --invert-paths --force,比逐个删文件高效得多 - 按大小批量删:
git-filter-repo --strip-blobs-bigger-than 50M --force,适合不知道具体路径但确定有大量超限文件的场景
用 BFG Repo-Cleaner 快速删 >1MB 的 .so 或其他二进制文件
如果你机器上已装 Java,BFG 是更快的选择,尤其适合 Windows 用户或 CI 环境中脚本化执行:
- 下载:
curl -O https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar - 必须用
--mirror克隆:git clone --mirror https://your/repo.git,得到repo.git目录(无工作区) - 删所有
.so:java -jar bfg-1.14.0.jar --delete-files "*.so" repo.git - 删所有大于 1MB 的 blob:
java -jar bfg-1.14.0.jar --strip-blobs-bigger-than 1M repo.git - 清理完必须进
repo.git执行:git reflog expire --expire=now --all && git gc --prune=now --aggressive,否则空间不释放
清理完推送到远程前最关键的三件事
很多人清理完一推就报错或同事拉不下来,问题几乎都出在这几步:
-
git-filter-repo会自动删除本地 remote 配置,你得手动加回去:git remote add origin https://your/repo.git - 强制推送前,先确认远程分支名(比如原来是
master),再重命名本地分支:git branch -M main,然后git push --force --all origin和git push --force --tags origin - 所有协作者必须重新克隆,不能
git pull——因为历史已变,旧提交哈希全部失效;提醒他们运行git clone后,检查git log --oneline | head -5的哈希是否和你推送的一致 - 如果项目用了 Git LFS,记得在清理后重新
git lfs track并git add .gitattributes,否则 LFS 规则不会生效于新历史
最容易被忽略的点:清理工具只动了 commit 历史,但不会自动删掉本地工作区残留的大文件。执行完所有步骤后,手动检查并 rm -rf 掉那些本不该进 Git 的 build/、dist/、.so 文件,并把它们加进 .gitignore——否则下次提交又会悄悄塞回去。


















