如何为commit vs tree vs blobs计算哈希值?

时间:2017-07-02 14:13:36

标签: git hash

我对如何计算提交,树和blob的SHA-1哈希感到困惑。根据{{​​3}},提交哈希值的计算基于以下因素:

  1. 提交的源代码树(对所有子树和blob进行解析)
  2. 父提交sha1
  3. 作者信息
  4. 提交者信息(对,那些不同!)
  5. 提交消息
  6. 树和blob哈希是否也涉及相同的因素?

2 个答案:

答案 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对象中的内容的所有时间。

您应该知道的另一件事是内容以对象类型为前缀,内容的长度然后被压缩。