【问题标题】:How hash is calculated for commit vs tree vs blobs?如何计算提交、树和 blob 的哈希值?
【发布时间】:2017-07-02 14:13:36
【问题描述】:

我对如何为提交、树和 blob 计算 SHA-1 哈希感到困惑。根据this article,提交哈希的计算基于以下因素:

  1. 提交的源树(分解为所有子树和 blob)
  2. 父提交 sha1
  3. 作者信息
  4. 提交者信息(对,它们是不同的!)
  5. 提交信息

树和 blob 哈希也涉及相同的因素吗?

【问题讨论】:

  • 没有。 Blob 没有树、父提交、作者、提交者或消息。树没有父提交、作者、提交者或消息。
  • 扮演魔鬼的拥护者,假设使用的算法使得冲突发生的可能性不大,为什么对您而言如何计算 SHA-1 哈希值很重要?
  • @Ryan Trees 确实有树(目录)和 blob,但没有父级、作者、提交者或消息。
  • @TimBiegeleisen 真正了解 git 命令如何实际影响对象存储对我来说很重要。恕我直言,如果一个人不知道事物是如何形成的,那么你真的不能放心,至少我不知道。
  • @appu:复制粘贴出错,抱歉。

标签: git hash


【解决方案1】:

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(<>)'

【讨论】:

  • 一个小的更正:注释存储为提交,包含包含文件的树。 refs/notes/commits 指向此类提交链的最顶端。与这个最尖端的提交相关联的树每个提交都有一个文件-that-has-a-note:如果有一个提交注释badf00...,例如,有一个名为ba/df00...ba/df/00...的文件或在“提示说明”中类似。该文件(一个 blob)包含提交 badf00... 的注释。这里的扇出程度取决于记录提交的数量。
  • 标签不需要指向提交。可以标记任何 Git 对象。
【解决方案2】:

我认为理解每一种 git 对象的内容最好的方式就是自己去探索。

您可以使用以下命令轻松完成:

git cat-file -p <a_sha1>

从提交的 sha1 开始。 您将获得树的 sha1s,取一个并始终应用相同的命令一直向下以完成一个 blob。

您每次都会看到存储在数据库中的 git 对象中的内容。

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

【讨论】:

    猜你喜欢
    • 2019-05-31
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 2016-10-17
    • 2022-01-04
    • 2014-04-01
    • 2016-06-23
    • 2011-12-19
    相关资源
    最近更新 更多