Note-47356-18

Token ID: 1

ERC-721 1 Transfers

Metadata

{
  "title": "Git中的一个特殊hash",
  "tags": [
    "post",
    "git",
    "hash"
  ],
  "sources": [
    "xlog"
  ],
  "external_urls": [
    "https://x.oii.im/special-hash-in-git"
  ],
  "date_published": "2023-03-26T15:58:06.130Z",
  "content": "最近了解了一点Git的内部原理,看到了一个特殊的hash,所以写了这一篇文章来分享自己的看法。\n\n==============\n\n既然你读这篇文章,那就意味着你应该比较熟悉Git的一系列操作,不过,在你使用 Git 的时候,你有没有遇到以下hash:\n```bash\n4b825dc642cb6eb9a060e54bf8d69288fbee4904\n```\n可能你会觉得git中的每个对象都有一个hash值,谁会注意hash的数值。确实,没有人会注意。\n\n但是上面的这个hash确实是一个很特别的hash,接下来就来说明为什么这个hash是一个特殊的存在。\n\ngit中hash从哪里来?\n-------------\n\n每个 git 存储库,即使是空存储库也将包含这段hash。这可以通过 git show 验证:\n```bash\n    $ git show 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n    tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n```\n那么这个hash是从哪里来的呢?在这之前我们需要了解一点Git的知识:_Git 的核心部分是一个简单的键值对数据库(key-value data store)。 你可以向 Git 仓库中插入任意类型的内容,它会返回一个唯一的键,通过该键可以在任意时刻再次取回该内容。_\n\n我们可以使用`git hash-object`命令来存储一个对象并获取该对象的键。\n```bash\n    $ echo 'test' | git hash-object -w --stdin\n    9daeafb9864cf43055ae93beb0afd6c7d144bfa4\n```\nGit 内部存储的数据类似下面这样,其中每个对象都有其对应的hash值:\n\n![Git 数据模型 from ProGit.v2](https://cdn.jsdelivr.net/gh/baddate/imagedb@master/data-model-1.png)\n\n\n\n_ps:如果你仍然好奇,Pro Git 的_ [_Git Internals_](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) _章节有更多详细介绍。_\n\n那么接下来说正题,这个特殊hash是如何产生的呢?它实际上是一棵空树的哈希值。可以通过为空字符串的`/dev/null`创建对象哈希来验证:\n```bash\n    $ git hash-object -t tree /dev/null\n    4b825dc642cb6eb9a060e54bf8d69288fbee4904\n    //或者\n    $ echo -n '' | git hash-object -t tree --stdin\n    4b825dc642cb6eb9a060e54bf8d69288fbee4904\n```\n空树hash的特殊用处\n-----------\n\n空树hash可以与`git diff`一起使用。例如,如果你想检查目录中的空白错误,您可以使用 `--check` 选项并将 HEAD 与空树进行比较:\n```bash\n    $ echo \"test  \" > readme.md\n    $ git add . && git commit -m \"init\"\n    [master 6d8e897] init\n     1 file changed, 1 insertion(+), 3 deletions(-)\n    $ git diff $(git hash-object -t tree /dev/null) HEAD --check -- readme.md\n    readme.md:1: trailing whitespace.\n    +test\n```\n在编写 `git hooks` 时,空树hash也非常有用。一个相当常见的用法是在使用类似于以下的代码在接受新提交之前验证它们:\n```bash\n    for changed_file in $(git diff --cached --name-only --diff-filter=ACM HEAD)\n    do\n      if ! validate_file \"$changed_file\"; then\n        echo \"Aborting commit\"\n        exit 1\n      fi\n    done\n```\n如果有以前的提交,这可以正常工作,但是如果没有提交,则 `HEAD` 引用将不存在。为了解决这个问题,可以在检查初始提交时使用空树hash:\n```bash\n    if git rev-parse --verify -q HEAD > /dev/null; then\n      against=HEAD\n    else\n      # Initial commit: diff against an empty tree object\n      against=\"$(git hash-object -t tree /dev/null)\"\n    fi\n    \n    for changed_file in $(git diff --cached --name-only --diff-filter=ACM \"$against\")\n    do\n      if ! validate_file \"$changed_file\"; then\n        echo \"Aborting commit\"\n        exit 1\n      fi\n    done\n```\n参考\n--\n\n[https://git-scm.com/book/en](https://git-scm.com/book/en)\n\n[https://floatingoctothorpe.uk/2017/empty-trees-in-git.html](https://floatingoctothorpe.uk/2017/empty-trees-in-git.html)\n\n----\n\n首发于个人博客:[方寸之间](https://sanmoji.xyz)",
  "attributes": [
    {
      "value": "special-hash-in-git",
      "trait_type": "xlog_slug"
    }
  ]
}