git学习之一:内部对象工作原理

浏览: 61 发布日期: 2016-11-22 分类: git

版本控制在于文件的控制,git的控制方法在于为每个文件生成(key,object)的结构。git利用sha-1加密算法,对每一个文件生成一个唯一的字符序列(明文大小不超过2^64位,对于普通文件,这个大小都可以满足)作为hash_key。对于sha-1算法,明文(对于git来讲,就是我们的文件内容)不变,其sha-1值不会改变,所以只要文件改变,就会生成一对新的(key,object)[对于key,当中还有一些细微的处理,不仅仅只是sha-1算法,这里我们只是需要理解,git为一个文件生成了一个唯一的key]。

使用git init初始化一个本地仓库,打开隐藏目录.git,其内容如下图。可以看到一个objects的目录,里面只有info和pack两个空文件夹。初始化的时候不存在任何object,也就是没有任何文件被记录下来。

1.blob对象

我们在工作目录下添加一个文件file_1.txt,里面只要一个字符串"file1 content",使用git hash-object [文件名],可以查看其经过算法生成的hash-key.这个一个40个字符长度的序列。其序列为d9039017ab6c958678a334446aadfe5047266027

这个时候object目录下还是空,使用git add file_1.txt之后,object里会多一个对象,下面详细来解析这个对象。首先看看object目录发生了什么事情

多了一个d9的目录


d9目录下

可以看到40位的hash-key 前两位作为目录名,后38位作为文件名,标识了这个object对象,这个对象里面的内容就是刚才file_1.txt里的内容,可以查看这个对象的内容和对象类型:

git cat-file -p [hash-key] 可以查看已经存在的object对象内容

git cat-file -t [hash-key] 可以查看已经存在的object对象类型

git object有四种类型,这是目前我们接触到的第一种类型blob,用来储存文件类容,它的具体内容就是刚刚新建的txt里的字符串file1 content。


2.tree对象

blob对应文件的内容,tree对象可以理解为目录,它的树节点信息包含文件名,hash-key,文件类型、权限等等。这样就可以组织整个需要控制文件的结构

下面我们再往工作目录下添加一个目录dir_1,在dir_1添加一个文件1.txt,类容为"1.txt content"。使用git add 将内容加入到暂存区(也称index,目前不是本文的重点,会在后续章节中详解)。使用git hash-object来查看生成的key值。

对于文件可以看到生成了hash-key,但是对于目录很明显没有达到预期的效果。我们看看object目录

只存在一个6d的目录,也就是6dc2bcda0c359c6dbb917dec90ca4a8d078ff789对应的1.txt文件,这时我们的目录并没有生成tree对象,tree对象是在commit的过程中生成的,其生成会根据.git目录下的index文件的内容来创建。git add的操作就是将文件的信息保存到index文件中,在commit时,根据index的内容来生成tree对象。

使用git ls-files --stage命令,我们看看index里的类容

可以看到index包含了创建tree对象的信息 文件类型(100644),ash-key,目录结构和文件名。

下面我们进行第一次commit,生成commit对象,同时生成tree对象。我们这里具体看看tree对象,master是分支名,master^{tree},表示master分支所指向的tree对象。

可以看到这个tree对象是我们的工作目录,目录下还有一个dir_1的tree对象,和file_1的blob对象,下面看看dir_1对应的tree对象的内容,这个tree对象只包含1.txt的信息。

目前我们的git仓库的内部结构如下:

3.commit对象

介绍tree对象时,提到过commit,只有在commit的时候,才会根据index记录的内容生成tree对象,那么commit对象里只有两个类容:1.代表工作目录的tree对象的key,上一个commit的key。

现在看看我们的object目录:

目前我们的对象数量还不多,每个目录里有一个对象,就是五个对象,刚才的总体tree图,只包含了四个对象,我们使用git log查看commit的历史

90对应的文件夹里面的文件就是我们的commit对象,它指向工作目录tree,和上一次的commit,这是第一个commit ,所以上一个commit不存在。

对象类型为commit 内容指向工作目录tree,所以能获取到一个commit,就可以完整得到当前的文件状况,现在我们的完整的object图如下:

现在我们在工作加入一个新的目录dir_2,和该目录下文件2.txt,内容为"content 2",在add和commit之后,我们在看看新的commit的信息

的commit指向了上一个commit,还指向了一个新生成的tree,这个tree表示了新的工作目录情况,看看这tree的类容:

这个tree包含了当前的文件目录和内容,现在我们的对象完整的图如下:

可以看到commit对象指向了工作目录tree,这样只要切换commit,就可以随意切换我们的版本类容。现有有9个object对象了,我们来看看.git/objects目录,对象还很少,没有在出现同一目录下的两个文件的情况。


上述三种对象:blob(记录文件内容),tree(目录结构),commit(工作目录tree,提交历史),git版本控制围绕这三类对象展开。



小结:本文通过讲解git内部的三种对象来解释git的内部工作机制,对于熟悉git使用的人,可以帮助其深入理解。对于版本控制的初学者,只需要了解大概,可以在了解其他章节之后再进一步理解




返回顶部