Git Diff 魔法
本章的实践展示了具有魔法效果的命令:git diff。 在不同参数的作用下,git diff 的输出并不相同。在理解了 Git 中的工作区、暂存区和版本库(当前分支)的最新版本分别是三个不同的目录树后,就非常好理解 git diff 的魔法般的行为了。
1. 工作区、暂存区和版本库的目录树浏览
有什么办法能够像查看工作区一样直观地查看暂存区及 HEAD 中的目录树吗?
对于HEAD (版本库中当前提交) 指向的目录树,可以使用 Git 底层命令 ls-tree 来查看。
[git@iZbp12wtztgoi1eseucsoyZ demo]$ git ls-tree -l HEAD 100644 blob 0dd8c691ccf1200086023bc7f627937823a5e5be 58 test.txt 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 test2.txt 100644 blob 3e757656cf36eca53338e520d134963a44f793f8 4 test3.sh 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 welcome.txt
其中:
- 使用 -l 参数可以显示文件的大小。 上面的 test.txt 的大小为25字节。
- 输出的test.txt 文件条目从左至右,第一个字段是文件的属性(rw-r--r--),第二个字段说明是Git 对象库中的一个 blob 对象(文件),第三个字段则是该文件在对象库中对应的ID,一个40位的 SHAI 哈希值格式的 ID(这个会在后面介绍),第四个字段是文件大小,第五个字段是文件名。
在浏览暂存区中的目录树之前,首先清除工作区当前的改动。通过 git clean --fd 命令清除当 前工作区中没有加入版本库的文件和目录(非跟踪文件和目录),然后执行 git checkout . 命令,用暂存区内容刷新工作区。
[git@iZbp12wtztgoi1eseucsoyZ demo]$ git clean -fd #清除当前工作区没有加入版本库的文件和目录。 Removing a/ [git@iZbp12wtztgoi1eseucsoyZ demo]$ ls test2.txt test3.sh test.txt welcome.txt [git@iZbp12wtztgoi1eseucsoyZ demo]$ git checkout . #用暂存区内容刷新工作区。
然后开始在工作区中作出一些修改(修改 welcome.txt,再增加一个子目录和文件),并添加到暂存区,最后再对工作区作出修改。
(1) 工作区和暂存区比较
git diff
(2) 暂存区和 HEAD 比较
git diff --cached
(3) 工作区和HEAD 比较
git diff HEAD
不要使用 git commit -a
实际上,Git 的提交命令(git commit)可以带上-a 参数,对本地所有变更的文件执行提交操作。包括对本地修改的文件和删除的文件,但不包括未被版本库跟踪的文件。
这个命令的确可以简化一些操作,减少用 git add 命令标识变更文件的步骤,但是如果习惯了使用这个“偷懒”的提交命令,就会丢掉 Git 暂存区带给用户的最大好处:对提交内容进行控制的能力。
有的用户甚至通过别名设置功能将 ci 设置为 git commit -a, 这根式不可取的行为,应严格禁止。
搁置问题,暂存状态
查看一下当前工作区的状态:
[git@iZbp12wtztgoi1eseucsoyZ demo]$ git status # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: welcome.txt # no changes added to commit (use "git add" and/or "git commit -a")
在状态输出中,Git 体贴地告诉了用户如何将加入暂存区的文件从暂存区撤出以便让暂存区和 HEAD 一致(这样提交就不会发生)。还告诉用户,对于暂存区更新后在工作区所做的再一次修改有两个选择:或者再次添加到暂存区,或者取消工作区新做出的改动。但是现在理解设计的命令还有些难度,一个是 git reset,一个是 git checkout。 需要先理解什么是 HEAD,什么是 master 分支,以及 Git 对象存储的实现机制等问题,这样才可以更好地操作暂存区。
为此,我做出一个非常艰难的决定:就是保存当前的工作进度,在研究了HEAD和master 分支的机制之后,继续对暂存区的探索。命令 git stash 就是用于保存当前工作进度的。
[git@iZbp12wtztgoi1eseucsoyZ demo]$ git stash Saved working directory and index state WIP on master: fb97aa3 commit welcome.txt HEAD is now at fb97aa3 commit welcome.txt [git@iZbp12wtztgoi1eseucsoyZ demo]$ git status -s
运行完 git stash 之后,再查看工作区状态,会看见工作区尚未提交的改动(包括暂存区的改动)全都不见了。