必须扫描对象层定位历史大文件,因Git仓库臃肿主因是已被删除却仍存于历史的“僵尸脂肪”(如.so、.zip等),用git rev-list --objects --all | git cat-file --batch-check=...可精准输出真实blob大小及路径,再结合git count-objects -vH查看size-pack字段判断是否需清理。

怎么快速定位真正占空间的历史大文件
别只看当前工作区,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-。这条命令输出的是真实 blob 大小(含路径),不是压缩后大小。
常见「僵尸脂肪」包括:.so、.a、.dll、.zip、.pdf —— 尤其 .so 文件,每次编译生成新哈希,体积叠加极快。
du -sh .git 的数字不可信:它包含未清理的 dangling 对象、reflog 和 pack 冗余 delta。真正要看的是 git count-objects -vH 输出里的 size-pack 字段,如果超过 100MB 就该动手了。
为什么必须用 git-filter-repo,而不是 git rm 或 reset
git rm 只删最新提交,历史里文件还在;git reset --hard + push -f 更危险:被 reset 的提交仍被 reflog 引用,Git GC 不回收,远程也还存着旧对象——仓库体积几乎不变。
只有重写历史的工具能让对象彻底从所有提交树中消失。git filter-branch 已被 Git 官方弃用,慢、易出错、内存高;BFG Repo-Cleaner 虽快但维护趋弱;git-filter-repo 是目前唯一官方推荐、Python 实现、功能全、内存低、自动保留标签的方案。
注意:git-filter-repo 会重写所有提交哈希,操作前必须完整备份整个项目文件夹(不是只 git clone),否则恢复无门。
实操时最容易踩的三个坑
第一,直接在已有远程 origin 的非裸仓库里运行:git-filter-repo 默认拒绝,加 --force 强行执行风险极高。更稳妥做法是先 git clone --mirror 出一个裸仓库再处理。
第二,误用 --path 参数逻辑:--path path/to/file --invert-paths 表示“剔除该路径”,不是“保留”。想删 dist/ 目录?得写 git filter-repo --path dist/ --invert-paths --force,漏掉 --invert-paths 就只剩这个目录了。
第三,清理完没清远程引用:本地变小了,但 git ls-remote --heads origin 还能看到旧提交 ID。GitHub 默认保留 unreachable 提交 30 天,必须进 Settings → Danger Zone → “Delete all refs that point to commits that are not reachable” 手动触发清理,否则协作同学 git pull 仍可能拉到旧对象。
清理后如何安全推送到远程并通知协作者
执行 git-filter-repo 后,本地 remote 地址会被自动删除(安全机制)。你需要重新添加:git remote add origin <your-repo-url>,再强制推送:
-
git push --force --all(推所有分支) -
git push --force --tags(推所有标签)
所有协作者必须重新克隆:git clone <url>。不能 git pull,因为旧提交哈希已失效,会报错 fatal: refusing to merge unrelated histories 或类似冲突。
特别提醒:CI/CD 流水线脚本里如果有缓存 .git 目录的步骤,也要同步更新——旧缓存会拖慢甚至破坏后续构建。


















