09. Git 日志与调试:log/blame/diff 让历史无所遁形

「Git」系列已发布: 00. 改崩代码不敢回退?Git 版本控制 3 分钟救你狗命 | 01. Git 装完不会配?Windows/macOS/Linux 三端配置一次到位 | 02. Git 配置总踩坑?全局/仓库/SSH 密钥一次搞懂 | 03. add/commit/push 别再死记:Git 工作流核心概念实操 | 04. Git 分支管理:5 步彻底搞懂 branch 与 merge | 05. clone/fetch/pull/push 分不清?Git 远程协作实战 | 06. 给版本打上永久印记:Git 标签管理与发布实战 | 07. 切分支怕丢改动?Git stash 暂存恢复保护现场 | 08. rebase 不是 merge 的替代品:Git 故事线重构进阶 | 09. Git 日志与调试:log/blame/diff 让历史无所遁形(本文)

谁改的、改了啥、为什么改?log 查脉络、blame 追到人、diff 看差异。掌握这三板斧,再诡异的历史也能查个水落石出。

Git 基础操作(第 03 篇)和分支管理(第 04 篇)要掌握。找个提交比较多的仓库练效果最好。

一、高级日志查询

1.1 按内容检索


# 搜提交信息

git log --grep="修复" --oneline

# 搜代码变更——新增或删除的字符串

git log -S "DeprecatedAPI" --oneline

# 找出所有修改或删除了 "DeprecatedAPI" 的提交

# 用正则搜代码变更

git log -G "def test_\w+" --oneline

# 找新增或删除包含 "def test_xxx" 的提交

# 组合搜

git log --all --grep="修复" --oneline --author="Zhang" --since="2026-06-01"

-S和-G的区别:

参数 匹配方式 适合
-S "string" 精确字符串,统计出现次数变化 找特定函数名/变量名的增加或删除
-G "regex" 正则,匹配差异行 找符合模式的代码变更

1.2 按文件追踪


# 某个文件的所有修改历史

git log --oneline -- src/config.py

# 包含重命名的历史

git log --follow --oneline -- src/config.py

# 某个目录下所有变更

git log --oneline -- src/api/

# 统计每个文件被改了多少次

git log --pretty=format: --name-only | sort | uniq -c | sort -nr | head -20

1.2.1 按范围过滤


# 某次提交之后到现在(不包含该提交)

git log a3f2b1c..HEAD --oneline

# 两个标签之间

git log v1.0.0..v2.0.0 --oneline

# 某次提交之前到现在(包含该提交)

git log a3f2b1c~1..HEAD --oneline

# feature 有但 main 没有的提交

git log main..feature --oneline

# 两个分支各自独有的提交

git log --left-right main...feature --oneline

# < 开头 = main 独有,> 开头 = feature 独有

# 尚未合并到 main 的 feature 提交

git log --no-merges main..feature --oneline

1.3 git blame:谁写了这一行


# 文件每行是谁在哪个提交里改的

git blame src/config.py

# 简短哈希

git blame -s src/config.py

# 指定行范围

git blame -L 10,50 src/config.py

# 忽略空白变更——只看实际内容修改

git blame -w src/config.py

# 显示原始行号——适用于代码被移动过的场景

git blame -M src/config.py

# 更激进的跨文件移动检测

git blame -C -C -C src/config.py

blame输出解读:

(Zhang San 2026-07-15 10:30:00 +0800  42)     db_host = os.getenv('DB_HOST')
^-------  ^-------- ^-----------------------  ^^      ^---------------------------
提交哈希   作者      修改日期时间               行号    代码

1.4 git bisect:二分找问题

知道某个版本是好的、某个版本坏了,但不知道中间哪个提交引入的问题——git bisect就是干这个的。


# 启动

git bisect start

# 标记当前有问题

git bisect bad HEAD

# 标记一个已知好的版本

git bisect good v1.0.0

# Git 自动检出中间版本

# 测一下这个版本,然后标记

git bisect good   # 没问题

# 或

git bisect bad    # 有问题

# 反复几次,Git 定位到引入问题的第一个提交:

# a3f2b1c4 is the first bad commit

# 结束

git bisect reset

1.4.1 自动化 bisect

能用脚本判断版本好坏的话,完全可以自动化:


# 启动自动化二分查找

git bisect start HEAD v1.0.0

# 脚本返回 0 = good,1-127 = bad

git bisect run python tests/integration_test.py

# 也可以一条命令

git bisect run sh -c "make test"

常见用途:

  • 回归测试定位。
  • 编译失败溯源。
  • 性能退化定位(benchmark)。

1.5 git diff 高级用法

1.5.1 比较不同对象


# 工作目录 vs 暂存区

git diff

# 暂存区 vs 最新提交

git diff --cached

# 工作目录 vs 最新提交

git diff HEAD

# 两次提交之间

git diff a3f2b1c b2c3d4e

# 两个分支之间

git diff main..feature

# 某个文件在两次提交间的差异

git diff a3f2b1c b2c3d4e -- src/config.py

1.5.2 diff 格式


# 只列出文件名

git diff --name-only HEAD~3 HEAD

# 统计每文件增删行数

git diff --stat HEAD~3 HEAD

# 按单词显示差异——而不是按行

git diff --word-diff HEAD~1

# 忽略空白差异

git diff -w HEAD~1

# 显示函数上下文

git diff --function-context HEAD~1

1.6 git reflog:最后的救命稻草

reflog记录 HEAD 和分支指针的所有变更。误操作之后,它可能是唯一能救你的东西。


# HEAD 的移动历史

git reflog

# a3f2b1c HEAD@{0}: commit: 实现用户登录

# b2c3d4e HEAD@{1}: checkout: moving from main to feature-login

# c3d4e5f HEAD@{2}: commit: 修复导航栏bug

# 特定分支的 reflog

git reflog show main

# 找回"丢失"的提交——比如 reset --hard 之后

git reflog

# 找到 reset 之前的提交哈希

git checkout a3f2b1c       # 检出那个提交
git checkout -b recovery   # 基于它建分支

几个要点:

  • 仅限本地:不会被 push 到远端,clone 不会带过来。
  • 有过期时间:默认 90 天未活动的条目自动清理。
  • 记录了所有引用变更:commit、checkout、reset、rebase、merge都会留下记录。

1.7 调试场景

1.7.1 谁删了某段代码


# 方法一:log -S

git log -S "deleted_function_name" --oneline

# 方法二:blame 回溯

# 先定位缺失代码的行号范围

# 再查该行最后一次被改的提交

git blame -L 42,42 src/config.py

# 然后看该提交之前的版本

git show <commit>~1:src/config.py

1.7.2 找引入 bug 的提交


# 搜索新增的特定模式——比如加了 sleep

git log -G "time\.sleep" --oneline --all

# 看每次搜索结果的详细差异

git log -G "time\.sleep" -p -- src/

# 从特定日期后开始搜

git log -G "print(" --oneline --since="2026-07-01"

1.7.3 定位性能退化

git bisect start HEAD v2.0.0
git bisect run python -c "
import subprocess, sys, time
start = time.time()
subprocess.run(['python', 'benchmark.py'], timeout=30)
elapsed = time.time() - start
sys.exit(0 if elapsed < 5.0 else 1)
"

1.7.4 恢复被删除的文件


# 找最后一次包含该文件的提交

git log --all --full-history --oneline -- old_file.py

# 从那个提交里把文件捞出来

git show a3f2b1c:old_file.py > old_file.py

1.8 文章引用

📌 本文首发于微信公众号 【运维知识点滴】——专注运维实战干货,微信搜索“运维知识点滴”或扫下方二维码关注,更新不迷路。
📎 微信原文:09. Git 日志与调试:log/blame/diff 让历史无所遁形

微信公众号【运维知识点滴】 微信小程序【A5单据合并&大试卷拆分转A4打印】
运维实战干货 · 关注追更 Word 转 PDF · 试卷/发票打印神器 · 免费
运维实战干货 · 关注追更 Word 转 PDF · 试卷/发票打印神器 · 免费

💡 小程序【A5单据合并&大试卷拆分转A4打印】是干嘛的? 一个 Word 转 PDF 的打印神器,专治“文档版式和打印机对不上”:

  • A3 试卷打不出来? 学校发的电子版试卷多是 A3,家用打印机只吃 A4——上传 Word 一键转换:可缩到一页 A4(页数不变),也可对半拆成两张 A4(1:1 原大、字更清晰),两种模式自选;
  • 版式不乱:真实渲染引擎原样转换、绝不重排内容,转出的 PDF 和原 Word 排版一致,公式/表格/图片不跑版;
  • 两张 A5 单据拼一张 A4:出租车票、火车票等报销凭证拼版后,一次打印、整齐粘贴;
  • 隐私省心:无需注册,转换结果留存 5 天、最多下载 5 次即自动删除,不留任何历史记录;
  • 微信扫码即用,免费、免安装,支持 doc / docx(≤50MB)。
    原文发布于 2026-08-14

上一篇:08. rebase 不是 merge 的替代品:Git 故事线重构进阶