git的半秘密空树对象是否可靠,为什么没有它的符号名?


125

Git有一个众所周知的或至少是众所周知的空树,其SHA1为:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(您可以使用git cat-file -t和在任何存储库(甚至是新创建的存储库中看到此代码git cat-file -p))。

如果您努力工作并且非常小心,则可以使用这种空树来存储没有文件的目录(请参阅“ 如何向git存储库中添加空目录的答案”),尽管这并不是一个好主意。

作为git diff-tree示例挂钩中的的一个参数,它更有用。

我想知道的是

  1. 这有多可靠?也就是说,将来的git版本中没有编号的git对象4b825dc642cb6eb9a060e54bf8d69288fbee4904吗?
  2. 为什么空树没有符号名(或者有没有?)。

(创建符号名称的一种快速而肮脏的方法是将SHA1放入。例如,.git/Nulltree不幸的是,您必须为每个存储库执行此操作。似乎最好将幻数放在脚本中,等等。我只是有一个一般厌恶魔术数字。)


3
只是要记住哈希值;-)使用SHA1(“ tree 0 \ 0”)= 4b825dc642cb6eb9a060e54bf8d69288fbee4904(\ 0是NUL字符)
Thomas

4
@Thomas:该git hash-object -t tree /dev/null方法(来自以下VonC的答案)具有不对SHA-1进行硬编码的优点,以防万一某些将来的git版本切换到SHA-2。(我不会试图预测何时会发生这种情况。:-)将Mercurial切换为SHA-2会更容易,因为他们留有余地。)
torek '16

的确如此,您说的没错,但这是“无用知识”的绝妙内容,在任何情况下对其他任何人都可能有帮助?
托马斯

2
@Thomas:看起来哈希算法的转换可能比预期的要早。:-)
torek '17

说到“ Git的将来版本”,我想您会对我最新(2017年12月)编辑到2012年答案的内容感兴趣:stackoverflow.com/revisions/9766506/7
VonC

Answers:


104

该线程提到:

如果您不记得空树sha1,则可以始终使用以下方法派生它:

git hash-object -t tree /dev/null

或者,正如Ciro Santilli 在评论中建议的那样

printf '' | git hash-object --stdin -t tree

或者,如从此处看到的,来自Colin Schimmelfing

git hash-object -t tree --stdin < /dev/null

因此,我想用该命令的结果将变量定义为空的sha1树会更安全(而不是依赖“众所周知的值”)。

注意:Git 2.25.1(2020年2月)在commit 9c8a294中提出:

empty_tree=$(git mktree </dev/null)
# Windows:
git mktree <NUL

并添加:

作为历史记录,该函数现在repo_read_object_file()346245a1bb中被教导为空树(“对空树对象进行硬编码”,2008-02-13,Git v1.5.5-rc0- merge),并且该函数现在已知正如oid_object_info()c4d9986f5f中讲授的空树(“ sha1_object_info:也检查cached_object商店”,2011-02-07,Git v1.7.4.1)一样。


请注意,当作者希望其第一次提交为空时,您将看到SHA1在某个GitHub存储库上弹出(请参阅博客文章“ 如何初始化Git存储库 ”):

$ GIT_AUTHOR_DATE="Thu, 01 Jan 1970 00:00:00 +0000" GIT_COMMITTER_DATE="Thu, 01 Jan 1970 00:00:00 +0000" git commit --allow-empty -m 'Initial commit'

会给你:

空树SHA1

(看到树SHA1吗?)

您甚至可以在该空提交的基础上重新建立现有的历史记录(请参阅“ git:如何首先插入一个提交,并转移所有其他提交? ”)

在这两种情况下,您都不依赖于该空树的确切SHA1值。
您只需遵循最佳实践,并使用第一个空commit初始化您的仓库


要做到这一点:

git init my_new_repo
cd my_new_repo
git config user.name username
git config user.email email@com

git commit --allow-empty -m "initial empty commit"

这将生成带有特定于您的存储库,用户名,电子邮件,创建日期的SHA1的提交(这意味着提交的SHA1每次都会不同)。
但是该提交引用的树将是4b825dc642cb6eb9a060e54bf8d69288fbee4904空树SHA1。

git log --pretty=raw

commit 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904      <====
author VonC <vonc@laposte.net> 1381232247 +0200
committer VonC <vonc@laposte.net> 1381232247 +0200

    initial empty commit

要仅显示提交树(显示提交树SHA1):

git show --pretty=format:%T 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
4b825dc642cb6eb9a060e54bf8d69288fbee4904

如果引用空树的提交确实是您的第一个提交,则可以使用以下命令显示空树SHA1:

git log --pretty=format:%h --reverse | head -1 | xargs git show --pretty=format:%T
4b825dc642cb6eb9a060e54bf8d69288fbee4904

(甚至在Windows上也可以使用Windows上的Gnu命令运行)


正如下面的评论,使用git diff <commit> HEAD,这将显示在目前的分公司负责所有的文件:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

注意:空树值在中正式定义cache.h

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

从Git 2.16(Q1 2018)开始,它用于不再与(仅)SHA1绑定的结构中,如在提交eb0ccfd中所示

切换空树和Blob查找以使用哈希抽象

开关的用途empty_tree_oidempty_blob_oid使用current_hash,表示当前使用的哈希算法抽象。

在“ 为什么Git不使用更现代的SHA? ”中查看更多信息:自Git 2.19(2018年第三季度)以来,它是SHA-2


使用Git 2.25(Q1 2020),测试正在为SHA-2过渡做准备,并且涉及到空树。

提交fa26d5e提交cf02be8提交38ee26b提交37ab8eb提交0370b35提交0253e12提交45e2ef2提交79b0edc提交840624f提交32a6707提交440bf91提交0b408ca提交2eabd38(2019年10月28日),以及提交1bcef51提交ecde49b(2019年10月5日)by brian m。卡尔森(bk2204
(由Junio C gitsterHamano合并--提交28014c1中,2019年11月10日)

t/oid-info:添加空树和空Blob值

签字人:brian m。卡尔森

测试人员最终将学习如何使用SHA-1以外的算法运行。为此,请教test_oid函数系列如何查找空的blob和空的树值,以便可以使用它们。

所以t/oid-info/hash-info现在包括:

rawsz sha1:20
rawsz sha256:32

hexsz sha1:40
hexsz sha256:64

zero sha1:0000000000000000000000000000000000000000
zero sha256:0000000000000000000000000000000000000000000000000000000000000000

algo sha1:sha1
algo sha256:sha256

empty_blob sha1:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813

empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321

SHA2“ 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321”是新的SHA1“ 4b825dc642cb6eb9a060e54bf8d69288fbee4904”空树。


@torek:我在第一个空提交最佳实践中添加了一些示例,以说明该空树SHA1。
VonC 2012年

好吧,目标之一是使用“空树”哈希作为git diff-tree我正在编写的某些脚本中的参数。不能保证仓库中有一个初始的空提交。因此,我只是想知道这些脚本是否最终有一天会中断。
torek 2012年

1
如果传递-wgit hash-object,它将在要运行的存储库中创建对象,并且如果将来将来消失,它将在您要运行的存储库中重新创建空树。
javawizard 2014年

如果要使用rebase进行第一次提交,可以使用git rebase --root
GergelyPolonkai 2014年

1
或者,如果您更喜欢管道的魔力而不是/dev/nullprintf '' | git hash-object --stdin -t tree:) 的魔力
Ciro Santilli郝海东冠状病六四事件法轮功2014年

3

我写了一篇博客文章,提供了两种不同的查找哈希的方式:http : //colinschimmelfing.com/blog/gits-empty-tree/

如果由于某种原因而要更改,则可以使用以下两种方法进行查找。但是,我对使用.bashrc别名等中的哈希值充满信心,而且我认为它不会很快改变。至少它可能是git的主要发行版。

两种方式是:

  1. 上面的答案: git hash-object -t tree --stdin < /dev/null
  2. 只需初始化一个空的仓库,然后git write-tree在该新仓库中运行-哈希将由git write-tree输出。

使用运行命令–-stdin给我fatal: Cannot open '–-stdin': No such file or directorygit 2.7.2。但是,如果不--stdin按VonC的答案运行它,则会得到哈希值
sigy

现在博客帖子已经死了,这个答案不是很有用。因此,为什么我们通常不认可SO上的这些答案。
菲利普·怀特豪斯

1
@PhilipWhitehouse的博客文章还没有死,但是无论如何,我都在回答中包括了两种方式-我同意不包括这两种方式,那将不是一个好答案。
schimmy

3

这是即使存储库尚未为空时如何创建空树提交的答案。 https://stackoverflow.com/a/14623458/9361507

但是我更喜欢“空”作为标签,而不是分支。简单的方法是:

git tag empty $(git hash-object -t tree /dev/null)

因为标记可以直接指向树状结构,而无需提交。现在获取工作树中的所有文件:

git diff --name-only empty

或与stat相同:

git diff --stat empty

所有文件作为差异:

git diff empty

检查所有文件中的空格:

git diff --check empty

...但是在标签创建过程中使用幻数只是在地毯上刷了一个问题(使用幻数SHA-1)
RomainValeri

不对。我使用标记指向树状对象。到目前为止,此树状结构已由SHA-1定义,将来可以更改为SHA-256等(通过存储库迁移)。但是标签将是相同的。:)标签的主要特征是指向对象。标签可以在内部使用SHA-1或其他方式,这仅是Git内部的问题。
Olleg,

我明白了。但是,如果您(或任何阅读此内容的人)(或更糟的是脚本)尝试在以后应用它(您的第一行),则可能会在新的散列算法上失败,该算法将第一行替换为执行的表达式(产生此哈希)将继续成功。
RomainValeri

如果将此与自动生成空树哈希的方法之一结合使用,则可以对此进行过时验证(如@RomainValeri所建议)。但是,如果由我决定,git rev-parse则将有新的标志或关键字或类似的内容,以产生(a)空树哈希和(b)空提交哈希。两者在脚本中都是有用的,并且可以防止提出的SHA-256更改。
torek '19

Okey,变了。但这不是“最简单的方法”。:)
Olleg
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.