Git 历史与差异 — log / diff / blame / reflog
版本控制系统最大的价值就是"能回到过去"。这一章讲怎么查看历史:用 git log 看提交记录、用 git diff 看改了什么、用 git blame 追溯每行代码的来历、用 git reflog 找回"误删"的提交。这些命令是你日常调试、排查问题的利器。
1. git log — 查看提交历史
git log 是查看历史的主入口,但它输出可能很长很乱。掌握几个常用选项,信息密度立刻上来:
# git log:查看提交历史
git log
# 完整输出:哈希、作者、日期、完整 message
# commit a1b2c3d4e5f6... (HEAD -> main, origin/main)
# Author: 你的名字 <you@example.com>
# Date: Mon Aug 5 10:00:00 2025
#
# feat: 添加登录功能
# 一行一条提交,简洁(最常用)
git log --oneline
# a1b2c3d (HEAD -> main) feat: 添加登录功能
# e4f5g6h fix: 修购物车 bug
# i7j8k9l docs: 更新 README
# 带分支图,看清分叉与合并
git log --oneline --graph --all
# * a1b2c3d (HEAD -> main) feat: 添加登录
# | * e4f5g6h (feature) fix: wip
# |/
# * i7j8k9l docs: 更新 README
# 看最近 N 条
git log -5 --oneline
# 看某个文件的提交历史
git log -- README.md
git log --oneline -- src/
# 看某段时间内的提交
git log --since="2 weeks ago"
git log --since="2025-01-01" --until="2025-06-30"
# 看某个作者的提交
git log --author="张三"2. 自定义 log 格式
想让 log 输出更符合自己的习惯?用 --pretty=format 自定义每一行显示什么。配个 lg 别名,以后 git lg 就是漂亮的分支图:
# 自定义输出格式(--pretty=format)
git log --pretty=format:"%h - %an, %ar : %s"
# a1b2c3d - 张三, 2 hours ago : feat: 添加登录
# e4f5g6h - 李四, 1 day ago : fix: 购物车
# 常用占位符:
# %h 简短哈希
# %H 完整哈希
# %an 作者名字
# %ae 作者邮箱
# %ad 作者日期
# %ar 相对日期(2 hours ago)
# %s 提交信息标题
# %b 提交信息正文
#
# 终极别名(强烈推荐):
git config --global alias.lg "log --oneline --graph --all --decorate"
# 之后 git lg 就是漂亮的分支图
# 显示每次提交改了哪些文件(+/- 统计)
git log --stat
git log --shortstat
# 显示每次提交的具体 diff(完整改动)
git log -p
git log -p -3 # 最近 3 次的完整 diff3. git diff — 查看差异
git diff 告诉你"哪里改了",是 commit 前必看的命令。它的参数组合很多,记住"比较的是哪两个状态"是关键:
# git diff:查看差异(改了什么)
# 1. 工作区 vs 暂存区(还没 add 的改动)
git diff
# 2. 工作区 vs 最新提交(包括已 add 的)
git diff HEAD
# 3. 暂存区 vs 最新提交(已 add 待 commit 的改动)
git diff --staged
# 或 git diff --cached(同义)
# 4. 比较两个提交
git diff a1b2c3d e4f5g6h
git diff HEAD~3 HEAD # 当前 vs 3 个提交前
# 5. 比较两个分支
git diff main feature
# 6. 只看某个文件的差异
git diff README.md
git diff main feature -- src/
# 7. 只看文件名(不看具体内容)
git diff --name-only
git diff --stat
# 8. 单词级差异(更适合 prose/文档)
git diff --word-diff一个高频用法:commit 前先 git diff --staged 看一遍将要提交的内容。很多"误提交密钥、调试 console.log"都是因为没看就 commit 了。
4. git blame — 追溯每行代码
看到一段神秘代码,想知道"这是谁、什么时候、为什么加的"?git blame 给文件的每一行打上"出生证明":
# git blame:逐行追溯"这行代码是谁、什么时候、哪个提交加的"
git blame README.md
# 输出(每行前面带元信息):
# a1b2c3d4 (张三 2025-01-15 10:00:00 1) # 项目说明
# e4f5g6h7 (李四 2025-02-20 14:30:00 2) 安装步骤...
# a1b2c3d4 (张三 2025-01-15 10:00:00 3) 第二步
# 只看某几行
git blame -L 10,20 README.md
# 忽略空格改动(更准确找"真正改内容"的提交)
git blame -w README.md
# 沿着移动追踪(代码被复制到别处也能找到源头)
git blame -M README.md
git blame -C README.md # 跨文件复制也追踪
# VS Code 装上 GitLens 插件,光标停在哪一行
# 就会浮窗显示 blame 信息,比命令行直观得多名字虽然叫 blame(责怪),但不是为了甩锅——而是找到"那个提交",看它的 message 和 diff 就能理解这行代码的来龙去脉。VS Code 装 GitLens 插件,光标停在哪行就浮窗显示 blame,比命令行直观。
5. git reflog — 救命神器
这是 Git 最被低估的命令。reflog(reference log)记录了 HEAD 的每一次移动——包括你 reset、rebase、checkout 的操作。即使你"删了"一个提交,reflog 里还有它的踪迹,能找回来:
# git reflog:记录 HEAD 的所有移动(救命神器!)
# 即使你 reset/rebase 删了提交,reflog 里还有记录
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: feat: 添加登录
# i7j8k9l HEAD@{2}: checkout: moving from dev to main
# ...
# 找回"误删"的提交:reset 回那个哈希
git reset --hard e4f5g6h
# 神奇地恢复了!
# reflog 是本地的(不推到远程),默认保留 90 天
# 它记录的是"HEAD 的足迹",几乎能找回任何本地操作过的提交
# 看 origin/远程的 reflog(远程跟踪分支的历史)
git reflog show origin/mainreflog 是本地的,只在你电脑上,默认保留 90 天。所以"只要 commit 过(哪怕后来 reset 掉了),本地就还能找回"。但如果你从来没 commit(只在工作区改了然后 git clean),那就真没了——这也是为什么要勤 commit。
6. git bisect — 二分查找 bug
遇到"突然出现的 bug",不知道是最近哪次提交引入的?git bisect 用二分法帮你精确定位:
# git bisect:二分查找定位"哪个提交引入了 bug"
# 场景:测试发现 bug,但不知道是最近哪次提交引入的
git bisect start
git bisect bad # 标记当前(有 bug)
git bisect good v1.0.0 # 标记 v1.0.0 是好的(没 bug)
# Git 自动 checkout 到中间的提交,你测一下:
git bisect good # 这个提交没 bug → 往后找
# 或
git bisect bad # 这个提交有 bug → 往前找
# 重复几次,Git 会精确指出"罪魁祸首"那个提交
# 结束二分
git bisect reset
# 自动化:写个脚本判断当前是否有 bug
git bisect start HEAD v1.0.0
git bisect run npm test # 用测试结果自动判断 good/bad假设你有 100 个提交,二分法最多 7 次就能找到"罪魁祸首"。配合测试脚本全自动跑,效率极高。
查看历史的快捷别名
把这几个别名配上,日常查历史效率翻倍:
git lg→log --oneline --graph --all --decorate(漂亮分支图)git ds→diff --staged(看暂存区差异)git dl→diff HEAD~1(和上次提交比)
← 上一篇 Git tag 标签
下一篇 Git 撤销操作 →