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
2 Git diff魔法
(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.