为什么你的 Git 仓库越来越大:大文件治理完全指南

Why Your Git Repository Keeps Growing: A Complete Guide to Large File Management

| Sophia Li | 2026-08-10T19:55:17

上周 git clone 等了 15 分钟,一查仓库 4.7GB。这篇文章记录了大文件治理的完整方案,最终仓库缩小了 95%。

Git repository grew to 4.7GB. This article documents the complete large file management solution that reduced it by 95%.

## 发现问题 上周 `git clone` 项目仓库,等了 15 分钟。看了一下仓库大小——**4.7GB**。一个 Java 后端项目凭什么这么大? ```bash git rev-list --objects --all | \ git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \ sort -rnk3 | head -20 ``` 结果触目惊心:好几个几百 MB 的 jar 包、一堆测试用的 SQL 数据文件、还有人把 node_modules 提交进来过。虽然后来删了,但 Git 历史里永远保留着。 ## Git 为什么会越来越大 Git 是一个**内容寻址的存储系统**。每次 commit 保存的是文件的完整快照(通过 pack 优化会做 delta 压缩,但只对文本文件有效)。 这意味着: 1. **二进制文件的每次修改都是全量存储**:一个 100MB 的 jar 包,改一个字节,Git 历史里就多了 100MB 2. **删除文件不会减小仓库体积**:`git rm` 只是在当前 commit 里删除了引用,历史中的对象还在 3. **`git gc` 只能优化 pack,不能删历史对象** ## 治理方案 ### 方案一:BFG Repo-Cleaner(推荐) 用来清理 Git 历史中的大文件,比 `git filter-branch` 快 10-700 倍: ```bash # 删除历史中所有超过 50MB 的文件 java -jar bfg.jar --strip-blobs-bigger-than 50M my-repo.git # 删除历史中的特定文件 java -jar bfg.jar --delete-files '*.sql.gz' my-repo.git # 清理并推送 cd my-repo.git git reflog expire --expire=now --all git gc --prune=now --aggressive git push --force ``` 注意:**这会改写 Git 历史**,所有协作者都需要重新 clone。 ### 方案二:Git LFS Git Large File Storage 将大文件存储在外部服务器,Git 仓库里只保留指针文件: ```bash # 安装 Git LFS git lfs install # 跟踪大文件类型 git lfs track "*.jar" git lfs track "*.zip" git lfs track "*.sql.gz" git lfs track "*.png" # .gitattributes 文件会被自动更新 cat .gitattributes # *.jar filter=lfs diff=lfs merge=lfs -text ``` 迁移现有大文件到 LFS: ```bash git lfs migrate import --include="*.jar,*.zip" --everything ``` ### 方案三:预防措施 最好的治理是预防。在 `.gitignore` 和 Git hooks 里拦截: ```bash # .gitignore *.jar *.war *.zip *.tar.gz *.sql.gz node_modules/ build/ dist/ target/ # pre-commit hook:拦截超过 10MB 的文件 #!/bin/bash max_size=10485760 # 10MB files=$(git diff --cached --name-only) for f in $files; do size=$(git cat-file -s ":$f" 2>/dev/null || echo 0) if [ "$size" -gt "$max_size" ]; then echo "ERROR: $f is $(($size/1048576))MB, exceeds 10MB limit" echo "Use Git LFS for large files: git lfs track '$f'" exit 1 fi done ``` ## 我们的实战效果 按照上面的方案,我们的治理过程: 1. 用 BFG 清理了历史中的 jar、zip 和 SQL 文件 2. 设置 Git LFS 管理测试数据和二进制依赖 3. 配置了 pre-commit hook 拦截大文件 4. 更新了 `.gitignore` 结果: | 指标 | 治理前 | 治理后 | |------|--------|--------| | 仓库大小 | 4.7GB | 230MB | | clone 时间 | 15 分钟 | 40 秒 | | fetch 时间 | 3 分钟 | 5 秒 | 4.7GB 到 230MB,缩小了 95%。clone 时间从 15 分钟到 40 秒。 ## 建议 1. **项目创建第一天就配好 `.gitignore` 和 Git LFS** 2. **定期检查仓库大小**,用 `git-sizer` 工具自动化 3. **在 CI 里加仓库大小检查**,超过阈值就告警 4. **团队规范里明确禁止提交二进制文件** 不要等仓库涨到几个 GB 才治理,到时候改写历史的代价会很大。


## The Problem A Java project repository grew to 4.7GB, taking 15 minutes to clone. Investigation revealed historical jar files, SQL data dumps, and accidentally committed node_modules. ## Why Git Grows Git stores complete file snapshots. Binary file modifications create full copies in history. Deleting files only removes current references — historical objects remain. ## Solutions 1. **BFG Repo-Cleaner**: Remove historical large files (10-700x faster than git filter-branch) 2. **Git LFS**: Store large files externally with pointer references in the repository 3. **Prevention**: .gitignore rules and pre-commit hooks blocking files over 10MB ## Results Repository shrank from 4.7GB to 230MB (95% reduction). Clone time improved from 15 minutes to 40 seconds.

← Back to News