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

