上一节了解了 Git 的一个重要的概念:暂存区。
暂存区是一个介于工作区和版本库的中间状态,当执行commit时,实际上是将暂存区的内容提交大版本库中,而执行add则是将本次变更添加到暂存区。
上一节中也出现了很多新的问题?比如说 HEAD 是什么?它与 master 是什么关系?
Git对象库探秘
前面我们查看提交日志的时候,出现了提交ID,一个由40位十六进制数字组成的SHA1哈希值,通过查看日志的详细输出,会看看到更多的SHA1哈希值。
可以看出,一个提交当中包含了三个用 SHA1哈希值 标识的Git 对象ID:
1,commit 99447928556fbc66f267bdcdcd8b4a84678cda60 这是本次提交的唯一标识;
2,tree 2f468676a412dd91435ad6b8c3706eebba13fa9c 这是本次提交所对应的目录树;
3,parent 1f2cc329be37c7678bac8c2d1f696effffe61c08 这是本次提交的父提交(上一次提交,第一个提交是没有parent的)
可以使用 git cat-file 命令来彻底研究Git 对象ID,分别查看一下上面的三个对象ID的类型
注意:在引用对象ID的时候,没有必要把整个40位的ID写全,只要保证从头开始的前几位不冲突就行,一般在日常工作中输入前6位即可。
再使用 git cat-file 命令来查看一下对象内容
查看目录树(tree对象)中看到了一个新类型的对象:blob 对象(二进制大对象),这个 blob 对象就保存着 index.html 文件的内容。我们继续使用 git cat-file 命令来查看该 blob对象。
那么这些对象都存放在什么地方呢?当然是Git版本库中(.git目录)的 objects 目录下了,对象ID的前2位作为目录,后38位最为文件名。
下面的图片展示了Git对象库中各个对象之间的关系
通过 Commit 对象之间的相互关联,可以很容易地识别出一条跟踪链条,这条跟踪链条可以在运行 git log 命令时候通过 --graph选项查看。--pretty=raw参数用于显示每个提交对象的parent属性
最后一个提交没有parent属性,所以跟踪链到此为止,这实际上就是提交的起点。
OK,我们再来看看 HEAD 和 master 。
命令 git branch 是分支管理的主要命令,也可以显示当前所在分支
在 master 分支名称前面的星号(*)表明这个分支是当前工作分支,至于什么是分支,后面会有一节专门介绍分支。
现在连续执行下面的三个命令会看到相同的输出:
1,git log HEAD
2,git log master
3,git log refs/heads/master
这个结果说明了什么呢?
结果表明,在当前的Git版本库中,HEAD , master 和 refs/heads/master 具有相同的指向!!!现在就到Git版本库(.git目录)中看看究竟。
首先,在 .git 目录下找到名字为 HEAD 或者 master 的文件,并查看一下 .git/HEAD 文件的内容:
.git/HEAD文件的内容翻译过来就是:”指向一个引用:refs/heads/master“。这个就是文件 .git/refs/heads/master了,ok,再看一下.git/refs/heads/master文件的内容:
又是一个SHA1哈希值,那么用 git cat-file 命令来查看它:
这个内容是不是很面熟!原来 master 分支指向的是一个最新的提交ID。
这就是Git分支的实现原理:从任何一个提交开始建立一条历史跟踪链条,用一个文件指向这个链条的最新提交,该文件就可以追踪整个提交历史了,这个文件就是 .git/refs/heads/master 。
下面的图标显示了一个完整的真实的版本库结构:
目录 .git/refs 是保存引用的命名空间,其中 .git/refs/heads 目录下的引用又称为分支。
对于分支,即可以使用正规的长格式的表示打:refs/heads/master ,也可以去掉前面的两级目录用 master 来表明。