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


Is git's semi-secret empty tree object reliable, and why is there not a symbolic name for it?

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

1
4b825dc642cb6eb9a060e54bf8d69288fbee4904

(您可以在任何仓库中看到这一点,即使是新创建的仓库,也可以使用git cat-file -tgit cat-file -p)。

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

它作为git diff-tree的一个参数更有用,其中一个示例钩子就是这样。

我想知道的是,

  • 这有多可靠 - 即,某些未来版本的git没有一个编号为4b825dc642cb6eb9a060e54bf8d69288fbee4904的git对象?
  • 为什么空树没有符号名称(或者有一个?)。
  • (创建符号名称的快速而肮脏的方法是将SHA1放入,例如,.git/Nulltree。不幸的是,您必须为每个repo执行此操作。似乎更好地将幻数放在脚本中等等。我只是对魔法数字的一般厌恶。)


    这个帖子提到:

    If you don't remember the empty tree sha1, you can always derive it with:

    1
    git hash-object -t tree /dev/null

    或者,正如Ciro Santilli在评论中提出的那样:

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

    或者,如此处所见,来自Colin Schimmelfing:

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

    因此,我认为将该命令的结果定义为空的sha1树(而不是依赖于"众所周知的值")更安全。

    注意,当作者想要第一次提交为空时,你会看到SHA1弹出一些GitHub仓库(参见博客文章"我如何初始化我的Git存储库"):

    1
    $ 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'

    会给你:

    Empty tree SHA1

    (参见树SHA1?)

    您甚至可以在该空提交之上重新定义现有历史记录(请参阅"git:如何插入提交作为第一个,转移所有其他提交?")

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

    要做到这一点:

    1
    2
    3
    4
    5
    6
    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"

    这将生成一个特定于您的repo,用户名,电子邮件,创建日期的SHA1的提交(意味着提交本身的SHA1每次都不同)。
    但该提交引用的树将是4b825dc642cb6eb9a060e54bf8d69288fbee4904,空树SHA1。

    1
    2
    3
    4
    5
    6
    7
    8
    git log --pretty=raw

    commit 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
    tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904      <====
    author VonC <[email protected]> 1381232247 +0200
    committer VonC <[email protected]> 1381232247 +0200

        initial empty commit

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

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

    如果提交引用空树,确实是您的第一次提交,则可以显示空树SHA1:

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

    (甚至适用于Windows,使用Gnu On Windows命令)

    如下所示,使用git diff HEAD,这将显示当前分支HEAD中的所有文件:

    1
    git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

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

    1
    2
    #define EMPTY_TREE_SHA1_HEX \
       "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

    它现在(Git 2.16,2018年第一季度),在一个不再绑定到(仅)SHA1的结构中使用,如commit eb0ccfd中所示:

    Switch empty tree and blob lookups to use hash abstraction

    Switch the uses of empty_tree_oid and empty_blob_oid to use the current_hash abstraction that represents the current hash algorithm in
    use.

    查看更多"为什么Git不使用更现代的SHA?"


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

    但我更喜欢"空"来标记,而不是分支。简单的方法是:

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

    因为tag可以直接指向tree-ish而不提交。
    现在获取工作树中的所有文件:

    1
    git diff --name-only empty

    或者与stat相同:

    1
    git diff --stat empty

    所有文件为diff:

    1
    git diff empty

    检查所有文件中的空格:

    1
    git diff --check empty


    我写了一篇博文,其中有两种不同的方法可以找到哈希:http://colinschimmelfing.com/blog/gits-empty-tree/

    如果由于某种原因它永远改变,你可以使用以下两种方式来找到它。但是,我会非常自信地使用.bashrc别名中的哈希等,我认为它不会很快改变。至少它可能是git的主要版本。

    这两种方式是:

  • 答案如上:git hash-object -t tree --stdin < /dev/null
  • 只需启动一个空的repo然后在新的repo中运行git write-tree - 哈希将由git write-tree输出。