16. 仓库太大会卡顿?git gc + bfg 清理历史垃圾实战
「Git」系列已发布: 00. 改崩代码不敢回退?Git 版本控制 3 分钟救你狗命 | 01. Git 装完不会配?Windows/macOS/Linux 三端配置一次到位 | 02. Git 配置总踩坑?全局/仓库/SSH 密钥一次搞懂 | 03. add/commit/push 别再死记:Git 工作流核心概念实操 | 04. Git 分支管理:5 步彻底搞懂 branch 与 merge | 05. clone/fetch/pull/push 分不清?Git 远程协作实战 | 06. 给版本打上永久印记:Git 标签管理与发布实战 | 07. 切分支怕丢改动?Git stash 暂存恢复保护现场 | 08. rebase 不是 merge 的替代品:Git 故事线重构进阶 | 09. Git 日志与调试:log/blame/diff 让历史无所遁形 | 10. 提交代码自动检查:Git Hooks 实战,质量不再靠人工 | 11. 多仓库依赖怎么管?Git 子模块统一版本一键更新 | 12. GitFlow vs GitHub Flow vs 主干:团队工作流 3 选 1 | 13. 仓库越来越大?Git LFS 让图片视频不再撑爆仓库 | 14. 合并冲突不是噩梦:Git 合并策略与冲突解决 | 15. 常用命令记不住?Git 别名让操作变成快捷键 | 16. 仓库太大会卡顿?git gc + bfg 清理历史垃圾实战(本文) | 17. 把密码提交进 Git?安全实践:密钥管理与审计日志
克隆要十分钟、操作总转圈,多半是历史里塞了垃圾。git gc 日常维护 + bfg 批量删密/清大文件,给仓库来一次彻底瘦身。
基础操作(第 03 篇)和变基进阶(第 08 篇)要熟。历史重写操作有破坏性,务必先在克隆副本上验证。
一、仓库怎么变"胖"的
Git 仓库随时间积累赘肉,常见原因:
| 原因 | 例子 | 后果 |
|---|---|---|
| 大文件提交后又删掉 | 打包了 200MB 视频,下一提交删了 | 文件赖在历史里不走 |
| 二进制文件频繁修改 | 每次构建产出 jar/war 都提交 | 每次全量存储 |
| 误提交依赖目录 | node_modules/没进 .gitignore |
几千小文件 |
| 旧分支/标签不清理 | 上百个已合并的 feature 分支 | 冗余对象 |
| 长期不跑 gc | 松散对象堆积 | 磁盘碎片 |
1.1 诊断健康状况
1.1.1 看大小和组成
# .git 目录大小
du -sh .git
# 按大小排
git count-objects -vH
# count: 3421
# size: 12.34 MiB
# in-pack: 12451
# packs: 3
# size-pack: 245.67 MiB
# prune-packable: 0
# garbage: 0
# size-garbage: 0 bytes
# 分析 pack
git verify-pack -v .git/objects/pack/*.idx | \
sort -k 3 -n | tail -10
1.1.2 找历史中的大文件
# 方法一:列最大对象
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {print $4, substr($2,1,8), $3}' | \
sort -k3 -n -r | head -20
# 方法二:git-sizer(推荐装一个)
brew install git-sizer
git-sizer --verbose
1.1.3 检查未引用对象
# 看松散对象
git fsck --unreachable --no-reflogs
# 统计
git fsck --unreachable --no-reflogs | wc -l
# 预览 gc 会清理什么
git prune --dry-run --verbose
1.2 瘦身方案
1.2.1 方案一:清理分支和标签
# 列出远程分支
git branch -r
# 删远程分支
git push origin --delete old-feature-branch
# 清理本地已不存在的远程分支引用
git remote prune origin
# 删本地分支
git branch -d old-feature-branch
# 列出旧标签
git tag -l | sort -V | head
# 删旧标签
git tag -d old-tag
git push origin --delete old-tag
1.2.2 方案二
BFG Repo-Cleaner
BFG 是专门做 Git 历史清理的工具,比git filter-branch快 10-720 倍。
# 下载
wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar
# 清理大于 100MB 的文件
java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
# 清理特定文件类型
java -jar bfg.jar --delete-files '*.{jar,war,zip,tar.gz}' my-repo.git
# 清理特定目录
java -jar bfg.jar --delete-folders 'node_modules' my-repo.git
# 清理敏感信息(替换为***REMOVED***)
java -jar bfg.jar --replace-text passwords.txt my-repo.git
# 清理完必须跑 gc
cd my-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
注意:
- 操作裸仓库(
--bareclone)。 - 不会改当前 HEAD 的提交。
- 比
filter-branch安全,不会出空提交。
1.3 方案三
git filter-repo(现代替代)
Git 2.38+ 官方推荐:
pip install git-filter-repo
# 从历史中移除特定文件
git filter-repo --path node_modules/ --invert-paths
# 只保留特定目录(如迁移到独立仓库)
git filter-repo --path src/ --path-rename src/:
# 移除大于 10MB 的文件
git filter-repo --strip-blobs-bigger-than 10M
# 改提交者信息
git filter-repo --email-callback '
return email.replace(b"@old-domain.com", b"@new-domain.com")
'
1.3.1 方案四:git gc
# 标准 gc
git gc
# 激进压缩
git gc --aggressive --prune=now
# 看效果
du -sh .git
git gc --aggressive --prune=now
du -sh .git
gc干四件事:
- 收集松散对象打包。
- 清理不再被引用的对象。
- 压缩 pack(
--aggressive)。 - 清理 reflog 过期条目(
--prune=now)。
1.4 怎么防
1.4.1 .gitignore 管好
# 二、编译产物
*.class
*.jar
*.war
*.o
*.so
# 三、依赖目录
node_modules/
vendor/
.venv/
# 四、IDE
.idea/
.vscode/
*.iml
# 五、系统
.DS_Store
Thumbs.db
# 六、构建输出
dist/
build/
target/
# 七、大文件
*.zip
*.tar.gz
*.mp4
*.psd
1.4.2 LFS 管大文件
见第 13 篇 Git LFS 大文件管理。
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "datasets/*.csv"
1.4.3 定期维护
每季度:清已合并的 feature 分支、查仓库大小、更新 .gitignore。
每半年:git gc --aggressive、清旧标签、删除未引用对象。
每年:全量审计、评估 LFS 迁移。
1.5 pre-commit 挡大文件
#!/bin/bash
# .git/hooks/pre-commit
# 阻止 >100MB 的文件提交
MAX_SIZE=$((100**1024**1024)
git diff --cached --name-only --diff-filter=ACM | while read file; do
if [ -f "$file" ]; then
size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
if [ "$size" -gt "$MAX_SIZE" ]; then
echo "错误:$file 大小为 $(numfmt --to=iec $size),超过 100MB 限制。"
echo "请使用 Git LFS 管理大文件。"
exit 1
fi
fi
done
1.6 文章引用
- BFG Repo-Cleaner:https://rtyley.github.io/bfg-repo-cleaner
- git filter-repo:https://github.com/newren/git-filter-repo
- Git 官方 gc 文档:https://git-scm.com/docs/git-gc
- GitHub 大文件处理:https://docs.github.com/en/repositories/working-with-files/managing-large-files
📌 本文首发于微信公众号 【运维知识点滴】——专注运维实战干货,微信搜索“运维知识点滴”或扫下方二维码关注,更新不迷路。
| 微信公众号【运维知识点滴】 | 微信小程序【A5单据合并&大试卷拆分转A4打印】 |
|---|---|
![]() |
![]() |
| 运维实战干货 · 关注追更 | Word 转 PDF · 试卷/发票打印神器 · 免费 |
💡 小程序【A5单据合并&大试卷拆分转A4打印】是干嘛的? 一个 Word 转 PDF 的打印神器,专治“文档版式和打印机对不上”:
- A3 试卷打不出来? 学校发的电子版试卷多是 A3,家用打印机只吃 A4——上传 Word 一键转换:可缩到一页 A4(页数不变),也可对半拆成两张 A4(1:1 原大、字更清晰),两种模式自选;
- 版式不乱:真实渲染引擎原样转换、绝不重排内容,转出的 PDF 和原 Word 排版一致,公式/表格/图片不跑版;
- 两张 A5 单据拼一张 A4:出租车票、火车票等报销凭证拼版后,一次打印、整齐粘贴;
- 隐私省心:无需注册,转换结果留存 5 天、最多下载 5 次即自动删除,不留任何历史记录;
- 微信扫码即用,免费、免安装,支持 doc / docx(≤50MB)。
上一篇:15. 常用命令记不住?Git 别名让操作变成快捷键 | 下一篇:17. 把密码提交进 Git?安全实践:密钥管理与审计日志

