Skip to content

[Git 黑魔法:高级技巧与故障恢复]

100 天认知提升计划 | Day 5


目录


第一部分:Git 高级命令原理

Git 对象模型回顾

概念说明

理解 Git 高级命令的基础是理解 Git 的对象存储模型。Git 是一个内容寻址文件系统,所有数据都以对象形式存储。

Git 对象类型

对象类型说明存储内容
blob文件内容文件数据,不含文件名
tree目录文件名到 blob/tree 的映射
commit提交tree 指针、父提交、作者信息
tag标签特定 commit 的引用
Git 对象关系图:

commit (abc1234)
  ├─ tree: 项目根目录快照
  ├─ parent: 父提交
  ├─ author: 作者信息
  └─ committer: 提交者信息

tree (def5678)
  ├─ blob: src/main.py (文件内容)
  ├─ tree: src/utils/ (子目录)
  └─ blob: README.md

查看 Git 对象

bash
# 查看对象类型和内容
git cat-file -t <hash>      # 查看对象类型
git cat-file -p <hash>      # 查看对象内容

# 查看提交的树
git ls-tree <commit-hash>

# 查看所有对象
git rev-list --all --objects

引用与 reflog 机制

概念说明

引用(refs) 是指向 Git 对象的指针,如分支、标签等。reflog(引用日志) 是 Git 的安全网,记录所有引用的移动历史。

引用类型

refs/
├── heads/          # 本地分支
│   ├── main
│   └── feature-xxx
├── tags/           # 标签
│   ├── v1.0
│   └── v2.0
├── remotes/        # 远程分支
│   └── origin/main
└── stash           # stash 记录

reflog 工作原理

bash
# reflog 记录每次 HEAD 的移动
git reflog

# 输出示例:
# abc1234 HEAD@{0}: commit: 修复登录bug
# def5678 HEAD@{1}: commit: 添加新功能
# 9012345 HEAD@{2}: reset: moving to main
# ...

关键理解:reflog 记录的是"引用的移动"而非"提交的创建"。即使删除了分支或 reset,reflog 仍然保留历史。

reflog 保留期限

引用类型保留期限
HEAD90 天
分支90 天
其他引用30 天

核心命令深度解析

1. git bisect - 二分查找

原理说明

bisect 使用二分查找算法定位引入 bug 的提交:

有效区间                二分位置
├──────●──────●──────●──────●────●────>
good   bad

时间复杂度:O(log n)
100个提交 → 最多7次查找
1000个提交 → 最多10次查找

bisect 工作流程

bash
# 1. 启动 bisect
git bisect start

# 2. 标记当前状态(坏的)
git bisect bad HEAD

# 3. 标记已知好的版本
git bisect good v1.0

# Git 自动切换到中间提交

# 4. 测试当前版本
./run_tests.sh          # 手动测试
git bisect good         # 或 git bisect bad

# 5. 重复步骤4直到定位
# Bisect: abc1234 is the first bad commit

# 6. 结束 bisect
git bisect reset

自动化 bisect

bash
# 创建测试脚本
cat > test.sh << 'EOF'
#!/bin/bash
make                    # 编译
./run_tests             # 运行测试
# 返回0表示good,非0表示bad
EOF

chmod +x test.sh

# 运行自动化 bisect
git bisect start
git bisect bad HEAD
git bisect good v1.0
git bisect run ./test.sh

2. git reflog - 引用日志

核心用途

场景命令
查看操作历史git reflog
恢复误删的分支git branch recovered HEAD@{n}
撤销 resetgit reset --hard HEAD@{1}
撤销 rebasegit reset --hard ORIG_HEAD

reflog 实战场景

bash
# 场景1: 误删分支后恢复
git branch -D feature-xxx    # 删除分支
git reflog                    # 找到分支最后的commit
git checkout -b feature-xxx HEAD@{5}  # 恢复分支

# 场景2: reset后悔了
git reset --hard HEAD~3      # 回退3个commit
# ... 发现回退错了
git reset --hard HEAD@{1}    # 回到reset前的状态

# 场景3: rebase混乱后
git rebase -i HEAD~10        # 交互式rebase
# ... 搞得一团糟
git reflog                    # 找到rebase前
git reset --hard HEAD@{n}    # ORIG_HEAD 也可以

# 场景4: 查看分支在某时刻的状态
git reflog show feature-xxx
git checkout feature-xxx@{yesterday}

reflog 高级用法

bash
# 按时间查看
git reflog --date=local

# 查看特定分支的reflog
git reflog show main

# 限制输出行数
git reflog -10

# 按日期过滤
git reflog --since="2 days ago"

3. git worktree - 多工作树

原理说明

传统 Git 仓库每个工作目录只能检出一个分支。worktree 允许同一仓库拥有多个工作目录,各自检出不同分支。

worktree vs stash

特性worktreestash
并行工作✅ 同时修改多个分支❌ 必须切换分支
隔离性✅ 完全独立目录❌ 共享工作区
持久化✅ 一直存在✅ 持久化
适用场景长期并行开发临时切换

worktree 基本操作

bash
# 添加新的 worktree
git worktree add ../feature-branch feature-branch
#                   目标位置      分支名

# 列出所有 worktree
git worktree list

# 删除 worktree
git worktree remove ../feature-branch

# 移动 worktree
git worktree move ../old-path ../new-path

# prune 清理已删除的 worktree 记录
git worktree prune

worktree 实战场景

bash
# 场景1: 紧急修复 bug
# 当前正在开发 feature,突然需要修复 hotfix
git worktree add ../hotfix main
cd ../hotfix
# ... 修复 bug,提交
cd ../original-project  # 回到原来的工作

# 场景2: 代码审查
git worktree add ../review pr-123
cd ../review
# ... 审查代码

# 场景3: 并行测试多个版本
git worktree add ../test-v1.0 v1.0
git worktree add ../test-v2.0 v2.0
# ... 在不同目录测试不同版本

# 场景4: CI/CD 部署
git worktree add /tmp/build-deploy main
cd /tmp/build-deploy
./deploy.sh
git worktree remove /tmp/build-deploy

4. git rerere - 重用冲突解决

原理说明

rerere (Reuse Recorded Resolution) 记录冲突解决方案,当相同冲突再次出现时自动应用。

rerere 工作流程

第一次遇到冲突:
┌─────────────────────────────────────┐
│  合并冲突                            │
│       ↓                              │
│  手动解决                            │
│       ↓                              │
│  git rerere记录解决方案              │
│       ↓                              │
│  git commit                          │
└─────────────────────────────────────┘

再次遇到相同冲突:
┌─────────────────────────────────────┐
│  合并冲突                            │
│       ↓                              │
│  git rerere自动应用之前记录          │
│       ↓                              │
│  检查自动解决是否正确                │
│       ↓                              │
│  git commit                          │
└─────────────────────────────────────┘

启用和配置 rerere

bash
# 启用 rerere
git config --global rerere.enabled true

# 查看 rerere 状态
git rerere status

# 查看记录的解决方案
git rerere diff

# 清除旧记录
git rerere forget <path>

rerere 实战价值

bash
# 场景: 长期功能分支与主分支频繁合并
feature-xxx ─┐
             ├─→ conflict1 ─┐
main       ─┘

feature-xxx ─┐
             ├─→ conflict1 (rerere自动解决)
main       ─┘

5. git range-diff - 版本范围对比

原理说明

range-diff 用于对比两个版本范围(如两个分支的提交系列),显示每个提交的变化。

使用场景

bash
# 对比两个分支的差异
git range-diff main..feature-branch

# 对比 rebase 前后的变化
git rebase -i HEAD~5
git range-diff HEAD@{1} HEAD

# 对比 PR 的更新
git range-diff origin/main...pr-v1 origin/main...pr-v2

range-diff 输出解读

1:  acaa63b = 1:  ef56789 feat: add login
2:  bcb234c ! 2:  gh67890 fix: handle error
    @@ Context: changed due to upstream
3:  cdc345d < 3:  (removed)
4:  -        > 4:  ij90123 feat: add logout

符号含义:

  • = 提交相同
  • ! 内容变化
  • < 被删除
  • > 新增

第二部分:实战场景与技巧

故障恢复场景

场景1: 误删重要分支

bash
# 问题:误删分支
git branch -D important-feature
# Branch deleted.

# 解决:使用 reflog 恢复
git reflog | grep "important-feature"
# abc1234 HEAD@{5}: checkout: moving from main to important-feature

git checkout -b important-feature abc1234
# Switched to a new branch 'important-feature'

场景2: 错误的 rebase

bash
# 问题:rebase 导致提交混乱
git rebase -i HEAD~10
# ... 手动编辑后保存

# 发现出错了,想回退
# 方法1: 使用 reflog
git reflog
git reset --hard HEAD@{n}

# 方法2: 使用 ORIG_HEAD
git reset --hard ORIG_HEAD

# 方法3: 创建备份分支后再操作
git branch backup-before-rebase
git rebase -i HEAD~10
# 如果出错
git reset --hard backup-before-rebase

场景3: hard reset 后后悔

bash
# 问题:hard reset 丢失了提交
git reset --hard HEAD~3
# ... 发现重要的提交丢失了

# 解决:reflog 恢复
git reflog
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: important change
# 9012345 HEAD@{2}: commit: another important change

# 恢复到 reset 前
git reset --hard HEAD@{1}

# 或创建新分支指向丢失的提交
git branch recovered def5678

场景4: 合并冲突后的后悔

bash
# 问题:合并产生大量冲突,想放弃
git merge feature-branch
# Auto-merging file.txt
# CONFLICT (content): Merge conflict in file.txt

# 解决:放弃合并
git merge --abort

# 如果已经 commit 了
git reset --hard ORIG_HEAD

场景5: 误 git commit --amend

bash
# 问题:amend 覆盖了原来的提交
git commit -m "WIP"
git add forgotten-file.txt
git commit --amend
# ... 发现需要原来的提交

# 解决:reflog 中查找
git reflog
# abc1234 HEAD@{0}: commit (amend): ...
# def5678 HEAD@{1}: commit: WIP

git show def5678  # 查看原提交内容

高效工作流

使用 fixup 和 autosquash 整理历史

传统方式 vs autosquash

bash
# 传统方式:手动编辑 rebase todo
git rebase -i HEAD~5
# 手动将 fixup 提交移动到对应提交下
# 手动标记为 fixup

# autosquash:自动整理
git commit -m "feat: add user login"
# ... 发现有个bug
git commit -m "fix: typo" --fixup HEAD~1
# ... 又发现一个问题
git commit -m "fix: validation" --fixup HEAD~2

# 自动 rebase 整理
git rebase -i --autosquash HEAD~3
# Git 自动将 fixup 提交放到对应位置

fixup 类型对比

类型说明使用场景
--fixup合并到目标提交,丢弃 fixup 的提交信息小修复
--squash合并到目标提交,保留提交信息重要修改
bash
# fixup 示例
git commit -m "feat: add feature"
# ... 发现小bug
git add .
git commit --fixup HEAD

# squash 示例
git commit -m "feat: add feature"
# ... 发现重要问题需要说明
git add .
git commit --squash HEAD
# 编辑器打开,合并提交信息

交互式 rebase 高级技巧

rebase 操作说明

命令说明
pick使用该提交
reword修改提交信息
edit停在该提交,允许修改
squash合并到前一个提交
fixup合并到前一个提交(丢弃信息)
drop删除该提交
exec在该提交后执行命令

实用 rebase 技巧

bash
# 1. 压缩多个小提交
git rebase -i HEAD~5
# 将多个 pick 改为 squash/fixup

# 2. 重新排序提交
git rebase -i HEAD~10
# 调整提交顺序

# 3. 修改特定提交
git rebase -i HEAD~5
# 将目标改为 edit
# ... 修改文件
git add .
git rebase --continue

# 4. 在 rebase 过程中执行命令
git rebase -i HEAD~10
pick abc1234 commit 1
exec make test          # 在该提交后运行测试
pick def5678 commit 2
exec ./deploy.sh        # 在该提交后部署

团队协作技巧

分支策略最佳实践

bash
# 1. 功能分支命名规范
feature/ticket-123-user-login
bugfix/ticket-454-memory-leak
hotfix/critical-security-fix

# 2. 提交信息规范
feat: add user authentication
fix: resolve memory leak in parser
docs: update API documentation
refactor: simplify error handling
test: add unit tests for auth module

# 3. 使用 worktree 进行代码审查
git worktree add ../review-pr-123 pr-123
cd ../review-pr-123
# ... 审查完成后删除
git worktree remove ../review-pr-123

安全的 rebase 操作

bash
# 1. rebase 前创建备份
git branch backup-$(date +%Y%m%d)
git rebase main

# 2. 使用 --onto 精确 rebase
git rebase --onto new-base old-base branch
# 将 branch 从 old-base 重新应用到 new-base

# 3. 只 rebase 本地提交
git rebase @{u}  # 只 rebase 本地未推送的提交

清理历史

bash
# 1. 清理大文件
git rev-list --objects --all |
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
  awk '/^blob/ {print substr($0,6)}' |
  sort -n -k2 |
  tail -10

# 使用 BFG Repo-Cleaner
bfg --delete-files unwanted-file.txt
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# 2. 清理无用分支
git branch -merged | grep -v "\*" | xargs git branch -d

# 3. 清理无用 reflog
git reflog expire --expire=now --all
git gc --prune=now

第三部分:实践与思考

实践记录

环境准备

  • [ ] 创建测试仓库
  • [ ] 准备测试数据(多个提交、分支)
  • [ ] 安装必要工具

基础练习

  • [ ] 使用 git bisect 定位引入 bug 的提交
  • [ ] 使用 git reflog 恢复误删的分支
  • [ ] 创建并管理多个 git worktree
  • [ ] 使用 git rerere 自动解决重复冲突
  • [ ] 使用 git range-diff 对比两个分支

进阶场景

  • [ ] 模拟 rebase 灾难并恢复
  • [ ] 使用 --fixup--autosquash 整理提交历史
  • [ ] 清理仓库中的大文件
  • [ ] 建立团队的 Git 规范文档

疑问与思考

已解答

  1. reflog 和 git log 有什么区别?

    • git log 显示提交历史(不可变)
    • git reflog 显示引用移动历史(本地操作记录)
  2. 什么时候用 worktree vs stash?

    • 长期并行工作用 worktree
    • 临时切换上下文用 stash
  3. rerere 记录存在哪里?

    • .git/rr-cache/ 目录
    • 可以纳入版本控制(团队共享冲突解决方案)

待探索

  1. 如何让 rerere 记录在团队中共享?

    • 考虑将 .git/rr-cache/ 加入版本控制
    • 需要评估安全性和适用性
  2. 大型仓库(如 Linux 内核)如何高效使用 bisect?

    • 研究并行 bisect
    • 考虑使用 CI 自动化测试脚本
  3. 如何防止团队成员误用 rebase?

    • 使用 pre-receive hook
    • 设置受保护分支规则
  4. git worktree 有什么限制?

    • 不能在同一个仓库中创建嵌套 worktree
    • 需要管理多个工作目录的存储

更新日期:2026-02-19

参考资料

用 ❤️ 和 AI 辅助学习