强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

07 - 历史查看:log、blame、bisect、reflog

第七章:历史查看

Git 不仅记录历史,还能帮你追溯每一行代码的来龙去脉。


7.1 git log 进阶

7.1.1 自定义格式

# 常用别名设置
$ git config --global alias.lg "log --oneline --graph --decorate --all --date=short --format='%C(yellow)%h%C(reset) %C(green)%ad%C(reset) %C(blue)%an%C(reset) %C(red)%d%C(reset) %s'"

# 效果
$ git lg
* abc1234 2024-01-15 John (HEAD -> main, origin/main) Latest feature
| * def5678 2024-01-14 Jane (feature) Add login
|/
* ghi9012 2024-01-13 John Initial commit

7.1.2 格式占位符完整表

占位符含义占位符含义
%H完整 commit hash%h简短 commit hash
%T完整 tree hash%t简短 tree hash
%P完整 parent hash%p简短 parent hash
%an作者名%ae作者邮箱
%cn提交者名%ce提交者邮箱
%ad作者日期%ar相对日期
%s提交标题%b提交正文
%d引用(分支/标签)%C(...)颜色

7.1.3 高级过滤

# 合并提交
$ git log --merges
$ git log --no-merges

# 按文件名过滤
$ git log --follow -p -- src/app.js

# 搜索引入特定代码的提交
$ git log -S "functionName"          # 字符串搜索
$ git log -G "regex_pattern"         # 正则搜索

# 按时间范围
$ git log --after="2024-01-01" --before="2024-06-30"
$ git log --since="2 weeks ago"

# 按作者
$ git log --author="John"
$ git log --author="john\|jane"      # 多个作者

# 按提交大小(引入/删除行数)
$ git log --stat --diff-filter=A     # 只看新增文件

# 比较两个分支
$ git log main..feature              # feature 有而 main 没有的提交
$ git log main...feature             # 两个分支各自的独有提交
$ git log --left-right main...feature # 标记来源方向

# 拓扑排序
$ git log --topo-order               # 按拓扑顺序显示

7.2 git blame — 逐行追溯

git blame 显示文件每一行的最后修改信息。

# 查看文件每行的修改者
$ git blame src/app.js
abc1234 (John   2024-01-10 09:00 +0800  1) const express = require('express');
abc1234 (John   2024-01-10 09:00 +0800  2) const app = express();
def5678 (Jane   2024-01-12 14:30 +0800  3)
def5678 (Jane   2024-01-12 14:30 +0800  4) app.get('/', (req, res) => {
ghi9012 (John   2024-01-14 10:15 +0800  5)     res.send('Hello Updated!');
abc1234 (John   2024-01-10 09:00 +0800  6) });

# 只显示特定行范围
$ git blame -L 10,20 src/app.js

# 显示完整哈希
$ git blame -l src/app.js

# 显示邮箱
$ git blame -e src/app.js

# 检测行移动(跨文件)
$ git blame -M src/app.js

# 检测行复制
$ git blame -C src/app.js

# 从特定提交开始追溯
$ git blame abc1234^ -- src/app.js

使用场景

  • 找出谁修改了某行代码
  • 追溯 bug 引入的提交
  • 理解代码变更历史

7.3 git bisect — 二分查找 bug

git bisect 通过二分搜索快速定位引入 bug 的提交。

7.3.1 基本使用

# 开始二分查找
$ git bisect start

# 标记当前版本为有问题
$ git bisect bad

# 标记某个版本为正常
$ git bisect good v1.0.0

# Git 自动切换到中间提交
# Bisecting: 50 revisions left to test after this (roughly 6 steps)
# [abc1234] Add feature X

# 运行测试,标记结果
$ npm test
# 如果测试通过
$ git bisect good
# 如果测试失败
$ git bisect bad

# Git 继续二分,直到找到第一个坏提交
# abc1234 is the first bad commit
# commit abc1234
# Author: John <[email protected]>
# Date:   Mon Jan 15 10:00:00 2024
#
#     Add feature X that breaks tests

# 结束二分查找,回到原始分支
$ git bisect reset

7.3.2 自动化二分查找

# 使用脚本自动标记(0=good, 1-124=bad, 125=skip)
$ git bisect start v1.2.0 v1.0.0
$ git bisect run npm test

# 使用自定义脚本
$ git bisect run ./test-script.sh

# 脚本示例
$ cat test-script.sh
#!/bin/bash
make
if ./run-tests; then
    exit 0  # good
else
    exit 1  # bad
fi

# 跳过无法测试的提交
$ git bisect skip

7.3.3 bisect 可视化

# 查看当前 bisect 状态
$ git bisect log

# 重放之前的 bisect 会话
$ git bisect replay bisect.log

# 可视化 bisect 范围
$ git bisect visualize --oneline

7.4 git reflog — 引用日志

git reflog 记录 HEAD 和分支引用的所有变更历史,即使提交被"删除"也能找回。

7.4.1 基本使用

# 查看 HEAD 的变更历史
$ git reflog
abc1234 HEAD@{0}: commit: Add feature
def5678 HEAD@{1}: checkout: moving from develop to main
ghi9012 HEAD@{2}: commit: Fix bug
jkl3456 HEAD@{3}: reset: moving to HEAD~1

# 查看特定分支的 reflog
$ git reflog main
$ git reflog develop

# 查看指定时间的 reflog
$ git reflog --since="2 days ago"
$ git reflog --until="1 week ago"

7.4.2 使用 reflog 恢复

# 恢复到某个历史状态
$ git reset --hard HEAD@{2}

# 查看某个 reflog 条目的内容
$ git show HEAD@{5}

# 基于 reflog 创建分支
$ git branch recovered HEAD@{3}

# 恢复误删的分支
$ git branch -D feature
# 哎呀,删错了!
$ git reflog
abc1234 HEAD@{5}: commit: Last commit on feature branch
$ git branch feature abc1234

# 恢复误删的提交
$ git reset --hard HEAD~3
# 找回!
$ git reflog
$ git reset --hard HEAD@{1}

💡 reflog 默认保留 90 天,是数据恢复的最后保障。


7.5 git diff — 差异比较

7.5.1 基本比较

# 工作区 vs 暂存区
$ git diff

# 暂存区 vs 最新提交
$ git diff --staged
$ git diff --cached

# 工作区 vs 最新提交
$ git diff HEAD

# 两个提交之间
$ git diff abc1234 def5678

# 两个分支之间
$ git diff main..feature

# 指定文件的差异
$ git diff HEAD -- src/app.js

# 统计差异(不显示具体内容)
$ git diff --stat

# 只显示文件名
$ git diff --name-only

# 只显示状态(A/M/D)
$ git diff --name-status

7.5.2 diff 选项

# 忽略空白字符变化
$ git diff -w
$ git diff --ignore-all-space

# 显示单词级差异
$ git diff --word-diff

# 并排显示差异
$ git diff --side-by-side

# 使用外部 diff 工具
$ git difftool

# 比较不同分支的特定文件
$ git diff main:src/app.js feature:src/app.js

7.6 git shortlog — 提交统计

# 按作者统计提交数
$ git shortlog -sn
    150  John Doe
     80  Jane Smith
     30  Bob Wilson

# 按作者统计(包含合并提交)
$ git shortlog -sn --all

# 按日期范围统计
$ git shortlog -sn --since="2024-01-01"

# 按文件统计
$ git shortlog -sn -- src/

7.7 git pickaxe — 内容搜索

# 搜索引入/删除特定字符串的提交
$ git log -S "functionName" --oneline

# 使用正则表达式
$ git log -G "def\s+\w+" --oneline

# 结合 -p 查看具体变更
$ git log -S "functionName" -p

# 搜索特定文件
$ git log -S "functionName" -- src/app.js

7.8 实用日志别名

# 设置常用别名
$ git config --global alias.lg "log --oneline --graph --decorate --all"
$ git config --global alias.last "log -1 HEAD --stat"
$ git config --global alias.today "log --since=midnight --oneline"
$ git config --global alias.week "log --since='1 week ago' --oneline"
$ git config --global alias.mine "log --author=$(git config user.email) --oneline"
$ git config --global alias.changes "diff --name-status"

业务场景

场景推荐命令
追溯某行代码作者git blame -L <start>,<end> <file>
定位 bug 引入提交git bisect start + git bisect run
恢复误删分支/提交git reflog + git reset/cherry-pick
查找引入特定代码的提交git log -S "keyword"
审查近期变更git log --since="1 week" --stat
比较分支差异git diff main..feature --stat
团队贡献统计git shortlog -sn --all

扩展阅读


🔗 上一章06 - 暂存 | 下一章08 - 撤销操作