Git 对象存储

之前我提到当存储数据内容时,同时会有一个文件头被存储起来。我们花些时间来看看 Git 是如何存储对象的。你将看来如何通过 Ruby 脚本语言存储一个 blob 对象 (这里以字符串 “what is up, doc?” 为例) 。使用irb 命令进入 Ruby 交互式模式:

[code]$ irb

content = “what is up, doc?”
=> “what is up, doc?”[/code]

Git 以对象类型为起始内容构造一个文件头,本例中是一个 blob。然后添加一个空格,接着是数据内容的长度,最后是一个空字节 (null byte):

>> header = "blob #{content.length}\0" => "blob 16\000"

Git 将文件头与原始数据内容拼接起来,并计算拼接后的新内容的 SHA-1 校验和。可以在 Ruby 中使用require 语句导入 SHA1 digest 库,然后调用 Digest::SHA1.hexdigest() 方法计算字符串的 SHA-1 值:

[code]>> store = header + content
=> “blob 16\000what is up, doc?”

require ‘digest/sha1’
=> true
sha1 = Digest::SHA1.hexdigest(store)
=> “bd9dbf5aae1a3862dd1526723246b20206e5fc37”[/code]

Git 用 zlib 对数据内容进行压缩,在 Ruby 中可以用 zlib 库来实现。首先需要导入该库,然后用Zlib::Deflate.deflate() 对数据进行压缩:

[code]>> require ‘zlib’
=> true

zlib_content = Zlib::Deflate.deflate(store)
=> “x\234K\312\311OR04c(\317H,Q\310,V(-\320QH\311O\266\a\000_\034\a\235”[/code]

最后将用 zlib 压缩后的内容写入磁盘。需要指定保存对象的路径 (SHA-1 值的头两个字符作为子目录名称,剩余 38 个字符作为文件名保存至该子目录中)。在 Ruby 中,如果子目录不存在可以用FileUtils.mkdir_p() 函数创建它。接着用 File.open 方法打开文件,并用 write() 方法将之前压缩的内容写入该文件:

[code]>> path = ‘.git/objects/’ + sha1[0,2] + ‘/’ + sha1[2,38]
=> “.git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37”

require ‘fileutils’
=> true
FileUtils.mkdir_p(File.dirname(path))
=> “.git/objects/bd”
File.open(path, ‘w’) { |f| f.write zlib_content }
=> 32
[/code]
这就行了 ── 你已经创建了一个正确的 blob 对象。所有的 Git 对象都以这种方式存储,惟一的区别是类型不同 ── 除了字符串 blob,文件头起始内容还可以是 commit 或 tree 。不过虽然 blob 几乎可以是任意内容,commit 和 tree 的数据却是有固定格式的。

REF:http://cwiki.ossez.com/pages/viewpage.action?pageId=7045952