- 提交的源树(展开为所有子树和数据块)
- 父提交SHA1
- 作者信息
- 提交者信息(没错,这些是不同的!)
- 提交消息
Git有时被称为“内容可寻址的文件系统”。哈希值是地址,它们基于各种对象的内容。因此,为了知道哈希值基于什么,我们只需要知道各种对象的内容。
Blob就是一串八位字节流,没有其他东西。类似于Unix文件系统中的文件内容概念。
因此,blob的哈希值仅基于其内容,blob没有元数据。
Tree将名称和权限与其他对象(blob或tree)关联起来。树只是一个由四元组(权限、类型、哈希、名称)
列表组成的集合。例如,一个树可能看起来像这样:
100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README
100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib
注意第三个条目是一个树形结构。
在Unix文件系统中,树形结构类似于一个“目录特殊文件”。
同样,哈希值基于树的内容,这意味着它取决于其叶子节点的名称、权限、类型和哈希值。
提交记录了某个时间点上树形结构的快照以及一些元数据以及快照的生成方式。提交包括:
提交的哈希值基于这些内容计算得出。
与上面的对象不同,标签并不是对象。他们不是对象存储的一部分,也没有哈希值。它们是对象的引用。(注意:不仅限于提交可以被打标签,但这是正常使用情况)。
注释标签不同:它是对象存储的一部分。
一个注释标签包含:
与所有其他对象一样,哈希值是基于它们计算得出的。
签名标签类似于注释标签,但添加了一个加密签名。
笔记允许你将任意提交与任意Git对象关联起来。
笔记的存储方式稍微复杂一些。实际上,一个笔记只是一个提交(包含一个树形结构,其中包含笔记内容的Blob)。Git为笔记创建了一个特殊的分支,笔记提交与其“带注释对象”的关联就发生在那里。我不熟悉具体的细节。
然而,由于笔记只是一个提交,而且关联关系外部产生,所以笔记的哈希值与任何其他提交相同。
存储格式包含一个简单的头信息。实际存储(和哈希)的内容是头信息后跟空字节(NULL octet)再后面是对象内容。
头信息包含对象内容的类型和长度,以ASCII编码表示。因此,包含ASCII编码的字符串“Hello, World”的Blob看起来像这样:
blob 12\0Hello, World
并且那就是被哈希和存储的内容。
其他类型的对象具有更结构化的格式,因此树形对象将以标题tree <content长度(以八进制表示)>\0
开头,后跟严格定义、结构化、序列化的树形表示。
提交也是一样的。
大多数格式都是基于简单ASCII的文本格式。例如,大小不是编码为二进制整数,而是作为十进制整数进行编码,每个数字都用相应的ASCII字符表示。
计算哈希值后,包括标头的字节流使用zlib-deflate进行压缩,并将生成的字节流存储在基于哈希值的文件中;默认情况下存储在目录中。
.git/objects/<first two characters of the hash>/<remaining hash>
上述存储格式称为散装对象格式(loose object format),因为每个对象都被单独存储。还有一种更高效的存储格式(也用作网络传输格式),称为打包文件(packfile)。
打包文件是一个重要的速度和存储优化,但它们相当复杂,因此我不会详细描述它们。
简单来说,打包文件由所有未压缩的对象连接成的单个文件和第二个文件组成,该文件包含索引,指示包文件中各个对象所在的位置。然后整个打包文件被压缩,这样可以获得更好的压缩比,因为算法可以发现对象之间的冗余,而不仅限于单个对象内部的冗余。(例如,如果您有两个几乎相同的 blob 版本,则这是 SCM 中的常态。)
它不使用 zlib-deflate,而是使用二进制 delta 压缩算法。它还使用某些启发式算法来确定如何将对象放置在打包文件中,以便相似性很大的对象彼此靠近。(delta 算法实际上不能完全看到整个打包文件,否则会消耗太多内存,而是在打包文件上滑动一个窗口运行;这些启发式算法试图确保类似的对象落在同一个窗口内。) 其中一些规则是:查看树关联的 blob 的名称,并尝试将具有相同名称的 blob 放置在靠近一起的位置,尝试将拥有相同文件扩展名的对象放置在靠在一起的位置,尝试使后续版本彼此靠近等等。
散装(即未打包)对象只需使用 zlib 解压缩即可。解压后查看它们的结构。请注意,未压缩的字节流 恰好就是被哈希的内容;这些对象在压缩之前被存储为压缩格式并进行了哈希。
这里有一个简单的 Perl 单行命令可以对流进行解压缩(也称作膨胀):
perl -MCompress::Zlib -e 'undef $/; print uncompress(<>)'
refs/notes/commits
指向链中最顶部的提交。与此最顶部提交相关联的树有一个文件对应每个具有笔记的提交:例如,如果有一个针对提交 badf00...
的注释,则在“尖端注释”中会有一个名为 ba/df00...
或 ba/df/00...
等文件。该文件(一个 blob)包含了提交 badf00...
的注释。这里的扇出度取决于被注释的提交数量。 - torek我认为了解每种git对象的最佳方法是自己探索。
您可以使用以下命令轻松完成:
git cat-file -p <a_sha1>
从一个提交的sha1开始。你会得到树的sha1,取其中之一并应用相同的命令一直向下,以blob结尾。
每次都会看到存储在git对象数据库中的内容。
你需要知道的唯一其他事项是,内容由对象类型、内容长度前缀和压缩组成。