Command Reference

git diff 教程

讲清 git diff 如何比较工作区、暂存区和提交状态,如何正确选择比较范围,以及怎样避免看错层级。

适合谁看
  • 已经会基本提交和分支操作的开发者
  • 想理解命令边界与风险的人
前置知识
  • 知道工作区、暂存区、提交的基本关系
  • 能读懂 `git status` 和简单历史图
常见风险
  • 误把本地整理命令用到共享历史
  • 在没确认恢复路径前直接继续改写历史

一句话理解

git diff 用来比较两个 Git 状态之间的差异,关键不是“会不会用”,而是“你到底在拿什么和什么比”。

diff 永远是相对的

大多数人对 git diff 的困惑,都不是因为输出难读,而是因为一开始就选错了比较对象。

先记住这三种最常用写法

git diff
git diff --staged
git diff HEAD
  • git diff:工作区 vs 暂存区
  • git diff --staged:暂存区 vs HEAD
  • git diff HEAD:工作区和暂存区相对 HEAD 的全部变化

只要把这三种关系吃透,很多“为什么 diff 没反应”之类的问题都会自然消失。

它为什么重要

git diff 的价值不只是在 code review。
它还用于:

  • commit 前确认 staged 内容
  • rebase 或 reset 前确认当前状态
  • PR 前核对实际差异
  • 排障时收集明确证据

换句话说,diff 是很多高风险动作之前的“最后确认层”。

一些高频场景

看还有哪些内容没暂存

git diff

看下一次提交会包含什么

git diff --staged

比较功能分支和主分支

git diff main...feature/login

三点比较很适合在 review 前看“这个分支相对共同基线真正新增了什么”。

只看某个文件

git diff -- src/search/doc-search.tsx

这在仓库改动很多时特别有用。

查看文件变更概要

# --stat:带文件名和变更行数的概要
git diff --stat

# --numstat:纯数字输出,适合脚本处理
git diff --numstat

# --raw:最底层的 Git 内部格式
git diff --raw

三种输出各有用途:

选项输出内容适用场景
--stat文件名 + 增删行数柱状图人工 review,快速了解哪些文件改动最大
--numstat每行一个文件:增行数 删行数 文件名CI 脚本自动化统计变更规模
--rawGit 内部格式:模式、SHA1、状态码调试、工具开发,日常极少使用

查看某个文件的变更历史

git diff HEAD~5..HEAD~3 -- src/utils.ts

这会查看指定两个提交之间 src/utils.ts 的差异。范围可以是任意两个提交引用:HEAD~5..HEAD~3v1.0..v2.0main...feature 等。

单词级 diff

git diff --word-diff
git diff --word-diff=color

默认 diff 以行为单位,--word-diff 会高亮显示行内哪些词被改了。配合 --word-diff=color 效果更好,删除的词标红、新增的词标绿,比传统 diff 更容易读懂小改动。

进阶:输出格式控制

三种 diff 格式对比

格式命令示例输出
默认补丁git diff完整的 + / - 行,含上下文
名字状态git diff --name-statusM A D R 加文件名
仅名字git diff --name-only只列出变动文件名

自定义 diff 驱动

通过 .gitattributes 可以为特定文件类型配置 diff 行为:

# .gitattributes
*.md diff=markdown
*.png diff=exif
*.pdf diff=pdf

然后配置对应的 diff 驱动:

git config diff.markdown.textconv "cat"
git config diff.exif.textconv "exif"
git config diff.pdf.textconv "pdftotext"

这样 git diff 会自动调用转换程序,对二进制文件也能看到有意义的差异。

集成外部 diff 工具

如果你更喜欢可视化的 diff 工具,可以配置 diff.external

# 使用 delta(带语法高亮的分页器)
git config --global core.pager "delta"

# 使用 diff-so-fancy
git config --global core.pager "diff-so-fancy | less --tabs=4 -RFX"

# 使用 icdiff(并排彩色 diff)
git config --global alias.d "difftool -y --tool=icdiff"
git config --global diff.tool icdiff
git config --global difftool.icdiff.cmd "icdiff $LOCAL $REMOTE"

# 使用外部 GUI 工具
git config --global diff.tool meld
git difftool

这些工具都能显著提升阅读 diff 的体验,尤其是代码审查和排查问题时。

例如:

  • git diff 没输出,可能只是因为改动已经全部进了 staged
  • git diff --staged 没输出,可能只是因为你还没 add
  • git diff HEAD 看起来很多,是因为它同时包含 staged 和 unstaged

这也是为什么 git statusgit diff 最适合配合使用。

图例理解

跨不同 Git 层查看差异只要先判断比较对象是工作区、索引还是历史提交,diff 的输出就会稳定很多。
可能范围
工作区暂存区HEAD命名引用
你会得到什么
补丁视图评审依据更安全的下一步判断
如果 patch 看起来不对,优先重新确认范围,而不是马上怀疑命令本身。

一个更稳的检查顺序

在 commit 或发评审前,很推荐这样做:

  1. git status
  2. git diff
  3. git diff --staged
  4. 然后再 commit、rebase 或发起 review

这个顺序能提前发现很多常见问题:

  • 忘了 add 某个文件
  • staged 里混入了不该提交的内容
  • 分支比较范围选错了
  • 以为工作区干净就代表 staged 也干净
把 diff 当成起飞前检查

很多 Git 事故不是命令本身危险,而是人在没看清状态时就继续执行了下一步。git diff 可以帮你在代价变大前发现问题。

边界与常见误区

  • git diff 不改历史,但它依然可能因为比较范围错了而误导你。
  • 输出太长不一定说明改动太大,也可能只是你看的范围太宽。
  • review 场景和 staged 检查场景,不是同一个问题,不能永远用同一种 diff 方式。
不要用错比较范围来做评审判断

如果 refs 选错了,你会误以为某个分支改得更多或更少。尤其在 PR 场景下,先确认自己想看的是两点比较还是三点比较。

跟着做一遍

练习:分别观察 unstaged、staged 和分支级差异

目标是形成条件反射:不同的 diff 形式到底在回答什么问题。

准备
git switch -c lab/diff-demo
# 修改一个文件,只暂存其中一部分
动手试
  1. 运行 `git diff`,观察还没暂存的内容。
  2. 运行 `git diff --staged`,确认下一次提交会包含什么。
  3. 运行 `git diff HEAD`,比较它和前两个输出的关系。
  4. 如果有 main 基线,再运行 `git diff main...HEAD`。
会发生什么
  • 你会发现每种 diff 形式都在回答不同问题。
  • 同一个文件在不同范围里显示出的结果可以不一样。
  • 以后 commit 或 review 前更不容易看错内容。
常见错误判断
  • 如果 `git diff` 空了,检查改动是不是已经进 staged。
  • 如果分支 diff 太大,先确认 refs 和范围是否写对。
  • 如果 patch 过长难读,先缩小到某个路径再判断。