Appearance
GC 与清理
概述
Git 垃圾回收(Garbage Collection,GC)是清理无用对象、优化仓库性能的重要机制。定期运行 GC 可以保持仓库健康,减少磁盘占用。
Git 垃圾回收
什么是 Git GC
Git GC 会:
- 清理不可达的对象
- 压缩松散对象为包文件
- 优化仓库存储结构
- 提高访问效率
Git 对象存储
.git/
├── objects/
│ ├── pack/ # 包文件(压缩存储)
│ │ ├── pack-abc123.idx
│ │ └── pack-abc123.pack
│ ├── info/ # 对象信息
│ └── [0-9a-f]{2}/ # 松散对象目录
│ └── [0-9a-f]{38}
└── ...对象类型
| 类型 | 说明 |
|---|---|
| blob | 文件内容 |
| tree | 目录结构 |
| commit | 提交信息 |
| tag | 标签对象 |
git gc 命令
基本用法
bash
# 运行垃圾回收
git gc
# 自动运行(Git 会在需要时自动执行)
git gc --auto
# 激进模式(更彻底的优化)
git gc --aggressive
# 立即清理(不等待过期)
git gc --prune=now
# 组合使用
git gc --aggressive --prune=now参数说明
| 参数 | 说明 |
|---|---|
--auto | 只在需要时运行 |
--aggressive | 激进优化,耗时更长 |
--prune=<date> | 清理指定日期前的对象 |
--prune=now | 立即清理所有不可达对象 |
--quiet | 静默模式 |
--force | 强制运行 |
GC 工作流程
bash
# 1. 检查是否需要 GC
git gc --auto
# 2. 打包松散对象
git repack
# 3. 清理不可达对象
git prune
# 4. 清理 reflog
git reflog expire
# 5. 更新索引
git update-server-info查看仓库状态
bash
# 查看对象统计
git count-objects -v
# 输出示例:
# count: 123 # 松散对象数量
# size: 456 # 松散对象大小(KB)
# in-pack: 7890 # 包文件中的对象数量
# packs: 3 # 包文件数量
# size-pack: 1234 # 包文件大小(KB)
# prune-packable: 0
# garbage: 0 # 损坏对象数量
# 查看磁盘使用
du -sh .git
# 查看包文件
ls -lh .git/objects/pack/清理无用对象
git prune
bash
# 清理不可达对象
git prune
# 清理特定日期前的对象
git prune --expire=now
# 清理 30 天前的对象
git prune --expire=30.days.ago
# 查看将被清理的对象
git prune --dry-run清理 reflog
bash
# 查看 reflog
git reflog
# 清理过期的 reflog
git reflog expire --expire=now --all
# 清理 30 天前的 reflog
git reflog expire --expire=30.days.ago --all
# 清理特定分支的 reflog
git reflog expire --expire=now refs/heads/main清理远程分支
bash
# 清理远程已删除的分支引用
git remote prune origin
# 查看将被清理的分支
git remote prune origin --dry-run
# 清理所有远程
git remote update --prune清理工作目录
bash
# 查看将被清理的文件
git clean -n
# 清理未跟踪的文件
git clean -f
# 清理未跟踪的文件和目录
git clean -fd
# 清理忽略的文件
git clean -fX
# 清理所有未跟踪文件(包括忽略的)
git clean -fx
# 清理所有(包括子模块)
git clean -fdx仓库维护
定期维护脚本
bash
#!/bin/bash
# git-maintenance.sh
echo "=== Git 仓库维护 ==="
echo "1. 清理远程分支引用..."
git remote prune origin
echo "2. 清理 reflog..."
git reflog expire --expire=30.days.ago --all
echo "3. 运行垃圾回收..."
git gc --prune=now
echo "4. 检查仓库完整性..."
git fsck
echo "5. 统计仓库信息..."
git count-objects -v
echo "=== 维护完成 ==="Git 维护命令(Git 2.30+)
bash
# 启动定期维护
git maintenance start
# 停止定期维护
git maintenance stop
# 手动运行维护任务
git maintenance run
# 运行特定任务
git maintenance run --task=gc
git maintenance run --task=commit-graph
git maintenance run --task=prefetch
# 查看维护状态
git maintenance register配置自动维护
bash
# 启用自动 GC
git config gc.auto 256
# 启用自动打包
git config repack.usedeltabaseoffset true
# 配置 GC 频率
git config gc.autoPackLimit 50
# 配置 reflog 过期时间
git config gc.reflogExpire 90.days.ago
git config gc.reflogExpireUnreachable 30.days.ago
# 配置 prune 过期时间
git config gc.pruneExpire 2.weeks.ago优化仓库性能
重新打包
bash
# 重新打包所有对象
git repack -a -d
# 使用增量打包
git repack -d
# 完全重新打包
git repack -a -d -f --depth=250 --window=250优化提交图
bash
# 生成提交图文件
git commit-graph write
# 生成完整提交图
git commit-graph write --reachable
# 验证提交图
git commit-graph verify
# 清理提交图
rm .git/objects/info/commit-graph优化索引
bash
# 重新生成索引
git read-tree HEAD
# 更新索引
git update-index --refresh
# 优化索引
git update-index --index-version 4使用 fsck 检查
bash
# 检查仓库完整性
git fsck
# 检查所有对象
git fsck --full
# 检查并显示进度
git fsck --progress
# 检查连通性
git fsck --connectivity-only
# 检查并修复(谨慎使用)
git fsck --lost-found清理大仓库
查找大对象
bash
# 查找最大的对象
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
sed -n 's/^blob //p' | \
sort -rnk2 | \
head -20
# 查找大文件
git ls-files | xargs -I {} du -h {} | sort -rh | head -20移除大文件
bash
# 使用 filter-repo 移除
git filter-repo --path large-file.zip --invert-paths
# 使用 BFG 移除
bfg --delete-files large-file.zip
# 清理
git reflog expire --expire=now --all
git gc --prune=now --aggressive清理历史中的敏感信息
bash
# 移除敏感文件
git filter-repo --path config/secrets.yml --invert-paths
# 替换敏感信息
git filter-repo --replace-text <(echo 'password==>REDACTED')
# 清理
git gc --prune=now --aggressive恢复丢失的提交
使用 reflog 恢复
bash
# 查看 reflog
git reflog
# 恢复到特定提交
git reset --hard HEAD@{5}
# 创建分支保存丢失的提交
git branch recovered-branch HEAD@{5}使用 fsck 恢复
bash
# 查找悬空提交
git fsck --lost-found
# 查看悬空提交
git show <dangling-commit-hash>
# 恢复悬空提交
git cherry-pick <dangling-commit-hash>
# 或创建分支
git branch recovered <dangling-commit-hash>从 .git/objects 恢复
bash
# 查找悬空对象
find .git/objects -type f | head
# 查看对象内容
git cat-file -p <object-hash>
# 恢复对象
git hash-object -w <file>GC 配置详解
常用配置
bash
# 自动 GC 阈值(松散对象数量)
git config gc.auto 256
# 自动打包阈值
git config gc.autoPackLimit 50
# reflog 过期时间
git config gc.reflogExpire 90.days.ago
# 不可达 reflog 过期时间
git config gc.reflogExpireUnreachable 30.days.ago
# prune 过期时间
git config gc.pruneExpire 2.weeks.ago
# 打包窗口大小
git config pack.windowMemory 256m
# 打包深度
git config pack.depth 50
# 并行压缩线程
git config pack.threads 4性能相关配置
bash
# 增加缓存
git config core.packedGitLimit 512m
git config core.packedGitWindowSize 512m
git config core.deltaBaseCacheLimit 512m
# 启用预加载
git config core.preloadindex true
# 启用文件系统缓存
git config core.fscache true
# 优化 status
git config core.untrackedCache true最佳实践
1. 定期维护
bash
# 每周运行一次
git gc
# 每月运行一次激进 GC
git gc --aggressive --prune=now2. 大型仓库维护
bash
#!/bin/bash
# large-repo-maintenance.sh
echo "开始维护大型仓库..."
# 1. 清理远程引用
git remote prune origin
# 2. 清理 reflog
git reflog expire --expire=7.days.ago --all
# 3. 重新打包
git repack -a -d -f --depth=250 --window=250
# 4. 生成提交图
git commit-graph write --reachable
# 5. 清理
git prune --expire=now
# 6. 检查
git fsck
echo "维护完成!"
git count-objects -v3. CI/CD 中的清理
yaml
# GitHub Actions
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 运行 GC
run: |
git gc --prune=now
git count-objects -v4. 预防措施
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,请使用 Git LFS"
exit 1
fi
fi
done常见问题
Q: GC 后仓库大小没变?
bash
# 可能原因:
# 1. reflog 保留了引用
git reflog expire --expire=now --all
# 2. 远程分支还有引用
git remote prune origin
# 3. 需要激进 GC
git gc --aggressive --prune=nowQ: GC 运行很慢?
bash
# 减少打包窗口
git config pack.window 10
# 减少深度
git config pack.depth 20
# 增加线程
git config pack.threads 8
# 分步运行
git repack -d
git pruneQ: 如何查看 GC 进度?
bash
# 使用 --verbose
git gc --verbose
# 或查看日志
GIT_TRACE=1 git gc总结
| 操作 | 命令 | 说明 |
|---|---|---|
| 垃圾回收 | git gc | 运行 GC |
| 激进 GC | git gc --aggressive --prune=now | 彻底清理 |
| 清理对象 | git prune | 清理不可达对象 |
| 清理 reflog | git reflog expire --expire=now --all | 清理引用日志 |
| 清理远程 | git remote prune origin | 清理远程分支引用 |
| 检查仓库 | git fsck | 检查完整性 |
| 统计对象 | git count-objects -v | 查看对象统计 |
维护建议:
- 定期运行
git gc - 大型仓库使用
git gc --aggressive - 清理 reflog 释放空间
- 使用
git fsck检查完整性 - 配置合适的 GC 参数
