Git: Git差异比较与变更分析
差异比较是理解代码变更的重要工具。通过比较不同版本之间的差异,可以清楚地看到修改了什么、为什么修改、修改的影响范围。
1. 差异比较基础
(1) Git的三个区域
Git有三个重要的区域,理解它们是掌握diff的关键:
graph TB
A[工作区<br/>Working Directory] -->|git add| B[暂存区<br/>Staging Area]
B -->|git commit| C[版本库<br/>Repository]
A -.->|git diff| B
B -.->|git diff --staged| C
A -.->|git diff HEAD| C
style A fill:#fff3cd
style B fill:#d4edda
style C fill:#c3e6cb
- 工作区:实际编辑文件的目录
- 暂存区:准备提交的修改(也叫索引区)
- 版本库:所有提交的历史记录
(2) 差异比较的场景
- 提交前检查:确认要提交的内容
- 代码审查:查看他人的修改
- 版本对比:比较不同版本的差异
- 分支对比:比较不同分支的差异
- 冲突分析:理解冲突的原因
(3) diff命令概述
git diff 用于显示差异,基本用法:
BASH
git diff [options] [<commit>] [--] [<path>...]
2. 比较三个区域
(1) 工作区 vs 暂存区
比较工作区和暂存区的差异,即未暂存的修改。
▶ 示例:查看未暂存的修改
BASH
# 修改文件
echo "new line" >> README.md
# 查看工作区修改(相对于暂存区)
git diff
# 输出:
# diff --git a/README.md b/README.md
# index abc1234..def5678 100644
# --- a/README.md
# +++ b/README.md
# @@ -1,3 +1,4 @@
# # My Project
#
# ## Features
# +new line
# 查看特定文件
git diff README.md
# 查看多个文件
git diff file1.js file2.js
(2) 暂存区 vs 版本库
比较暂存区和版本库的差异,即已暂存但未提交的修改。
▶ 示例:查看已暂存的修改
BASH
# 添加修改到暂存区
git add README.md
# 查看暂存区修改(相对于最新提交)
git diff --staged
# 或者使用旧语法
git diff --cached
# 输出:
# diff --git a/README.md b/README.md
# index abc1234..def5678 100644
# --- a/README.md
# +++ b/README.md
# @@ -1,3 +1,4 @@
# # My Project
#
# ## Features
# +new line
(3) 工作区 vs 版本库
比较工作区和版本库的差异,即所有未提交的修改。
▶ 示例:查看所有未提交的修改
BASH
# 查看工作区相对于HEAD的差异
git diff HEAD
# 查看工作区相对于某个提交的差异
git diff a1b2c3d
# 查看工作区相对于上一次提交的差异
git diff HEAD^
# 输出包含已暂存和未暂存的所有修改
3. 差异输出解读
(1) diff输出格式
▶ 示例:理解diff输出
BASH
git diff
# 输出格式说明:
# diff --git a/file.js b/file.js <- Git差异头
# index abc1234..def5678 100644 <- 文件模式
# --- a/file.js <- 原文件(-表示删除)
# +++ b/file.js <- 新文件(+表示添加)
# @@ -1,3 +1,4 @@ <- 变化位置(hunk头)
# # My Project <- 上下文行(空格开头)
#
# ## Features <- 上下文行
# +new line <- 新增行(+开头)
# -old line <- 删除行(-开头)
(2) hunk头格式
hunk头的格式:@@ -a,b +c,d @@
-a,b:原文件从第a行开始,共b行+c,d:新文件从第c行开始,共d行
TEXT
📖 仅展示
@@ -10,5 +10,7 @@ function login() {
表示:原文件第10行开始的5行,变成了新文件第10行开始的7行。
(3) 统计信息
▶ 示例:查看统计摘要
BASH
# 显示统计信息
git diff --stat
# 输出:
# README.md | 1 +
# file1.js | 5 ++---
# file2.js | 3 +++
# 3 files changed, 5 insertions(+), 3 deletions(-)
# 显示简短统计
git diff --shortstat
# 输出:
# 3 files changed, 5 insertions(+), 3 deletions(-)
# 显示数字统计
git diff --numstat
# 输出:
# 1 0 README.md
# 2 3 file1.js
# 3 0 file2.js
4. 比较不同版本
(1) 比较两次提交
▶ 示例:提交对比
BASH
# 比较两次提交
git diff a1b2c3d d4e5f6g
# 比较某个提交和当前工作区
git diff a1b2c3d
# 比较某个提交和暂存区
git diff --staged a1b2c3d
# 比较相邻提交
git diff HEAD^ HEAD
# 查看某次提交引入的修改
git show a1b2c3d
# 等价于
git diff a1b2c3d^ a1b2c3d
(2) 比较分支
▶ 示例:分支对比
BASH
# 比较两个分支
git diff main feature
# 查看feature分支相对于main的修改
git diff main...feature
# 查看main分支相对于feature的修改
git diff feature...main
# 比较分支的某个文件
git diff main feature -- file.js
# 查看分支分叉后的修改
git diff $(git merge-base main feature) feature
(3) 比较标签
▶ 示例:版本对比
BASH
# 比较两个版本
git diff v1.0.0 v2.0.0
# 查看某个版本的修改
git diff v1.0.0 HEAD
# 查看版本发布说明
git diff v1.0.0 v2.0.0 --stat
# 输出:
# src/auth.js | 25 +++++++++++++++++++++++++
# src/user.js | 10 +++++-----
# package.json | 2 +-
# 3 files changed, 32 insertions(+), 5 deletions(-)
5. diff选项详解
(1) 常用选项
| 选项 | 说明 | 用途 |
|---|---|---|
--staged |
暂存区vs版本库 | 查看已暂存的修改 |
--stat |
显示统计 | 查看修改统计 |
--shortstat |
简短统计 | 查看总修改量 |
--name-only |
只显示文件名 | 查看修改的文件列表 |
--name-status |
显示状态 | 查看文件修改状态 |
-w |
忽略空白 | 忽略空格和Tab差异 |
▶ 示例:使用diff选项
BASH
# 只显示修改的文件名
git diff --name-only
# 输出:
# README.md
# file1.js
# file2.js
# 显示文件修改状态
git diff --name-status
# 输出:
# M README.md <- 修改
# A file1.js <- 新增
# D file2.js <- 删除
# R old.js new.js <- 重命名
# 忽略空白字符差异
git diff -w
# 忽略所有空白
git diff -w --ignore-blank-lines
# 显示单词级别的差异
git diff --word-diff
# 显示颜色高亮
git diff --color-words
(2) 上下文控制
▶ 示例:控制上下文行数
BASH
# 减少上下文行数
git diff -U1
# 输出只显示1行上下文:
# @@ -10,3 +10,4 @@ function login() {
# const user = authenticate();
# + if (!user) return;
# return user;
# 不显示上下文
git diff -U0
# 增加上下文行数
git diff -U10
(3) 路径限制
▶ 示例:限制比较范围
BASH
# 只比较特定文件
git diff -- file.js
# 只比较特定目录
git diff -- src/
# 排除某些文件
git diff -- . ':!*.test.js'
# 只比较某种类型的文件
git diff -- '*.js'
6. 实际应用场景
(1) 提交前检查
▶ 示例:提交前完整检查
BASH
# 1. 查看未暂存的修改
git diff
# 2. 查看已暂存的修改
git diff --staged
# 3. 查看所有修改的统计
git diff HEAD --stat
# 4. 确认无误后提交
git commit -m "feat: 添加新功能"
(2) 代码审查
▶ 示例:审查他人代码
BASH
# 查看某个提交的修改
git show a1b2c3d
# 查看某个提交的统计
git show --stat a1b2c3d
# 查看某个提交修改的文件列表
git show --name-only a1b2c3d
# 比较两个分支的差异
git diff main...feature
# 查看Pull Request的差异
git diff origin/main...origin/feature
(3) 冲突分析
▶ 示例:分析合并冲突
BASH
# 查看将要合并的内容
git diff main...feature
# 查看共同祖先到feature的修改
git diff $(git merge-base main feature) feature
# 查看可能冲突的文件
git diff --name-only main...feature
# 查看具体的冲突内容
git diff main feature -- file.js
(4) 版本回退确认
▶ 示例:确认回退内容
BASH
# 查看回退会丢失的内容
git diff HEAD HEAD~1
# 查看回退的统计
git diff HEAD HEAD~1 --stat
# 查看特定文件的回退内容
git diff HEAD HEAD~1 -- file.js
# 确认后执行回退
git reset --hard HEAD~1
❓ 常见问题
Q git diff、git diff --staged、git diff HEAD有什么区别?
A
git diff 比较工作区和暂存区(未暂存的修改);git diff --staged 比较暂存区和版本库(已暂存的修改);git diff HEAD 比较工作区和版本库(所有未提交的修改)。Q 如何只查看修改的文件名而不看具体内容?
A 使用
git diff --name-only 只显示文件名,或使用 git diff --stat 显示文件名和修改统计。Q diff输出中的+和-是什么意思?
A 以+开头的行表示新增的内容,以-开头的行表示删除的内容,以空格开头的行是上下文(未修改)。
Q 如何比较两个分支的差异?
A 使用
git diff main feature 比较两个分支,使用 git diff main...feature 查看feature分支相对于main的修改(从分叉点开始)。Q 如何忽略空格差异?
A 使用
git diff -w 忽略空格和Tab的差异,使用 git diff --ignore-space-at-eol 忽略行尾空格差异。📖 小节
- Git有三个区域:工作区、暂存区、版本库,diff可以比较任意两个区域
- 工作区vs暂存区:
git diff查看未暂存的修改 - 暂存区vs版本库:
git diff --staged查看已暂存的修改 - 工作区vs版本库:
git diff HEAD查看所有未提交的修改 - 比较版本:可以比较提交、分支、标签之间的差异
- diff选项丰富:--stat统计、--name-only文件列表、-w忽略空白
📝 作业
-
基础题:创建一个Git仓库,修改文件后使用
git diff查看未暂存的修改,添加到暂存区后使用git diff --staged查看已暂存的修改。 -
进阶题:创建两个分支,在不同分支上修改相同文件,使用
git diff比较两个分支的差异,分析可能产生的冲突。 -
挑战题:使用
git diff的各种选项(--stat、--name-status、-w等)分析一个实际项目的修改,生成一份完整的变更报告。