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 次的完整 diff

3. 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/main

reflog 是本地的,只在你电脑上,默认保留 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 tag 标签

下一篇 Git 撤销操作

✈️💬