Week 07: 调试、恢复与仓库维护
掌握 Bisect 二分查找, Reflog 恢复, fsck 验证, GC 机制与仓库优化技术.
1. Bisect (二分查找)
1.1 什么是 Bisect
Bisect 通过二分搜索在提交历史中快速定位引入 Bug 的提交.
原理: 每次测试将搜索范围减半, 对于 1000 个提交, 最多只需约 10 次测试.
1.2 手动 Bisect
# 1. 开始 bisect
git bisect start
# 2. 标记当前版本 (有 Bug)
git bisect bad
# 3. 标记已知正常的版本
git bisect good v1.0.0
# Git 会检出中间提交
# 4. 测试并标记
git bisect good # 如果正常
git bisect bad # 如果有问题
# 5. 重复直到找到
# abc1234 is the first bad commit
# 6. 结束 bisect
git bisect reset1.3 自动 Bisect
提供一个测试脚本, Git 自动运行:
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# 自动测试
git bisect run ./test.sh
# 脚本返回 0 = good, 非 0 = bad测试脚本示例
#!/bin/bash
# test.sh
# 编译项目
make || exit 125 # 125 = 跳过此提交
# 运行测试
./run_tests || exit 1 # 1 = bad
exit 0 # good1.4 跳过提交
# 无法测试当前提交时
git bisect skip
# 跳过某个范围
git bisect skip abc1234..def56781.5 查看 Bisect 日志
# 查看 bisect 历史
git bisect log
# 保存日志
git bisect log > bisect.log
# 从日志恢复
git bisect replay bisect.log2. Reflog (引用日志)
2.1 什么是 Reflog
Reflog 记录本地仓库中所有引用的变更历史, 包括:
- 分支移动
- HEAD 变化
- reset、rebase、checkout 等操作
git reflog
# abc1234 HEAD@{0}: commit: Latest commit
# def5678 HEAD@{1}: checkout: moving from feature to main
# ghi9012 HEAD@{2}: commit: WIP
# jkl3456 HEAD@{3}: reset: moving to HEAD~22.2 Reflog 是恢复的利器
几乎任何"丢失"的提交都可以通过 Reflog 找回:
找回被 reset 的提交
# 不小心 reset 丢失了提交
git reset --hard HEAD~5
# 查看 reflog
git reflog
# 恢复到 reset 之前
git reset --hard HEAD@{1}找回删除的分支
# 删除了分支
git branch -D feature
# 查看 reflog
git reflog
# 重新创建分支
git branch feature abc1234找回 rebase 之前的状态
# rebase 后发现问题
git reflog
# 恢复到 rebase 之前
git reset --hard ORIG_HEAD
# 或
git reset --hard HEAD@{2}2.3 Reflog 配置与清理
# 查看 reflog 过期时间
git config gc.reflogExpire # 默认 90 天
git config gc.reflogExpireUnreachable # 默认 30 天
# 手动清理
git reflog expire --expire=now --all
git gc2.4 Reflog 的局限
- 只存在于本地: 不会 push 到远程
- 有过期时间: 默认 90 天后被清理
- 依赖 gc: gc 运行后, 过期的 reflog 条目会被删除
3. fsck (文件系统检查)
3.1 什么是 fsck
fsck (File System Check) 验证 Git 对象的完整性:
# 完整检查
git fsck
# 静默模式 (只报告错误)
git fsck --no-full
# 检查不可达对象
git fsck --unreachable
# 检查悬空对象
git fsck --dangling3.2 常见问题
悬空对象 (Dangling Objects)
不被任何引用指向的对象:
git fsck
# dangling commit abc1234
# dangling blob def5678恢复悬空提交
# 查看悬空提交内容
git show abc1234
# 创建分支指向它
git branch recovered abc1234损坏的对象 (Corrupt Objects)
git fsck
# error: sha1 mismatch abc1234
# missing blob def5678恢复选项:
- 从备份恢复
.git/objects/ - 从远程 fetch 丢失的对象
- 使用 reflog 和其他引用重建
4. 垃圾回收 (GC)
4.1 GC 的作用
- 打包松散对象为 Packfile
- 删除不可达对象
- 压缩仓库
- 清理过期的 reflog
4.2 GC 命令
# 运行 gc
git gc
# 激进模式 (更彻底的压缩)
git gc --aggressive
# 立即删除不可达对象
git gc --prune=now4.3 自动 GC
Git 在某些操作后自动运行 gc:
# 配置自动 gc 阈值
git config gc.auto 256 # 松散对象数量
git config gc.autoPackLimit 50 # pack 文件数量
# 禁用自动 gc
git config gc.auto 04.4 Prune (修剪)
删除不可达对象:
# 检查会删除什么
git prune -n
# 执行删除
git prune
# 删除所有过期对象
git prune --expire=now4.5 Repack (重新打包)
优化 pack 文件:
# 基本重新打包
git repack
# 生成单个 pack
git repack -a -d
# 激进模式
git repack -A -d --depth=250 --window=2505. 仓库瘦身
5.1 分析仓库大小
# 查看仓库大小
du -sh .git
# 查看 pack 文件大小
du -sh .git/objects/pack
# 查看最大的对象
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
sort -k3 -n -r | head -205.2 清理大文件
# 使用 git-filter-repo 删除大文件
git filter-repo --strip-blobs-bigger-than 10M
# 使用 BFG
bfg --strip-blobs-bigger-than 10M
# 运行 gc
git gc --prune=now --aggressive
git reflog expire --expire=now --all
# 强制推送
git push origin --force --all
git push origin --force --tags5.3 shallow clone (浅克隆)
# 只克隆最近 N 个提交
git clone --depth=1 url
git clone --depth=10 url
# 只克隆特定分支
git clone --single-branch --branch main url
# 后续获取更多历史
git fetch --unshallow
git fetch --depth=1005.4 partial clone (部分克隆)
Git 2.22+ 支持:
# 只克隆 commit 和 tree, 按需获取 blob
git clone --filter=blob:none url
# 限制 blob 大小
git clone --filter=blob:limit=1m url
# 稀疏检出
git clone --filter=blob:none --sparse url
cd repo
git sparse-checkout set src/6. 超大规模仓库优化: Monorepo & Scalar
对于 GB 级或百万文件的超大仓库 (如 Windows, Office), 标准 Git 会变慢.
6.1 核心加速技术
- Commit Graph: 缓存提交图遍历结果, 加速 log/merge.
- Multi-Pack Index (MIDX): 减少对象查找的 pack 文件扫描数.
- Filesystem Monitor (FSMonitor): 避免扫描工作区 100k+ 文件, 仅处理 OS 变更通知.
# 写入 commit-graph
git commit-graph write --reachable --changed-paths
# 写入 MIDX
git multi-pack-index write --bitmap6.2 Scalar
Microsoft 开源 (现合并入核心) 的大仓库管理工具.
# 注册大仓库 (自动配置所有优化参数)
scalar register /path/to/repo
# 克隆大仓库
scalar clone https://github.com/microsoft/gitScalar 会自动配置:
- Partial Clone (不下载 Blob)
- Sparse Checkout (只检出根目录)
- FSMonitor
- 后台维护任务
6.3 Git Maintenance
Git 的后台任务调度器 (cron/launchd):
# 开启后台维护
git maintenance start它会定期运行:
gccommit-graphprefetch(预取远程提交)
7. 备份与迁移
6.1 Clone 备份
# 裸仓库备份
git clone --mirror origin-url backup.git
# 更新备份
cd backup.git
git fetch origin +refs/*:refs/*6.2 Bundle 备份
# 创建完整备份
git bundle create backup.bundle --all
# 增量备份
git bundle create incremental.bundle main ^v1.0.0
# 验证
git bundle verify backup.bundle6.3 仓库迁移
# 1. 裸克隆原仓库
git clone --bare old-url
# 2. 推送到新地址
cd repo.git
git push --mirror new-url
# 3. 或直接更新 remote
git remote set-url origin new-url
git push --mirror8. 练习
7.1 Bisect 练习
- 创建一个包含 Bug 的提交历史.
- 使用 bisect 找到引入 Bug 的提交.
- 尝试自动化 bisect.
7.2 Reflog 恢复
- 创建几个提交.
- 使用
reset --hard丢失提交. - 使用 reflog 恢复.
7.3 仓库瘦身
- 分析仓库大小.
- 找出最大的对象.
- 清理并压缩仓库.
9. 思考题
- Bisect 的时间复杂度是多少?
- Reflog 能跨仓库使用吗?
- 为什么
gc --prune=now通常不建议使用? - shallow clone 有什么缺点?
git clone --mirror和git clone --bare有什么区别?
10. 本周小结
- Bisect: 二分查找定位 Bug.
- Reflog: 本地引用变更历史, 恢复利器.
- fsck: 验证仓库完整性.
- GC/Prune/Repack: 仓库清理和优化.
- 瘦身: 清理大文件, shallow/partial clone.
- 备份: mirror, bundle, 增量备份.
Reflog 是本地仓库的"后悔药". 只要 Reflog 还在, 几乎没有不可挽回的操作.