写写代码,聊聊生活

0%

git diff 详解

最近用到 git diff 命令比较差异性,遇到了点问题,网上搜了一遍,发现说得都非常模糊、甚至有些不准确的,最后自己动手把所有情况实践一遍,总结记录一下

git diff 介绍

git diff 可以用来比较对象库中任意两个树对象之间的差异,也可以用来比较工作目录,索引,和对象树之间的差异,通常,git diff 命令进行树对象之间的比较时,你可以指定 commit / branch / tag 等。

看起来有点抽象,为了方便理解,这里主要以比较工作区和暂存区、工作区和上一次commit后分支的差异来说

工作区和暂存区

工作区是指实际工作使用的目录,简单暴力来说就是 .git 目录所在目录
.git 目录为 git 的版本库,主要存储 git 相关的信息,比如分支、config、index 等

而暂存区则是由 git 生成,在 git add 后,被 add 的改动会加入到暂存区中,通过 git commit 可以将暂存区中的改动提交到分支上
工作区中已有 a.txt,现在新创建 b.txt,在 git add b.txt 之前,暂存区是空的

git add b.txt 后,变化如下

HEAD 是一个指向当前分支的指针

git commit 后,变化如下

git diff 具体用法

  • git diff
  • git diff –cached
  • git diff HEAD

新建目录 gitDiffTest,进入该目录初始化 git,并新建 a.txt

1
2
3
4
git init
echo "1111" > a.txt
git add a.txt
git commit -m "init"

执行完上面的命令后修改 a.txt

1
echo "2222" >> a.txt

分别在不同场景下使用这三个命令,看看分别输出什么

使用 git diff

git add a.txt 之前,执行该命令,结果如下

1
2
3
4
5
6
7
diff --git a/a.txt b/a.txt
index 5f2f16b..4f142ee 100644
--- a/a.txt
+++ b/a.txt
@@ -1 +1,2 @@
1111
+2222

这时候该命令比较的是工作区和上一次commit的差异,因为工作区有修改,所以有输出

git add a.txt 之后,执行该命令,没有输出,这时候比较的是工作区和暂存区的差异,因为此时工作区和暂存区是一样的,所以没有输出

使用 git diff --cached

git add a.txt 之前,执行该命令没有输出

git add a.txt 之后,有输出,所以该命令比较的是暂存区和上一次 commit 的差异

使用 git diff HEAD

不管 git add a.txt 之前还是之后,执行该命令都有输出,所以该命令比较的是工作区和上一次 commit 的差异,也可以理解为明确告诉 git 要比较工作区和 HEAD 的差异

以上三个命令在不同场景下有着不一样的作用,但是对于 Untracked 的改动,比如新创建的文件,我试了下,三个命令都不管用了,原因也很简单,Untracked 的改动是 git 还未跟踪的,也就不存在 diff 一说了,对于这种情况,可以使用 git add -N file 跟踪修改,然后就可以通过 diff 命令输出差异了

解析 git diff

git diff 信息一般有固定的格式,通过解析这些信息,可以用于代码审查等

基本格式
1
2
3
4
5
6
7
diff --git a/f1 b/f2
index 58c9bdf..a30a52a 100644
--- a/f1
+++ b/f2
@@ -1 +1,2 @@
111
+222

a 表示更改前的版本,b 表示更改后的版本

第一行为 diff 的 header,用于表示比较的是哪两个文件,f1跟f2比较

第二行表示两个文件的 hash 值和文件类型,100644 表示文本文件

第三、四行表示进行比较的两个文件,— 表示变动前的版本,+++ 表示变动后的版本

第五行是一个 thunk header(可能会有多个),提供变动的”上下文“(context),-1 表示接下来展示变动前文件第一行,+1,2 表示接下来展示变动后文件第一至第二行

接下来的几行就是具体的变动内容了

扩展 header

在第一行 header 之后有可能包含如下的几种扩展 header:

新增:

1
2
3
4
5
diff --git a/file b/file
new file mode 100644
index 0000000..53bffd7
--- /dev/null
+++ b/file

删除:

1
2
3
4
5
diff --git a/file b/file
deleted file mode 100644
index 53bffd7..0000000
--- a/file
+++ /dev/null

复制:

1
2
3
4
5
diff --git a/file b/file
deleted file mode 100644
index 53bffd7..0000000
--- a/file
+++ /dev/null

重名:

1
2
3
4
5
diff --git a/file1 b/file2
rename from file1
rename to file2
--- a/a
+++ b/b

了解 diff 格式后,就可以通过正则匹配等操作进行解析了,用 python 写了个脚本,具体代码就不贴了…

思考

git diff 除了用于代码对比,代码审核外,是不是还可以用于代码规范检查呢?相比于用编译器提供的接口来做,这种方式更轻量级,而且不影响编译速度,还能跨平台,只要把规则定定好就行了

总结

  • git 工作区表示 .git 目录所在的目录,暂存区在 .git 中,git 默认生成
  • git add 把工作区的修改提交到暂存区,git commit 把暂存区的修改提交到分支上
  • git diff:当暂存区为空时,比较的是工作区和上一次 commit 的差异,当暂存区不为空时,比较的是工作区和暂存区的差异
  • git diff --cached:比较的是暂存区和上一次 commit 的差异
  • git diff HEAD:比较的是工作区和上一次 commit 的差异
  • git diff 信息有固定格式
  • 通过解析 diff 可以实现代码比较/审查等

参考