1. 当仓库膨胀时会发生什么?

每个开发者都可能遇到这样的场景:某天你执行git push时突然收到GitLab的红色警告:"Repository size exceeds 10GB limit"。就像塞满杂物的房间,你的仓库里可能躺着陈旧的二进制文件、失效的依赖包或者被遗忘的测试数据。这些"仓库垃圾"不仅影响协作效率,还会拖慢CI/CD流水线的执行速度。

2. 诊断仓库健康状态(Git技术栈实战)

2.1 使用Git自带工具扫描

# 查看前10大文件(包含历史记录)
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {print $3, $4}' | \
sort -k1 -n | \
tail -n 10

# 示例输出:
# 234567890  path/to/obsolete-lib.zip
# 123456789  docs/archived-video.mp4

这个命令像X光机一样扫描整个仓库,列出体积最大的10个文件。注意这些文件可能分布在历史提交中,即使当前分支已删除,仍然占用空间。

2.2 可视化分析工具

brew install reposurgeon  # macOS安装仓库分析工具
git clone --mirror your_repo.git
reposurgeon <<< "read your_repo.git" "list size" "quit"

Reposurgeon会生成类似这样的报告:

Commit 1a2b3c4: 添加测试视频 (201MB)
|-- path/to/demo.mp4 (198MB)
Commit 5d6e7f8: 临时上传依赖包 (512MB)
|-- vendor/old-sdk.tar.gz (510MB)

3. 手术刀级清理方案(Git Filter-Repo实战)

3.1 精确移除指定文件

# 安装最新版git-filter-repo
pip3 install git-filter-repo

# 创建仓库镜像(安全操作)
git clone --mirror git@gitlab.com:yourgroup/yourproject.git
cd yourproject.git

# 执行文件清理(示例删除所有.zip文件)
git filter-repo --path-glob '*.zip' --invert-paths

# 强制推送到远端(危险操作!)
git push --force origin

这个操作就像时间机器,从所有历史提交中抹去指定文件。注意--path-glob支持通配符,*.jpg会匹配所有层级下的jpg文件。

3.2 目录级清理示例

# 删除已废弃的Android SDK目录
git filter-repo --path 'android/sdk/' --invert-paths

# 保留特定目录外的所有内容
git filter-repo --path 'src/' --path 'docs/' --invert-paths

当某个子目录完全不需要时(如废弃的移动端代码),这种批量删除能显著缩减体积。但要注意路径匹配的精确性,避免误删。

4. 高级优化技巧

4.1 重写提交历史

# 压缩最后20次提交为1次
git filter-repo --commit-callback '
  if commit.committer_date > b"2023-01-01":
      commit.message = b"Batch: historical commits compaction"
'

这对频繁提交的小文件特别有效,比如持续生成的配置文件。重写后仓库的元数据体积可减少40%-60%。

4.2 使用Git LFS管理大文件

# 安装Git LFS
git lfs install

# 跟踪PSD设计文件
git lfs track "*.psd"

# 查看追踪规则
cat .gitattributes
# 输出:*.psd filter=lfs diff=lfs merge=lfs -text

当必须保留大文件时,LFS就像仓库的"外置硬盘"。但要注意:

  1. LFS存储有单独配额
  2. 需要配置GitLab的LFS支持
  3. 二进制文件无法diff

5. 操作风险防控指南

5.1 操作前必须做的三件事

  1. 本地备份:git bundle create backup.bundle --all
  2. 通知团队:清理期间锁定仓库
  3. 测试验证:在测试仓库完整执行CI流程

5.2 清理后验证

# 检查仓库实际大小
git count-objects -vH

# 查看远端仓库信息
curl -H "PRIVATE-TOKEN: <your_token>" \
"https://gitlab.com/api/v4/projects/<project_id>/"

重点关注响应中的statistics字段,对比清理前后的存储占用变化。

6. 技术方案对比分析

方法 适用场景 优点 缺点
filter-repo 历史大文件清理 精确控制,支持正则 需要重写历史
BFG工具 快速清理常见文件类型 执行速度快 配置灵活性较低
仓库镜像重建 极端情况下的彻底清理 完全控制仓库内容 丢失所有分支/标签
Git LFS 必须保留的大文件管理 透明化存储 需要额外配额和配置

7. 防患于未然的5个习惯

  1. .gitignore中屏蔽*.log, *.tmp等临时文件
  2. 使用pre-commit钩子检查文件大小:
# .git/hooks/pre-commit
MAX_SIZE=1048576 # 1MB
for file in $(git diff --cached --name-only); do
  size=$(wc -c < "$file")
  if [ $size -gt $MAX_SIZE ]; then
    echo "错误:$file 超过1MB限制"
    exit 1
  fi
done
  1. 定期执行仓库体检(季度/半年)
  2. 建立分支清理策略,删除已合并的feature分支
  3. 在CI流水线中加入仓库大小检查

8. 总结与建议

经历过一次痛苦的仓库清理后,我们团队建立了这样的规范:任何超过10MB的文件必须经过架构评审才能入库。对于历史遗留项目,建议采用分阶段清理策略:

  1. 第一阶段:用git filter-repo清理已确认的废弃文件
  2. 第二阶段:迁移仍在使用的资源到LFS
  3. 第三阶段:重构项目结构,拆分出独立仓库

记住,仓库健康不是一次性任务,而是需要持续关注的开发习惯。当你的git clone时间从半小时缩短到一分钟时,就会明白这些努力的价值所在。