git diff 从入门到精通

发布时间:2026/5/21 4:13:33

git diff 从入门到精通 从三个区域模型出发拆解 git diff 的默认行为、区间语义、输出格式以及那些让人困惑的设计选择。前置知识三个区域理解git diff之前必须先理解 Git 的三个状态区域工作区 暂存区 本地仓库 (Working Directory) (Staging / Index) (HEAD) ↓ ↓ ↓ 你编辑文件的地方 git add 进来的地方 git commit 进来的地方区域是什么怎么进怎么出工作区磁盘上的实际文件你保存编辑器git add复制到暂存区暂存区下次提交的待提交清单git addgit commit变成新 HEADHEAD当前分支最后一次提交的快照git commit只能被下一次 commit 覆盖有了这个模型三个核心 diff 命令就一目了然gitdiff# 工作区 ←→ 暂存区gitdiff--staged# 暂存区 ←→ HEADgitdiffHEAD# 工作区 ←→ HEAD为什么git diff默认比较工作区和暂存区这个默认行为让很多人困惑。直觉上你会觉得默认应该比较「工作区和上一次提交」因为那才是我改了什么。但 Git 的设计哲学不同Git 假设你git add时已经检查过那些改动了“确认没问题准备提交”。所以git diff默认展示的是你还没确认的那部分——add 之后又改了什么帮你决定要不要再 add 一次。真正常用的提交预览其实是git diff --staged# 看看我即将提交什么——这才是你最常用的gitdiff--staged# 设个别名省事gitconfig--globalalias.dsdiff --staged一句话git add 勾选git diff 还没勾的git diff --staged 已经勾了的。习惯了这个心理模型就不别扭了。工作区、暂存区、HEAD 状态演练假设你创建一个新文件333.txt内容333然后git add# 修改文件 333.txt → git add 333.txtgitdiff# 空工作区和暂存区内容一致gitdiff--staged# 有输出暂存区有 333.txtHEAD 没有gitdiffHEAD# 有输出工作区有这个文件HEAD 没有三步的状态图工作区 暂存区 HEAD 333.txt ──(相同)──▶ 333.txt ──(有差异)──▶ 无此文件git diff为空是因为 add 之后你没再改过文件——工作区和暂存区完全一致。git diff --staged展示的是「暂存了但还没提交」这才是你下一步的动作预览。未跟踪文件diff 看不到git diff只比较已跟踪内容未跟踪文件不会出现在任何 diff 输出中。想看未跟踪的文件只能用git status。如果一定要让 diff 包含未跟踪文件可以用--intent-to-add假装暂存一个空版本gitadd-Nuntracked-file.txt# 暂存一个空占位gitdiff# 现在能看到差异了这不是日常操作——只是让你知道有这条路。区间比较两点 vs 三点基础语法gitdiffA..B# 从 A 到 BA 之后的变化gitdiffA...B# 从共同祖先到 BB 分支独有的变化gitdiffA# 省略第二个参数默认对比工作区A vs 当前工作区闭区间还是开区间A..B是左开右闭(A, B]以 A 为基准线A 自己的改动不包含A 之后到 B 的改动包含。A 的改动 commit C commit D B 的改动 ✗ ✓ ✓ ✓想包含 A 的改动基准线往前挪一位gitdiffA~1..B# A 的改动也被算进去了两点 vs 三点在有分支时才有区别线性历史上..和...结果相同。分支场景下...才有特殊含义# 从 main 分叉出去后feature 分支上独有的变化gitdiffmain...feature-branch# feature 分支从分叉点到现在的全部变化含 main 合进来的gitdiffmain..feature-branch...常用于 PR review“别人在这个分支上到底改了啥去掉 main 上混入的”。实用输出格式快速浏览# 只看改了哪些文件、改了多少行gitdiff--stat# 额外标注新增/删除/重命名gitdiff--stat--compact-summary# 只看文件名gitdiff--name-only控制上下文行数# 默认 3 行上下文gitdiff# 精简到 1 行gitdiff--unified1# 或 -U1# 只显示改动行完全不要上下文gitdiff-U0其他有用选项# 只看新增的行过滤掉整个 diff 的元信息gitdiff|grep^|grep-v^# 忽略空白变化gitdiff-w# 单词级别的 diff改动浓缩到一行内gitdiff--word-diffplain# 按文件类型过滤gitdiff--*.pygitdiff-- src/高级用法查看最近 N 个提交的累计差异# 最近 3 个提交 当前未暂存修改gitdiffHEAD~3# 只看最近 3 个提交不含未暂存gitdiffHEAD~3..HEAD指定文件在某区间内的变化gitdiffmain..HEAD -- path/to/file.py查看某次提交本身做了什么gitshowcommit-hash# 等价于gitdiffcommit-hash~1..commit-hash查看暂存区中某个文件的改动gitdiff--staged-- path/to/file.py分支合并前的预览# 合入 main 会带进去什么gitdiffmain...feature-branch# 如果有冲突只看冲突文件gitdiff--name-only --diff-filterU比较两个分支的文件差异不看内容gitdiff--name-status main..feature# 输出每行A/M/D 文件名检查是否有改动脚本中常用gitdiff--quiet# 工作区干净 → exit 0有改动 → exit 1gitdiff--quiet--staged# 同上但检查暂存区常见场景速查你想看什么命令改了还没 add 的git diffadd 了还没 commit 的git diff --staged所有还没 commit 的git diff HEAD上一次 commit 改了什么git show HEAD最近 3 个 commit 总共改了啥git diff HEAD~3某两个 commit 之间的差异git diff A..BPR 里这个分支独有改动git diff main...feature只列文件名和统计git diff --stat --compact-summary只看 .py 文件的改动git diff -- *.py总结git diff的复杂性来自 Git 的三区域模型——工作区、暂存区、HEAD 各司其职。一旦理解了这个模型各种 diff 变体只是选两个点做比较而已。日常只用三个命令就够了gitdiff--staged# 提交前最后看一眼gitdiffHEAD~3# 回顾最近的改动gitdiffmain...HEAD# 分支合并前确认剩下的--stat、-U0、--word-diff是调味料按需加。

相关新闻