- 已经会基本提交和分支操作的开发者
- 想理解命令边界与风险的人
Command Reference
git diff 教程
讲清 git diff 如何比较工作区、暂存区和提交状态,如何正确选择比较范围,以及怎样避免看错层级。
- 知道工作区、暂存区、提交的基本关系
- 能读懂 `git status` 和简单历史图
- 误把本地整理命令用到共享历史
- 在没确认恢复路径前直接继续改写历史
一句话理解
git diff 用来比较两个 Git 状态之间的差异,关键不是“会不会用”,而是“你到底在拿什么和什么比”。
大多数人对 git diff 的困惑,都不是因为输出难读,而是因为一开始就选错了比较对象。
先记住这三种最常用写法
git diff
git diff --staged
git diff HEAD
git diff:工作区 vs 暂存区git diff --staged:暂存区 vsHEADgit 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 脚本自动化统计变更规模 |
--raw | Git 内部格式:模式、SHA1、状态码 | 调试、工具开发,日常极少使用 |
查看某个文件的变更历史
git diff HEAD~5..HEAD~3 -- src/utils.ts
这会查看指定两个提交之间 src/utils.ts 的差异。范围可以是任意两个提交引用:HEAD~5..HEAD~3、v1.0..v2.0、main...feature 等。
单词级 diff
git diff --word-diff
git diff --word-diff=color
默认 diff 以行为单位,--word-diff 会高亮显示行内哪些词被改了。配合 --word-diff=color 效果更好,删除的词标红、新增的词标绿,比传统 diff 更容易读懂小改动。
进阶:输出格式控制
三种 diff 格式对比
| 格式 | 命令 | 示例输出 |
|---|---|---|
| 默认补丁 | git diff | 完整的 + / - 行,含上下文 |
| 名字状态 | git diff --name-status | M 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没输出,可能只是因为改动已经全部进了 stagedgit diff --staged没输出,可能只是因为你还没 addgit diff HEAD看起来很多,是因为它同时包含 staged 和 unstaged
这也是为什么 git status 和 git diff 最适合配合使用。
图例理解
一个更稳的检查顺序
在 commit 或发评审前,很推荐这样做:
git statusgit diffgit diff --staged- 然后再 commit、rebase 或发起 review
这个顺序能提前发现很多常见问题:
- 忘了 add 某个文件
- staged 里混入了不该提交的内容
- 分支比较范围选错了
- 以为工作区干净就代表 staged 也干净
很多 Git 事故不是命令本身危险,而是人在没看清状态时就继续执行了下一步。git diff 可以帮你在代价变大前发现问题。
边界与常见误区
git diff不改历史,但它依然可能因为比较范围错了而误导你。- 输出太长不一定说明改动太大,也可能只是你看的范围太宽。
- review 场景和 staged 检查场景,不是同一个问题,不能永远用同一种 diff 方式。
如果 refs 选错了,你会误以为某个分支改得更多或更少。尤其在 PR 场景下,先确认自己想看的是两点比较还是三点比较。
跟着做一遍
目标是形成条件反射:不同的 diff 形式到底在回答什么问题。
git switch -c lab/diff-demo # 修改一个文件,只暂存其中一部分动手试
- 运行 `git diff`,观察还没暂存的内容。
- 运行 `git diff --staged`,确认下一次提交会包含什么。
- 运行 `git diff HEAD`,比较它和前两个输出的关系。
- 如果有 main 基线,再运行 `git diff main...HEAD`。
- 你会发现每种 diff 形式都在回答不同问题。
- 同一个文件在不同范围里显示出的结果可以不一样。
- 以后 commit 或 review 前更不容易看错内容。
- 如果 `git diff` 空了,检查改动是不是已经进 staged。
- 如果分支 diff 太大,先确认 refs 和范围是否写对。
- 如果 patch 过长难读,先缩小到某个路径再判断。