Git打包文件


原文: http://gitbook.liuhui998.com/7_5.html
一、打包文件索引
首先, 我们来看一下打包文件索引, 基本上它只是一系列指向打包文件内位置的书签.
打包文件索引有两个版本.
 版本1 的格式用于Git 1.6版本之前, 版本2的格式用于Git 1.6及以后的版本. 
但是版本2可以被Git 1.5.2及以上的Git读取, 同时也被后向移植(backport)到了1.4.4.5版本.
版本2 包含了每个对象的 CRC校验值 , 因此在重打包的过程中, 压缩过的对象可以直接进行包间拷贝(from pack to pack)而不用担心数据损坏. 版本2的打包文件索引同时亦支持 大于4G的打包文件 .

  
在两个版本格式中,  sha1表 存储的是对象的SHA1值,并把它们按照其SHA1值进行排序(以便于对这个表进行二分搜索),   offset表 存储的是 sha1表 中对应位置的对象在打包文件中的偏移值。
为了加速对象的查找,git使用了分段的思想,在打包文件中包含了一个 fanout表 fanout表 用一种特殊的方法指向 offset/sha1 表。
简单的说应该是这样的, fanout[ 0 ] 表示的是 SHA1值 0x00 开头的 所有的对象中 S HA1 最小的对象在 offset/sha1 表中的偏移; fanout[1] 表示的是 SHA1值 0x01 开头的 所有的对象中 SHA1 值最小的对象在 offset/sha1 表中的偏移; fanout[2] 表示的是 SHA1值 0x02 开头的 所有的对象中 SHA1 值最小的对象在 offset/sha1 表中的偏移。以此类推, f anout[254] 表示的是 SHA1值 0xfe 开头的 所有的对象中 SHA1 值最小的对象在 offset/sha1 表中的偏移, f anout[255] 表示的是 SHA1值 0xff 开头的 所有的对象中 SHA1 值最大的对象在 offset/sha1 表中的偏移,它同时也表示了当前 offset/sha1 的大小。
因此通过SHA1值查找一个对象时,首先通过 SHA1值 前两位在 f anout表 中,确定它在 offset/sha1表 的一个区间范围,然后再在 sha1表 中根据 SHA1值 使用二分法进行查找。这样通过 f anout表 对于最坏的情况就减少了 8次 二分搜索 迭代

在第1版中,  offset(偏移) SHA值 存在在同一位置. 但是在第2版中, SHA值, CRC值和offset被放在不同的表中. 两个版本的文件最后都是索引文件以及指向的打包文件的CRC校验值.

很重要的一点是, 要从打包文件中提取(extract)出一个对象, 索引文件不是必不可少的. 索引文件的作用是帮助用户快速地从打包文件中提取对象. 那些"上传打包"(upload-pack)和"取回打包"(receive-pack)程序(译注: 实现push和fetch协议的程序)使用打包文件格式(packfile format)去传输对象, 但是没有使用索引 .因为 索引可以在上传或者取回打包文件之后通过扫描打包文件重新建立.
二、打包文件格式
打包文件格式是很简单的. 它有一个头部(header)和一系列打包过的对象(每个都有自己的header和body), 还有一个校验尾部(trailer). 前4个字节是字符串'PACK', 它用于确保你找到了打包文件的起始位置. 紧接着是4个字节的打包文件版本号, 之后的4个字节指出了此文件中入口(entry)的个数. 你可以用下面Ruby程序读出打包文件的头部:
<p style="margin-top: 0px; margin-bottom: 10px; padding-top: 0px; padding-bottom: 0px;"></p><div><span class="kwd" style="color: rgb(0, 0, 136);">def</span><span class="pln"> read_pack_header</span></div><div><span class="pln">  sig </span><span class="pun" style="color: rgb(102, 102, 0);">=</span><span class="pln"> </span><span class="lit" style="color: rgb(0, 102, 102);">@session</span><span class="pun" style="color: rgb(102, 102, 0);">.</span><span class="pln">recv</span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="lit" style="color: rgb(0, 102, 102);">4</span><span class="pun" style="color: rgb(102, 102, 0);">)</span></div><div><span class="pln">  ver </span><span class="pun" style="color: rgb(102, 102, 0);">=</span><span class="pln"> </span><span class="lit" style="color: rgb(0, 102, 102);">@session</span><span class="pun" style="color: rgb(102, 102, 0);">.</span><span class="pln">recv</span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="lit" style="color: rgb(0, 102, 102);">4</span><span class="pun" style="color: rgb(102, 102, 0);">).</span><span class="pln">unpack</span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="str" style="color: rgb(0, 136, 0);">"N"</span><span class="pun" style="color: rgb(102, 102, 0);">)[</span><span class="lit" style="color: rgb(0, 102, 102);">0</span><span class="pun" style="color: rgb(102, 102, 0);">]</span></div><div><span class="pln">  entries </span><span class="pun" style="color: rgb(102, 102, 0);">=</span><span class="pln"> </span><span class="lit" style="color: rgb(0, 102, 102);">@session</span><span class="pun" style="color: rgb(102, 102, 0);">.</span><span class="pln">recv</span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="lit" style="color: rgb(0, 102, 102);">4</span><span class="pun" style="color: rgb(102, 102, 0);">).</span><span class="pln">unpack</span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="str" style="color: rgb(0, 136, 0);">"N"</span><span class="pun" style="color: rgb(102, 102, 0);">)[</span><span class="lit" style="color: rgb(0, 102, 102);">0</span><span class="pun" style="color: rgb(102, 102, 0);">]</span></div><div><span class="pln">  </span><span class="pun" style="color: rgb(102, 102, 0);">[</span><span class="pln">sig</span><span class="pun" style="color: rgb(102, 102, 0);">,</span><span class="pln"> ver</span><span class="pun" style="color: rgb(102, 102, 0);">,</span><span class="pln"> entries</span><span class="pun" style="color: rgb(102, 102, 0);">]</span></div><div><span class="kwd" style="color: rgb(0, 0, 136);">end</span></div><p style="margin-top: 0px; margin-bottom: 10px; padding-top: 0px; padding-bottom: 0px;"></p>
头部 之后是一系列按照SHA值排序的打包对象, 每一个打包对象包含了头部和内容.  打包文件的尾部 是该文件中所有(已排序)SHA值的SHA1校验值(20字节长)(译注: 即按照排序好的顺序进行迭代SHA1运算).

 
  对象头部 (object header)由1个或以上的字节按序组成, 它指出了后面所跟数据的类型及展开后的尺寸. 头部的每一个字节有 7 位用于数据, 第 1 位用于说明头部是否还有后续字节. 如果第1位是'1', 你需要再读入1个字节(译注: 即下一字节仍属于头部), 否则下一字节就是数据. 第一个字节的前3位指定了数据的类型, 具体含义参见下表.

(3个位可以组合成为8个数. 在当前的使用中, 0(000)是'未定义', 5(101)目前未被使用.)

这里我们举一个由两个字节组成的头部的例子. 第1个字节的前3位说明了数据的类型是提交(commit), 余下的4位和第2个字节的7位组成的数字是144, 说明数据展开后的长度是144字节.

 

值得注意的一点是, 对象头部中包含的'尺寸'不是后面跟着的数据的长度, 而是 数据展开之后的长度 . 因此, 打包索引文件中的偏移是很有用的, 有了它你不必展开每一个对象就可以得到下一个头部的起始位置.
对于非delta对象, 数据部分就只是zlib压缩后的数据流. 对于那两种delta对象, 数据部分包含了它所依赖的基对象(base object)以及用于重构对象的delta(差异)数据. 数据的前20个字节称为ref-delta, 它是基对象SHA值的前20个字节. ofs-delta存储了基对象在同一打包文件中的偏移. 任何情况下, 有两个约束必须严格遵守:
1、 delta对象和基对象必须位于同一打包文件;
2、 delta对象和基对象的类型必须一致(即tree对tree, blob对blob, 等等).
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值