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