第6章 Git对象

我们在上一章学习了Git的一个最重要的概念:暂存区。暂存区是一个介于工作区和版本库的中间状态,当执行提交时,实际上是将暂存区的内容提交到版本库中,而且Git的很多命令都会涉及暂存区的概念,例如git diff命令。

上一章也留下了很多疑惑,例如什么是HEAD?什么是master?为什么它们二者(在上一章)可以相互替换使用?为什么Git中的很多对象(如提交、树、文件内容等)都用40位的SHA1哈希值来表示?本章将会揭开这些奥秘,并且还会画出一个更为精确的版本库结构图。

6.1 Git对象库探秘

前面刻意回避了对提交ID的说明,现在是时候来揭开由40位十六进制数字组成的“魔幻数字”的奥秘了。

通过查看日志的详尽输出,我们会惊讶地看到非常多的“魔幻数字”,这些“魔幻数字”实际上是SHA1哈希值。


$git log-1—pretty=raw

commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86

tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9

parent a0c641e92b10d8bcca1ed1bf84ca80340fdefee6

author Jiang Xin<jiangxin@ossxp.com>1291022581+0800

committer Jiang Xin<jiangxin@ossxp.com>1291022581+0800

which version checked in?


一个提交中居然包含了三个SHA1哈希值表示的对象ID:

commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86:这是本次提交的唯一标识。

tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9:这是本次提交所对应的目录树。

parent a0c641e92b10d8bcca1ed1bf84ca80340fdefee6:这是本地提交的父提交(上一次提交)。

研究Git对象ID的一个重量级武器就是git cat-file命令。用下面的命令可以查看一下这三个ID的类型。


$git cat-file-t e695606

commit

$git cat-file-t f58d

tree

$git cat-file-t a0c6

commit


在引用对象ID的时候,没有必要把整个的40位ID写全,只要从头开始的几位不冲突即可。下面再用git cat-file命令查看一下这几个对象的内容。

commit对象 e695606fc5e31b2ff9038a48a3d363f4c21a3d86


$git cat-file-p e695606

tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9

parent a0c641e92b10d8bcca1ed1bf84ca80340fdefee6

author Jiang Xin<jiangxin@ossxp.com>1291022581+0800

committer Jiang Xin<jiangxin@ossxp.com>1291022581+0800

which version checked in?


tree对象 f58da9a820e3fd9d84ab2ca2f1b467ac265038f9


$git cat-file-p f58da9a

100644 blob fd3c069c1de4f4bc9b15940f490aeb48852f3c42 welcome.txt


commit对象 a0c641e92b10d8bcca1ed1bf84ca80340fdefee6


$git cat-file-p a0c641e

tree 190d840dd3d8fa319bdec6b8112b0957be7ee769

parent 9e8a761ff9dd343a1380032884f488a2422c495a

author Jiang Xin<jiangxin@ossxp.com>1290999606+0800

committer Jiang Xin<jiangxin@ossxp.com>1290999606+0800

who does commit?


在上面的目录树(tree)对象中看到了一个新类型的对象:blob对象,这个对象保存着文件welcome.txt的内容,我们用git cat-file研究一下。

该对象的类型为blob。


$git cat-file-t fd3c069c1de4f4bc9b15940f490aeb48852f3c42

blob


该对象的内容就是welcome.txt文件的内容。


$git cat-file-p fd3c069c1de4f4bc9b15940f490aeb48852f3c42

Hello.

Nice to meet you.


这些对象保存在哪里?当然是Git库中的objects目录下了(ID的前2位作为目录名,后38位作为文件名)。用下面的命令可以看到这些对象在对象库中的实际位置。


$for id in e695606 f58da9a a0c641e fd3c069;do\

ls.git/objects/${id:0:2}/${id:2}*;done

.git/objects/e6/95606fc5e31b2ff9038a48a3d363f4c21a3d86

.git/objects/f5/8da9a820e3fd9d84ab2ca2f1b467ac265038f9

.git/objects/a0/c641e92b10d8bcca1ed1bf84ca80340fdefee6


第6章 Git对象 - 图1

第6章 Git对象 - 图2

现在来看看HEAD和master的奥秘吧。

因为在上一章的最后执行了git stash命令来将工作区和暂存区的改动全部封存起来,所以执行下面的命令会看到工作区和暂存区中没有改动。


$git status-s-b

master


说明 上面在显示工作区状态时,除了使用了-s参数以显示精简输出外,还使用了-b参数,以便能够同时显示出当前工作分支的名称,这个-b参数是在Git 1.7.2以后加入的新参数。

下面的git branch是分支管理的主要命令,也可以显示当前的工作分支。


$git branch

*master


在master分支名称前的星号表明这个分支是当前工作分支。至于为什么没有其他分支,以及什么叫分支,会在本书后面的章节中讲解。

现在连续执行下面的三个命令会看到相同的输出:


$git log-1 HEAD

commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86

Author:Jiang Xin<jiangxin@ossxp.com>

Date:Mon Nov 29 17:23:01 2010+0800

which version checked in?

$git log-1 master

commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86

Author:Jiang Xin<jiangxin@ossxp.com>

Date:Mon Nov 29 17:23:01 2010+0800

which version checked in?

$git log-1 refs/heads/master

commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86

Author:Jiang Xin<jiangxin@ossxp.com>

Date:Mon Nov 29 17:23:01 2010+0800

which version checked in?


也就是说,在当前版本库中,HEAD、master和refs/heads/master具有相同的指向。现在到版本库(.git目录)中一探它们的究竟。


$find.git-name HEAD-o-name master

.git/HEAD

.git/logs/HEAD

.git/logs/refs/heads/master

.git/refs/heads/master


找到了4个文件,其中在.git/logs目录下的文件稍后再予以讨论,现在把目光锁定在.git/HEAD和.git/refs/heads/master上。

显示一下.git/HEAD的内容:

第6章 Git对象 - 图3

第6章 Git对象 - 图4

目录.git/refs是保存引用的命名空间,其中.git/refs/heads目录下的引用又称为分支。对于分支,既可以使用正规的长格式的表示法,如refs/heads/master,也可以去掉前面的两级目录用master来表示。Git有一个底层命令git rev-parse可以用于显示引用对应的提交ID。


$git rev-parse master

e695606fc5e31b2ff9038a48a3d363f4c21a3d86

$git rev-parse refs/heads/master

e695606fc5e31b2ff9038a48a3d363f4c21a3d86

$git rev-parse HEAD

e695606fc5e31b2ff9038a48a3d363f4c21a3d86


可以看出它们都指向同一个对象。为什么这个对象是40位,而不是更少或更多?这些ID是如何生成的呢?