Skip to content

大仓库优化

概述

随着项目的发展,Git 仓库可能会变得非常大,导致克隆、拉取、推送等操作变慢。本章介绍大仓库的优化策略和技巧。

大仓库问题

常见问题

bash
# 克隆速度慢
git clone https://github.com/large-project/project.git
# 可能需要几分钟甚至更长时间

# 拉取速度慢
git pull origin main
# 每次拉取都需要传输大量数据

# Git 操作变慢
git status
git log
git diff
# 响应时间明显增加

# 磁盘空间占用大
du -sh .git
# 可能达到几 GB

问题原因

原因说明
历史记录长多年开发积累大量提交
大文件包含大型二进制文件
频繁提交每次提交都保存完整快照
分支众多大量分支和标签
未清理未运行垃圾回收

分析仓库大小

bash
# 查看仓库大小
du -sh .git

# 查看各目录大小
du -sh .git/*

# 查看最大的文件
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  sed -n 's/^blob //p' | \
  sort --numeric-sort --key=2 | \
  tail -20

# 使用 git-sizer 工具
# 安装: go install github.com/github/git-sizer@latest
git-sizer

# 查看提交数量
git rev-list --count HEAD

# 查看分支数量
git branch -a | wc -l

# 查看标签数量
git tag | wc -l

单体仓库策略

Monorepo 概述

单体仓库(Monorepo)是将多个项目放在一个仓库中的策略。

优点

  • 统一版本管理
  • 简化依赖管理
  • 方便代码共享
  • 原子提交

缺点

  • 仓库体积大
  • 克隆时间长
  • 权限控制复杂

Monorepo 优化策略

1. 目录结构优化

monorepo/
├── packages/
│   ├── core/
│   ├── utils/
│   └── ui/
├── apps/
│   ├── web/
│   └── mobile/
├── docs/
└── tools/

2. 使用工作区

json
// package.json
{
  "workspaces": [
    "packages/*",
    "apps/*"
  ]
}

3. 分离关注点

bash
# 将大型组件拆分为独立仓库
# 使用子模块或子树引用

部分克隆

概述

部分克隆(Partial Clone)允许只下载部分对象,按需获取其他内容。

类型

类型说明命令
无 Blob 克隆不下载文件内容--filter=blob:none
树深度限制限制历史深度--filter=tree:<depth>
Blob 大小限制不下载大文件--filter=blob:limit=<size>

无 Blob 克隆

bash
# 克隆时不下载文件内容
git clone --filter=blob:none https://github.com/user/repo.git

# Git 会在需要时自动下载文件内容
# 适合只需要历史记录的场景

Blob 大小限制

bash
# 不下载超过 1MB 的文件
git clone --filter=blob:limit=1m https://github.com/user/repo.git

# 大文件会在需要时下载

树深度限制

bash
# 限制树的深度
git clone --filter=tree:0 https://github.com/user/repo.git

# 只下载根目录的树对象

配置部分克隆

bash
# 配置默认使用部分克隆
git config --global clone.filter blob:none

# 配置按需获取的行为
git config --global fetch.showForcedUpdates false

部分克隆的限制

bash
# 某些操作需要完整对象
git blame file.txt  # 可能需要下载文件

# 解决方法:预先获取
git fetch --unshallow

性能优化技巧

1. 使用浅层克隆

bash
# 只克隆最近的提交历史
git clone --depth 1 https://github.com/user/repo.git

# 后续可以获取更多历史
git fetch --unshallow

2. 单分支克隆

bash
# 只克隆单个分支
git clone --single-branch --branch main https://github.com/user/repo.git

# 减少不必要的历史下载

3. 使用 Git LFS

bash
# 安装 Git LFS
brew install git-lfs  # macOS
apt install git-lfs   # Linux

# 初始化
git lfs install

# 跟踪大文件
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/*"

# 查看跟踪规则
git lfs track

# 克隆包含 LFS 的仓库
git lfs clone https://github.com/user/repo.git

4. 优化 Git 配置

bash
# 增加缓存大小
git config --global core.packedGitLimit 512m
git config --global core.packedGitWindowSize 512m

# 增加 delta 缓存
git config --global core.deltaBaseCacheLimit 512m

# 启用并行处理
git config --global core.preloadindex true

# 优化 status 性能
git config --global core.untrackedCache true

# 使用更快的状态检查
git config --global status.showUntrackedFiles all

5. 使用 sparse-checkout

bash
# 初始化仓库
git init repo
cd repo
git remote add origin https://github.com/user/large-repo.git

# 启用稀疏检出
git sparse-checkout init --cone

# 只检出需要的目录
git sparse-checkout set packages/core apps/web

# 拉取代码
git pull origin main

# 添加更多目录
git sparse-checkout add packages/utils

# 查看当前配置
git sparse-checkout list

# 禁用稀疏检出
git sparse-checkout disable

6. 优化 .gitignore

bash
# 忽略不需要跟踪的文件
# .gitignore

# 构建产物
dist/
build/
*.min.js

# 依赖目录
node_modules/
vendor/

# IDE 配置
.idea/
.vscode/
*.swp

# 系统文件
.DS_Store
Thumbs.db

# 日志文件
*.log
logs/

# 临时文件
tmp/
temp/
*.tmp

7. 定期清理

bash
# 清理未跟踪的文件
git clean -fd

# 清理忽略的文件
git clean -fdX

# 清理所有未跟踪文件
git clean -fdx

# 清理远程已删除的分支
git remote prune origin

# 清理 reflog
git reflog expire --expire=now --all

# 运行垃圾回收
git gc --prune=now --aggressive

8. 使用 bundle

bash
# 创建 bundle 文件
git bundle create repo.bundle --all

# 从 bundle 克隆
git clone repo.bundle repo

# 从 bundle 获取更新
git bundle create updates.bundle origin/main..HEAD
git fetch updates.bundle main:main

大文件处理

查找大文件

bash
# 查找历史中的大文件
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  awk '/^blob/ {print $3, $4}' | \
  sort -n | \
  tail -20

# 使用脚本
#!/bin/bash
# find-large-files.sh
git rev-list --objects --all | \
while read hash path; do
  if [ -n "$path" ]; then
    size=$(git cat-file -s $hash 2>/dev/null)
    echo "$size $path"
  fi
done | sort -n | tail -20

移除大文件

bash
# 使用 git-filter-repo 移除大文件
pip install git-filter-repo

# 移除特定文件
git filter-repo --path large-file.zip --invert-paths

# 移除大文件(超过 10MB)
git filter-repo --strip-blobs-bigger-than 10M

# 清理
git reflog expire --expire=now --all
git gc --prune=now --aggressive

使用 BFG Repo-Cleaner

bash
# 安装 BFG
brew install bfg  # macOS

# 删除大文件
bfg --delete-files large-file.zip

# 删除超过 50MB 的文件
bfg --strip-blobs-bigger-than 50M

# 清理
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force

性能监控

Git 内置诊断

bash
# 查看 Git 配置
git config --list --show-origin

# 检查仓库完整性
git fsck

# 统计仓库信息
git count-objects -v

# 输出示例:
# count: 1234
# size: 5678
# in-pack: 9012
# packs: 3
# size-pack: 4567
# prune-packable: 0
# garbage: 0
# size-garbage: 0

使用 git-sizer

bash
# 安装
go install github.com/github/git-sizer@latest

# 分析仓库
git-sizer --verbose

# 输出:
# Processing blobs: 12345
# Processing trees: 6789
# Processing commits: 1234
# Processing commits: done
# 
# | Name                         | Value     | Level of concern               |
# | ---------------------------- | --------- | ------------------------------ |
# | Overall repository size      |           |                                |
# | * Commits                    |           |                                |
# |   * Count                    |   1,234   |                                |
# |   * First commit             | 2020-01-01|                                |
# | * Trees                      |           |                                |
# |   * Count                    |   6,789   |                                |
# | * Blobs                      |           |                                |
# |   * Count                    |  12,345   |                                |
# |   * Total size               |  123.4 MiB| *                              |

最佳实践

1. 预防措施

bash
# 使用 pre-commit 钩子检查文件大小
#!/bin/bash
# .git/hooks/pre-commit

MAX_SIZE=10485760  # 10MB

for file in $(git diff --cached --name-only); 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 超过 10MB"
            exit 1
        fi
    fi
done

exit 0

2. 定期维护

bash
#!/bin/bash
# maintenance.sh

echo "清理远程分支..."
git remote prune origin

echo "清理 reflog..."
git reflog expire --expire=30.days.ago --all

echo "运行垃圾回收..."
git gc --prune=now

echo "检查仓库完整性..."
git fsck

echo "维护完成!"

3. CI/CD 优化

yaml
# GitHub Actions 优化示例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # 浅层克隆
      - uses: actions/checkout@v3
        with:
          fetch-depth: 1
      
      # 或稀疏检出
      - uses: actions/checkout@v3
        with:
          sparse-checkout: |
            src/
            package.json

总结

策略命令适用场景
浅层克隆git clone --depth 1只需要最新代码
单分支克隆git clone --single-branch只需要特定分支
部分克隆git clone --filter=blob:none需要历史但不需要文件
稀疏检出git sparse-checkout只需要部分目录
Git LFSgit lfs track "*.psd"大型二进制文件

优化建议

  • 使用 Git LFS 管理大文件
  • 定期运行垃圾回收
  • 使用部分克隆减少下载量
  • 配置合适的 .gitignore
  • 预防大文件进入仓库