我对如何计算提交,树和blob的SHA-1哈希感到困惑。根据{{3}},提交哈希值的计算基于以下因素:
树和blob哈希是否也涉及相同的因素?
答案 0 :(得分:9)
Git有时被称为"内容可寻址文件系统"。哈希是地址,它们基于各种对象的内容。因此,为了知道散列基于什么,我们只需要知道各种对象的内容。
blob 只是一个八位字节流。而已。它类似于Unix文件系统中文件内容的概念。
因此,blob的哈希仅基于其内容,blob没有元数据。
树将名称和权限与其他对象(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字符。
在计算散列之后,使用zlib-deflate压缩与包括头部的对象相对应的八位字节流,并且将得到的八位字节流存储在基于散列的文件中;默认情况下在目录
中.git/objects/<first two characters of the hash>/<remaining hash>
上述存储格式称为松散对象格式,因为每个对象都是单独存储的。有一种更有效的存储格式(也用作网络传输格式),称为 packfile 。
Packfiles是一种重要的速度和存储优化,但它们相当复杂,所以我不打算详细描述它们。
作为第一个近似值,packfile包含连接成单个文件的所有未压缩对象和第二个文件,其中包含对象所在的packfile中的位置的索引。然后压缩整个packfile,这样可以获得更好的压缩率,因为算法还可以在对象之间找到的冗余,而不仅仅是 单个对象。 (例如,如果你有两个几乎相同的blob修订版......这在SCM中是一种常态。)
它不使用zlib-deflate,而是使用二进制增量压缩算法。它还使用某些启发式方法来处理如何将对象放在packfile中,以便将可能具有较大相似性的对象紧密地放在一起。 (delta算法实际上不能立即看到整个包文件,这会消耗太多内存,而是在包文件上的滑动窗口上运行;启发式尝试确保类似对象落在同一个文件中窗口。)其中一些启发式方法是:查看树与blob关联的名称,并尝试将具有相同名称的名称保持在一起,尝试将具有相同文件扩展名的名称保持在一起,尝试保持后续修订关闭在一起等等。
松散(即未打包)的对象只是zlib-deflated。取消他们的收缩,只是看看他们是如何构建的。请注意,未压缩的八位字节流正好正在进行哈希处理;对象在压缩之前存储为压缩但经过哈希处理。
Here's a simple Perl one-liner to un-deflate(这是膨胀?)一个流:
perl -MCompress::Zlib -e 'undef $/; print uncompress(<>)'
答案 1 :(得分:2)
我认为理解每种git对象内容的最佳方法是自己探索它们。
您可以使用以下命令轻松完成:
git cat-file -p <a_sha1>
从提交的sha1开始。 你将获得树的sha1s,取一个并始终使用相同的命令来完成一个blob。
您将看到存储在数据库中git对象中的内容的所有时间。
您应该知道的另一件事是内容以对象类型为前缀,内容的长度然后被压缩。