原文: 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开头的所有的对象中SHA1值最小的对象在offset/sha1表中的偏移;fanout[1]表示的是SHA1值以0x01开头的所有的对象中SHA1值最小的对象在offset/sha1表中的偏移;fanout[2]表示的是SHA1值以0x02开头的所有的对象中SHA1值最小的对象在offset/sha1表中的偏移。以此类推,fanout[254]表示的是SHA1值以0xfe开头的所有的对象中SHA1值最小的对象在offset/sha1表中的偏移,fanout[255]表示的是SHA1值以0xff开头的所有的对象中SHA1值最大的对象在offset/sha1表中的偏移,它同时也表示了当前offset/sha1表的大小。
因此通过SHA1值查找一个对象时,首先通过SHA1值的前两位在fanout表中,确定它在offset/sha1表的一个区间范围,然后再在sha1表中根据SHA1值使用二分法进行查找。这样通过fanout表对于最坏的情况就减少了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程序读出打包文件的头部:
def read_pack_header
  sig = @session.recv(4)
  ver = @session.recv(4).unpack("N")[0]
  entries = @session.recv(4).unpack("N")[0]
  [sig, ver, entries]
end
头部之后是一系列按照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, 等等).

最新文章

  1. 两台win7电脑网线直连办法(共享文件夹形式)
  2. SQL SERVER中如何在声明游标的语句中,用变量做表名
  3. 【小梅哥FPGA进阶学习之旅】基于Altera FPGA 的DDR2+千兆以太网电路设计
  4. Xcode 7.2.1 下载地址
  5. Python基本语法[二],python入门到精通[四]
  6. ios 编码转换 保存文件
  7. Python练习题 028:求3*3矩阵对角线数字之和
  8. 分享一张oracle scan图
  9. vector迭代器失效的一种情形
  10. Dynamics CRM2013 Odata的filter中含有日期字段时遇到的一个奇葩问题
  11. Thunk
  12. VIM编辑器用法
  13. Amundsen — Lyft’s data discovery & metadata engine
  14. eclipse常用快捷键整理
  15. 【bzoj2229】 Zjoi2011—最小割
  16. Python+Selenium笔记(四):unittest的Test Suite(测试套件)
  17. chrome浏览器使用
  18. 30道Spring面试题和答案
  19. Linq多字段排序
  20. PHP Mailer 发送邮件

热门文章

  1. Unity 播放 视频
  2. Sphinx 2.2.6 window下安装全过程 未完 持续标记~~~~
  3. Javascript验证手机号码正则表达式
  4. WinSCP 连接 Ubuntu 拒绝的问题
  5. [译]Node.js : Building RESTful APIs using Loopback and MySQL
  6. Java-开启Java之路
  7. 同一个解决方案"引用"其他的项目出现感叹号...
  8. Python 集合set添加删除、交集、并集、集合操作符号
  9. C 文件读写 容易疏忽的一个问题
  10. zabbix 3.0.4 监控windows 服务