5.3 Git Diff魔法

本章的实践展示了具有魔法效果的命令:git diff。在不同参数的作用下,git diff的输出并不相同。在理解了Git中的工作区、暂存区和版本库(当前分支)的最新版本分别是三个不同的目录树后,就非常好理解git diff的魔法般的行为了。

1.工作区、暂存区和版本库的目录树浏览

有什么办法能够像查看工作区一样直观地查看暂存区及HEAD中的目录树吗?

对于HEAD(版本库中当前提交)指向的目录树,可以使用Git底层命令ls-tree来查看。


$git ls-tree-l HEAD

100644 blob fd3c069c1de4f4bc9b15940f490aeb48852f3c42 25 welcome.txt


其中:

使用-l参数可以显示文件的大小。上面的welcome.txt的大小为25字节。

输出的welcome.txt文件条目从左至右,第一个字段是文件的属性(rw-r—r—),第二个字段说明是Git对象库中的一个blob对象(文件),第三个字段则是该文件在对象库中对应的ID——一个40位的SHA1哈希值格式的ID(这个会在后面介绍),第四个字段是文件大小,第五个字段是文件名。

在浏览暂存区中的目录树之前,首先清除工作区当前的改动。通过git clean-fd命令清除当前工作区中没有加入版本库的文件和目录(非跟踪文件和目录),然后执行git checkout.命令,用暂存区内容刷新工作区。


$cd/path/to/my/workspace/demo

$git clean-fd

$git checkout.


然后开始在工作区中做出一些修改(修改welcome.txt,再增加一个子目录和文件),并添加到暂存区,最后再对工作区做出修改。


$echo "Bye-Bye.">>welcome.txt

$mkdir-p a/b/c

$echo "Hello.">a/b/c/hello.txt

$git add.

$echo "Bye-Bye.">>a/b/c/hello.txt

$git status-s

AM a/b/c/hello.txt

M welcome.txt


上面的命令运行完毕后,通过精简的状态输出可以看出,工作区、暂存区和版本库当前分支的最新版本(HEAD)各不相同。先来看看工作区中文件的大小:


$find.-path./.git-prune-o-type f-printf "%-20p\t%s\n"

./welcome.txt 34

./a/b/c/hello.txt 16


要显示暂存区的目录树,可以使用git ls-files命令。


$git ls-files-s

100644 18832d35117ef2f013c4009f5b2128dfaeff354f 0 a/b/c/hello.txt

100644 51dbfd25a804c30e9d8dc441740452534de8264b 0 welcome.txt


注意,这个输出与之前使用git ls-tree命令的输出不一样,其中第三个字段不是文件大小而是暂存区编号。如果想针对暂存区的目录树使用git ls-tree命令,需要先将暂存区的目录树写入Git对象库(用git write-tree命令),然后针对该目录树执行git ls-tree命令。


$git write-tree

9431f4a3f3e1504e03659406faa9529f83cd56f8

$git ls-tree-l 9431f4a

040000 tree 53583ee687fbb2e913d18d508aefd512465b2092-a

100644 blob 51dbfd25a804c30e9d8dc441740452534de8264b 34 welcome.txt


从上面的命令中可以看出:

到处都是40位的SHA1哈希值格式的ID,可以用于指代文件内容(blob)、目录树(tree)和提交。但什么是SHA1哈希值ID,作用是什么,这些疑问暂时搁置,下一章再解答。

命令git write-tree的输出就是写入Git对象库中的Tree ID,这个ID将作为下一条命令的输入。

在git ls-tree命令中,没有把40位的ID写全,而是使用了前几位,实际上只要不与其他对象的ID冲突,就可以随心所欲地使用缩写ID。

可以看到git ls-tree的输出显示的第一条是一个tree对象,即刚才创建的一级目录a。

如果想要递归显示目录内容,则使用-r参数调用。使用参数-t可以把递归过程中遇到的每棵树都显示出来,而不只是显示最终的文件。下面执行递归操作显示目录树的内容:


$git write-tree|xargs git ls-tree-l-r-t


5.3 Git Diff魔法 - 图1

2 Git diff魔法

5.3 Git Diff魔法 - 图2

(2)暂存区和HEAD比较。


$git diff—cached

diff—git a/a/b/c/hello.txt b/a/b/c/hello.txt

new file mode 100644

index 0000000..18832d3

—-/dev/null

+++b/a/b/c/hello.txt

@@-0,0+1@@

+Hello.

diff—git a/welcome.txt b/welcome.txt

index fd3c069..51dbfd2 100644

—-a/welcome.txt

+++b/welcome.txt

@@-1,2+1,3@@

Hello.

Nice to meet you.

+Bye-Bye.


(3)工作区和HEAD比较。


$git diff HEAD

diff—git a/a/b/c/hello.txt b/a/b/c/hello.txt

new file mode 100644

index 0000000..e8577ea

—-/dev/null

+++b/a/b/c/hello.txt

@@-0,0+1,2@@

+Hello.

+Bye-Bye.

diff—git a/welcome.txt b/welcome.txt

index fd3c069..51dbfd2 100644

—-a/welcome.txt

+++b/welcome.txt

@@-1,2+1,3@@

Hello.

Nice to meet you.

+Bye-Bye.