Xudifsd Rational life

Git源码阅读——对象数据库

2011-09-21
xudifsd
git

git其实就是一个CAS(Content-addressable storage)系统,要想取得一个对象就必须提供该对象的哈希值而非对象的路径,这样特性的实现方法就是使用对象的哈希值作为对象的路径。git使用的哈希算法是sha1算法。git的对象数据库还有一个属性就是所有的对象只能添加、读取,不能删除、修改,这样保证了整个代码历史的完整性。

git对象文件

所有git对象都保存在.git/objects中,例如

.git/objects/03/7dc5e0f2cd4e0f4ff2385843beab28170c31f5

表示一个对象,.git/objects目录下的目录名03是对象sha1值的前两位,文件名则是剩余的sha1值,这样类似于数据桶的结构可以让打开文件更快。由于git不保存文件的差别而是保存整个文件,所以对于一个只修改了几行的文件git也会保存两份几乎完全相同的内容,当跟踪的历史变长时数据冗余也会越来越多,这样的对象叫松散对象。不过git可以对所有对象进行打包,保存每个文件的不同,这样的动作叫垃圾收集,git会在每次进行网络传输或运行git gc时进行。

git中所有对象都有一个公共的文件头,格式是:

type size\0

其中type是对象对应的类型,而size则是整数的字符串表示,表示对象内容的长度,’\0’之后的内容就是各个对象的内容。

读取操作

在git中我们只能通过20个字节的sha1哈希值来取得文件的内容,在代码中可以使用

read_sha1_file(sha1, type, &size)

来进行读取,参数type和size分别用于存从文件头中读出的对象类型和内容大小。但是read_sha1_file并不仅仅只是打开,解压再读取,由于可能对象已经被打包,read_sha1_file也包括了unpack之类的函数用来解析打包后的文件。

写操作

我们通过

write_sha1_file(buffer, buffer_len, type, return_sha1)

来进行写操作,其中buffer是对象的内容,buffer_len是内容的大小,type是对象的类型,最后通过return_sha1来接收刚刚写入对象的sha1值。需要注意的是写操作的实现:由于I/O可能会被中断,如果直接创建sha1文件,一旦中断会导致麻烦的数据不完整,而产生硬连接的link则很难被打断,所以为了最大限度地保证数据完整性,git使用临时文件:先调用mkstemp产生一个临时文件,并将内容写入,成功之后会调用link产生一个文件名为对应sha1值的指向临时文件的硬连接,再unlink临时文件。

对象的属性

git共有四种类型的对象:blob,tree,commit和tag。

blob是最基本的对象,不能引用任何其他对象,它类似于文件系统中的文件,但与文件不同的是它自身没有任何文件名,它所包含的信息只有文件内容。这样的文件名和文件内容的分离可以实现对象的重用:如果你将一个文件加到git,然后对文件名进行修改但是没有修改文件内容,之后再将其加到git,这样并不会产生另一个blob对象,修改的只是tree对象中的引用名而已。

tree对象的内容包括对其他tree对象或blob对象的引用和对应对象的名字及模式,这样的属性让tree非常类似于文件系统中的目录,tree引用tree类似于目录中的子目录,tree引用blob类似于目录中的文件。

commit对象可以引用一个tree对象和零个或多个commit对象,commit引用的tree对象表示整个项目的根目录,而被引用的commit对象表示父commit,如果没有引用其他的commit,则表示该commit对象是整个项目的初次提交,而多个commit引用则表示由多个commit合并而来。

tag对象可以引用任意类型的一个对象,它的作用就是给某个对象一个方便人们记忆的名字,比如可以对一个项目某个commit对象标为v1.0来表示这个项目的1.0版本,也可以将存有项目公钥的blob对象打上标签。


Comments

Content