在Git中,树哈希值存储在哪里?

6
我可以帮助您翻译这段内容。这是关于 Git 架构的教程,您正在学习它(https://jwiegley.github.io/git-from-the-bottom-up/1-Repository/3-blobs-are-stored-in-trees.html)。下面是指令:
$ git cat-file commit HEAD

为我提供了由HEAD引用的树的哈希值,"0563f77d884e4f79ce95117e2d686d7d6e282887"。现在,我尝试在.git中找到这个哈希值:

$ find .git/ | xargs grep "0563f77"

为什么没有任何结果?这个哈希值没有被存储在任何地方吗?

它适用于由$ git rev-parse HEAD返回的提交哈希。该哈希存储在.git/refs/heads/master.git/logs/refs/heads/master.git/logs/HEAD中。 - Joshua Meyers
请参阅以下链接,了解底层数据结构的概述:https://dev59.com/v2Ag5IYBdhLWcg3w9e_m 和 https://dev59.com/S2Uq5IYBdhLWcg3wF8rw。 - Ciro Santilli OurBigBook.com
3个回答

7

我认为你在这里混淆了几个概念:

Git内部对象的名称(SHA-1哈希值)是唯一的,并且完全由对象的内容决定(因此在哲学意义上说,它们就是对象本身)。更正确地说,它们是对象类型名称的SHA-1哈希值,作为字符串(“commit”,“blob”,“tree”或“tag”),后跟一个空格和字节长度的十进制表示形式,后跟一个NUL或零字节,最后是底层对象的原始数据。请注意,如果两次哈希相同的对象,则两次都会得到相同的哈希值。因此,如果名为README.txt的文件包含一些文本,然后将该文件复制到read-me-too.txt并哈希该文件,则再次得到相同的哈希值。这是因为文件的名称不是哈希计算的输入部分,只有类型(在本例中为blob)、空白、大小、零字节和内容。如果两个文件仅包含一行读取hello(加上换行符,总共六个字节),则哈希函数的输入为blob 6\0hello\n(其中\0和\n代表零字节和换行符)。实际上,这两个文件的哈希值是ce013625030ba8dba906f756967f9e9ca394464a。(我使用git hash-object找到了这个值,尽管任何SHA-1代码都可以完成此操作:您可以使用几行Python或Ruby代码或相当多的C代码找到这个值。哈希树更加棘手。)对象ID ce013625030ba8dba906f756967f9e9ca394464a表示包含单词hello后跟换行符的文件。如果我们知道文件包含的数据,则可以对数据进行哈希并查找Git对象ID。通常,我们从有效的Git对象ID开始,并从存储库中检索数据。但是,当我们git add文件时,我们会走这条路,将数据转换为哈希值并将其存储为Git对象(如果尚未在存储库中)。如果它已经存在,那么一切都很好:我们只需再次使用相同的哈希值。对象本身-对象的数据存储在Git存储库的某个位置。您发现的位置,其中对象0563f77d884e4f79ce95117e2d686d7d6e282887位于名为05的目录中,具有以63f77开头并继续使用其余哈希的文件名,是Git当前保留所谓松散对象的位置。但是,Git还将对象打包成所谓的打包文件。打包文件的格式相当复杂,这里需要花费太长时间去讨论。但是,我们可以说,单个打包文件可以存储数万个对象。(打包文件格式已经多次修订以提高性能和单个对象的可访问性。)我们需要一种方法将人类可读名称(如分支名称)转换为Git哈希值。这是您在注释中指出的搜索中找到的内容:它适用于由$ git rev-parse HEAD返回的提交哈希值。此哈希值存储在.git/refs/heads/master [和两个reflogs]中。Git的设计提供了两种特别区分明显的外部名称形式,具体而言是分支名称和标记名称,我们可以使用它们来记住特定的提交哈希值。Git的一般术语是引用。Git的远程跟踪分支也是引用,存储在refs/remotes/下。除了这些分支和标记名称之外,您还可能会遇到注释和“stash”(git stash):这些也使用引用,具体来说是在refs/notes/和
分支名称只是以refs/heads/2开头的引用。标签是以refs/tags/3开头的名称。任何一个都可以让您找到提交的SHA-1哈希值。两者之间的关键区别在于,分支名称预计会随时间而变化,指向分支上最新的提交;但是标签名称应该永远指向相同的提交。
实际上,不仅分支名称预计会更改,Git还将自动为您更改它。特别地,如果git status显示您在on branch master上,并且您进行了new提交,则Git将更改refs/heads/master以指向新的提交。Git还使新提交具有其父提交ID,即在您进行新提交之前master指向的提交。这就是分支增长的方式:引用始终指向最顶端的提交,根据定义。该顶部提交通过其父ID指向较早的提交,后者进一步指向历史记录中更早的提交,依此类推。(如果提交是merge commit,则它具有两个或甚至三个以上的父ID,而不仅仅是一个)。 这意味着您将在其他Git对象内部找到这些Git对象ID的关键位置。 这就是在漂亮打印提交时看到的内容(通过 git cat-file -p HEAD 或者 git cat-file commit HEAD,两者都可以做到同样的效果):您查看当前分支顶部提交的内容,并看到 tree <ugly-sha-1>。 因此,树的 ID 存储在提交中。然而,如果提交本身是在一个松散的对象中,而您在文件编辑器或查看器中打开了 .git/objects/05/...,则您将无法看到该哈希值,甚至无法看到单词 tree。 这是因为仓库数据被压缩了(具体地说,使用 zlib 进行了压缩;存储在包文件中的对象使用修改版的 xdelta 进行不同的压缩,然后还进行了 zlib 压缩)。 这也是为什么您可以并且应该使用像 git cat-file 这样的东西来查看对象的内容:这使您免受位置和格式细节的影响。 您只需要对象的 ID;git cat-file 将找到并解压缩该对象。

树对象本身包含其他Git对象ID,您可以通过使用 git cat-file -p 查看树对象:

$ git cat-file -p 'HEAD^{tree}'
[snip]
100644 blob cb2ca2bb2e86aa4a4c3c9b08490c72b04a1778d3    rfuncs.h
040000 tree 05006c6f2e6119fede241cf6ec845291a5be665e    sbuf
[snip more]

因此,与HEAD提交相关联的树中保存了一个特定的Git blob对象(cb2ca2b...)和一个附加的Git树对象(05006c6...)的Git对象名称。

1鸽巢原理告诉我们,如果我们散列足够多的不同对象,我们将至少获得两个不同文件的ce013625030ba8dba906f756967f9e9ca394464a。在那一天,Git就会崩溃。:-)虽然需要大量输入才能产生哈希冲突,但概率数学表明,即使您有数十亿个文件,也会在数千个磁盘驱动器上丢失数据,而不是获得Git哈希冲突。事实上,需要大约1.71千万亿个文件才能将哈希冲突的概率提高到10-18之一,这是企业级存储介质的典型误差率。

当然,这些假设是随机输入,而不是使用密码学理论恶意构造文件来尝试破坏Git。

2你在refs/heads文件夹中发现了master文件并非巧合。然而,Git可能不再将名称存储在平面文件中,因为这会对分支命名施加文件系统限制:特别是它使得同时拥有一个名为x和一个名为x/y的分支变得不可能。请注意,当引用位于.git/packed-refs中时,至少在信息理论意义上,同时拥有xx/y是可能的。仅仅是一个烦人的文件系统限制,您不能同时拥有名为x的文件和一个名为x的目录,其中包含一个名为y的文件。(没有特别好的原因来解释这个文件系统限制,除了POSIX要求这样做。)

3如果标签是一个注释标签,它将引用一个Git对象类型为“标签”,该对象随后指向下一个对象。实际上,这就是注释标签名称的定义:它是指向注释标签对象的refs/tags/中的名称。标签对象通常直接指向提交,尽管您可以标记标签对象,而不是直接标记提交,然后必须剥离两个标签层以到达基础提交。

Git允许您将标签(轻量级或注释)指向任何Git对象,但通常只允许您将分支名称指向提交对象。


感谢您的出色回答。除了回答我提出的问题,这也回答了我许多其他问题(例如,松散对象文件中包含什么)。 - Joshua Meyers

4
哈希值在grep中无法显示,因为它的前两位数字是目录的名称。
.git/objects/05/63f77d884e4f79ce95117e2d686d7d6e282887

Git 是一种版本控制系统,它将所有的信息存储在 .git/objects 中。详情可以参考这里:https://git-scm.com/book/zh/v2/Git-Internals-Git-Objects


2

试试这个:

git log --pretty=format:'%T %s'

这只是给我提交哈希的开头,而不是树哈希。 - Joshua Meyers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接