第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
现在来看看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的内容:
目录.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是如何生成的呢?