Git 有时被称为“内容可寻址文件系统”。哈希是地址,它们基于各种对象的内容。所以,要想知道哈希是基于什么的,我们只需要知道各个对象的内容即可。
斑点
blob 只是一个八位字节流。而已。它类似于 Unix 文件系统中的文件内容的概念。
因此,blob 的哈希仅基于其内容,blob 没有元数据。
树木
tree 将名称和权限与其他对象(blob 或树)相关联。一棵树只是一个四元组(permission, type, hash, name) 的列表。例如,一棵树可能如下所示:
100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README
100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib
注意第三个条目,它本身就是一棵树。
树类似于 Unix 文件系统中的目录特殊文件。
同样,哈希基于树的内容,这意味着它的叶子的名称、权限、类型和哈希。
提交
commit 会及时记录树的快照以及一些元数据以及快照是如何形成的。提交包括:
- (任意数量)父提交的哈希列表(包括零)
- 树的哈希
- 提交消息
- 提交元数据(提交日期和提交者名称)
- 创作元数据(创作日期和作者姓名)
提交的哈希值基于这些。
标签
标签 不是上述意义上的对象。它们不是对象存储的一部分,也没有散列。它们是对象的引用。 (注意:可以标记任何对象,而不仅仅是提交,尽管这是正常的用例。)
带注释的标签
一个带注释的标签是不同的:它是对象存储的一部分。
带注释的标签存储:
- 提交的哈希
- 标签消息
- 标记元数据(标记名称和标记日期)
与所有其他对象一样,哈希是基于所有对象计算的,仅此而已。
签名标签
签名标签类似于带注释的标签,但添加了加密签名。
注意事项
Notes 允许您将任意提交与任意 Git 对象相关联。
笔记的存储有点复杂。实际上,注释只是一个提交(包含一棵树,其中包含包含注释内容的 blob)。 Git 为笔记创建了一个特殊的分支,并且笔记提交和它的“注释对象”之间的关联发生在那里。具体方法我不知道。
但是,由于注释只是一个提交,并且关联发生在外部,因此注释的哈希值与任何其他提交相同。
存储格式
存储格式包含一个简单的标题。实际存储(和散列)的内容是标头,后跟一个 NULL 八位字节,然后是对象内容。
标头包含对象内容的类型和长度,以 ASCII 编码。因此,包含以 ASCII 编码的字符串 Hello, World 的 blob 将如下所示:
blob 12\0Hello, World
而那是被散列和存储的。
其他类型的对象具有更结构化的格式,因此树对象会以标题 tree <length of content in octets>\0 开头,后跟严格定义的、结构化的、序列化的树表示。
提交也是如此,以此类推。
大多数格式都是文本格式,基于简单的 ASCII。例如,大小不是编码为二进制整数,而是编码为十进制整数,每个数字都表示为对应的 ASCII 字符。
压缩
计算出hash后,使用zlib-deflate对包含header的对象对应的八位字节流进行压缩,得到的八位字节流根据哈希值存储在一个文件中;默认在目录中
.git/objects/<first two characters of the hash>/<remaining hash>
包
上述存储格式称为松散对象格式,因为每个对象都是单独存储的。还有一种更高效的存储格式(也用作网络传输格式),称为packfile。
Packfiles 是一个重要的速度和存储优化,但它们相当复杂,所以我不打算详细描述它们。
作为第一个近似值,一个包文件由所有未压缩的对象组成,这些对象连接成一个文件和一个第二个文件,该文件包含一个对象所在包文件中位置的索引。然后将整个包文件压缩,从而获得更好的压缩率,因为该算法还可以找到 个对象之间的冗余,而不仅仅是 个单个对象。 (例如,如果您有两个几乎相同的 blob 修订版……这是 SCM 中的一种规范。)
它不使用 zlib-deflate,而是使用二进制增量压缩算法。它还使用某些启发式方法来将对象放置在包文件中,以便将可能具有较大相似性的对象紧密放置在一起。 (delta 算法实际上不能一次看到 整个 包文件,这会消耗太多内存,而是在包文件上的滑动窗口上运行;启发式算法试图确保相似的对象落在同一个窗口。)其中一些启发式方法是:查看与 blob 关联的树的名称,并尝试将具有相同名称的名称保持在一起,尝试将具有相同文件扩展名的名称保持在一起,尝试使后续修订保持紧密一起等等。
四处逛逛
松散(即未打包)对象只是 zlib-deflate。对它们进行放气,然后查看它们以了解它们的结构。请注意,未压缩的八位字节流正是被散列的内容;对象被压缩存储,但在压缩之前经过哈希处理。
Here's a simple Perl one-liner to un-deflate(这是膨胀吗?)流:
perl -MCompress::Zlib -e 'undef $/; print uncompress(<>)'