git-diff 与暂存区操作

3 min中级

git diff与diff --staged的详细用法:差异比较、输出格式与实用技巧。

1. git diff 概述

1.1 diff 的三种模式

命令比较对象用途
git diff工作区 vs 暂存区查看未暂存的修改
git diff --staged暂存区 vs 最新提交查看已暂存的修改
git diff <commit>工作区 vs 指定提交查看与指定提交的差异

2. diff 输出格式

2.1 标准输出

diff --git a/src/index.js b/src/index.js
index abc1234..def5678 100644
--- a/src/index.js
+++ b/src/index.js
@@ -10,7 +10,8 @@ function process(data) {
   const result = transform(data);
   if (result.isValid) {
-    return result.value;
+    const processed = enhance(result.value);
+    return processed;
   }
   return null;
 }

2.2 输出解读

部分含义
diff --git a/... b/...比较的文件路径
index abc1234..def5678对象哈希范围
100644文件模式
--- a/src/index.js原文件
+++ b/src/index.js新文件
@@ -10,7 +10,8 @@变更位置(旧文件第10行起7行,新文件第10行起8行)
- 前缀删除的行
+ 前缀新增的行
空格前缀上下文行

2.3 统计输出

git diff --stat
#  src/index.js    | 3 ++-
#  src/utils.js    | 5 +++--
#  2 files changed, 4 insertions(+), 4 deletions(-)

git diff --numstat
# 2       1       src/index.js
# 3       2       src/utils.js
# ↑新增行  ↑删除行

3. 常用 diff 选项

3.1 过滤选项

# 只看文件名
git diff --name-only

# 只看文件名和状态
git diff --name-status
# M  src/index.js      ← Modified
# A  src/new.js        ← Added
# D  src/old.js        ← Deleted

# 按路径过滤
git diff -- src/
git diff -- '*.js'
git diff -- ':(exclude)*.test.js'

3.2 显示选项

# 增加上下文行数
git diff -U5              # 5行上下文(默认3行)

# 忽略空白
git diff -w               # 忽略所有空白变化
git diff --ignore-space-at-eol  # 只忽略行尾空白

# 彩色输出
git diff --color-words    # 词语级别的差异高亮
git diff --word-diff      # 词语级别的差异标记

# 函数上下文
git diff -W               # 显示完整函数

3.3 比较选项

# 比较两个分支
git diff main..feature

# 比较两个提交
git diff abc1234..def5678

# 比较分支分叉点以来的变化
git diff main...feature   # feature 相对于 main 的变更

# 只看暂存区与 HEAD 的差异
git diff --staged
git diff --cached         # 同上

4. 高级用法

4.1 比较特定文件

# 比较特定文件在不同提交间的差异
git diff HEAD~3 -- src/index.js

# 比较两个分支的特定文件
git diff main feature -- package.json

4.2 交互式 diff

# 逐文件查看 diff
git diff --stat | fzf | xargs -I{} git diff -- {}

# 使用 difftool
git difftool              # 使用配置的 diff 工具
git difftool --tool=vimdiff

4.3 查看合并冲突的差异

# 查看冲突标记
git diff --check          # 标记空白错误和冲突标记

# 合并冲突的三方差异
git diff HEAD...MERGE_HEAD

5. diff 算法

5.1 算法选择

# 默认算法(Myers diff)
git diff

# 耐心算法(更好的人类可读性)
git diff --patience

# 直方图算法
git diff --histogram
算法特点适用场景
Myers默认,快速通用
Patience关注唯一行匹配代码重构
HistogramPatience 的改进复杂变更

5.2 重命名检测

# 检测文件重命名
git diff -M               # 检测重命名(默认50%相似度)
git diff -M50%             # 50%相似度阈值
git diff -M90%             # 90%相似度阈值(更严格)

# 检测文件复制
git diff -C               # 检测复制
git diff -C -M             # 同时检测重命名和复制

6. 实用别名

# .gitconfig
[alias]
    d = diff
    ds = diff --staged
    dn = diff --name-only
    dns = diff --staged --name-only
    dw = diff --color-words
    dws = diff --staged --color-words
    dst = diff --stat
    dsts = diff --staged --stat

diff 三种模式

基本写法:工作区与暂存区差异 git diff

# 查看工作区与暂存区的差异
git diff;

基本写法:暂存区与最新提交差异 git diff --staged

# 查看暂存区与 HEAD 的差异
git diff --staged;

基本写法:工作区与指定提交差异 git diff <commit>

# 查看工作区与 abc1234 提交的差异
git diff abc1234;

统计输出

基本写法:统计差异 git diff --stat

# 显示每个文件的增删行数统计
git diff --stat;

基本写法:数字统计 git diff --numstat

# 输出格式:新增行数 删除行数 文件名
git diff --numstat;

过滤选项

基本写法:只看文件名 git diff --name-only

# 列出所有变更的文件名
git diff --name-only;

基本写法:只看文件名和状态 git diff --name-status

# M 修改 / A 新增 / D 删除
git diff --name-status;

基本写法:按目录路径过滤 git diff -- <路径>

# 查看 src/ 目录的差异
git diff -- src/;

基本写法:按文件类型过滤 git diff -- <模式>

# 查看 JavaScript 文件的差异
git diff -- '*.js';

基本写法:排除路径 git diff -- ':(exclude)<模式>'

# 排除测试文件
git diff -- ':(exclude)*.test.js';

显示选项

基本写法:增加上下文行数 git diff -U<行数>

# 显示 5 行上下文(默认 3 行)
git diff -U5;

基本写法:忽略空白 git diff -w

# 忽略所有空白变化
git diff -w;

基本写法:忽略行尾空白 git diff --ignore-space-at-eol

# 只忽略行尾空白
git diff --ignore-space-at-eol;

基本写法:词语级别差异 git diff --color-words

# 词语级别的差异高亮
git diff --color-words;

基本写法:词语差异标记 git diff --word-diff

# 词语级别的差异标记
git diff --word-diff;

基本写法:函数上下文 git diff -W

# 显示完整函数的差异
git diff -W;

比较选项

基本写法:比较两个分支 git diff <分支1>..<分支2>

# 比较 main 与 feature 分支
git diff main..feature;

基本写法:比较两个提交 git diff <提交1>..<提交2>

# 比较 abc1234 与 def5678 提交
git diff abc1234..def5678;

基本写法:比较分叉点以来的变化 git diff <基础分支>...<目标分支>

# feature 相对于 main 的变更
git diff main...feature;

基本写法:暂存区与 HEAD 差异 git diff --cached

# 查看暂存区与 HEAD 的差异
git diff --cached;

比较特定文件

基本写法:比较特定文件在不同提交间的差异 git diff <提交> -- <文件>

# 查看 src/index.js 在最近 3 次提交的差异
git diff HEAD~3 -- src/index.js;

基本写法:比较两个分支的特定文件 git diff <分支1> <分支2> -- <文件>

# 比较 main 和 feature 分支的 package.json
git diff main feature -- package.json;

交互式 diff

基本写法:使用 difftool git difftool

# 使用默认 diff 工具
git difftool;

基本写法:指定 diff 工具 git difftool --tool=<工具名>

# 使用 vimdiff 查看差异
git difftool --tool=vimdiff;

查看合并冲突差异

基本写法:检查冲突标记 git diff --check

# 检查冲突标记和空白错误
git diff --check;

基本写法:合并冲突的三方差异 git diff HEAD...MERGE_HEAD

# 查看合并冲突的三方差异
git diff HEAD...MERGE_HEAD;

diff 算法

基本写法:默认算法(Myers) git diff

# 使用默认 Myers 算法
git diff;

基本写法:耐心算法 git diff --patience

# 使用耐心算法,适合代码重构
git diff --patience;

基本写法:直方图算法 git diff --histogram

# 使用直方图算法,适合复杂变更
git diff --histogram;

重命名检测

基本写法:检测文件重命名 git diff -M

# 检测重命名(默认 50% 相似度)
git diff -M;

基本写法:指定相似度阈值 git diff -M<百分比>

# 90% 相似度阈值(更严格)
git diff -M90%;

基本写法:检测文件复制 git diff -C

# 检测文件复制
git diff -C;

基本写法:同时检测重命名和复制 git diff -C -M

# 同时检测重命名和复制
git diff -C -M;

实用别名

基本写法:配置 diff 别名 git config --global alias.<别名> "<命令>"

# 配置常用 diff 别名
git config --global alias.d "diff";

基本写法:配置暂存区差异别名 git config --global alias.<别名> "<命令>"

# 配置暂存区差异别名
git config --global alias.ds "diff --staged";

基本写法:配置文件名差异别名 git config --global alias.<别名> "<命令>"

# 配置文件名差异别名
git config --global alias.dn "diff --name-only";

基本写法:配置词语差异别名 git config --global alias.<别名> "<命令>"

# 配置词语差异别名
git config --global alias.dw "diff --color-words";

基本写法:配置统计差异别名 git config --global alias.<别名> "<命令>"

# 配置统计差异别名
git config --global alias.dst "diff --stat";