banner
三文字

方寸之间

居善地,心善渊,与善仁,言善信,正善治,事善能,动善时。
github
email
misskey

Gitの特定のハッシュ

最近、Git の内部原理について少し理解しました。特別なハッシュを見つけたので、自分の意見を共有するためにこの記事を書きました。

==============

この記事を読んでいるということは、おそらく Git の一連の操作についてかなり詳しいということを意味しますが、Git を使用する際に次のハッシュに出会ったことはありますか:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

おそらく、Git の各オブジェクトにはハッシュ値があり、誰もがハッシュの値に注意を払うことはないと思います。

しかし、上記のハッシュは実際には非常に特別なハッシュです。次に、なぜこのハッシュが特別な存在であるかを説明します。

Git のハッシュはどこから来るのですか?#

空のリポジトリであっても、すべての Git リポジトリにはこのハッシュが含まれています。これは、git show コマンドで確認できます:

    $ git show 4b825dc642cb6eb9a060e54bf8d69288fbee4904
    tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904

では、このハッシュはどこから来るのでしょうか?その前に、Git の基本的な知識を少し理解する必要があります:「Git のコア部分は、シンプルなキーバリューデータベース(キーバリューデータストア)です。Git リポジトリには、任意の種類のコンテンツを挿入することができ、それに対して一意のキーが返され、いつでもそのコンテンツを再度取得することができます。」

git hash-objectコマンドを使用してオブジェクトを保存し、そのオブジェクトのキーを取得することができます。

    $ echo 'test' | git hash-object -w --stdin
    9daeafb9864cf43055ae93beb0afd6c7d144bfa4

Git 内部で保存されるデータは次のようなものです。各オブジェクトには対応するハッシュ値があります:

Git データモデル from ProGit.v2

ps:もしまだ興味があるなら、Pro Git の Git Internals セクションに詳細な説明があります。

それでは、本題に入りましょう。この特別なハッシュはどのように生成されるのでしょうか?実際には、これは空のツリーのハッシュ値です。次のように、空の文字列の/dev/nullを使用してオブジェクトのハッシュを作成できます:

    $ git hash-object -t tree /dev/null
    4b825dc642cb6eb9a060e54bf8d69288fbee4904
    //または
    $ echo -n '' | git hash-object -t tree --stdin
    4b825dc642cb6eb9a060e54bf8d69288fbee4904

空のツリーハッシュの特別な用途#

空のツリーハッシュはgit diffと一緒に使用することができます。たとえば、ディレクトリ内の空白エラーをチェックしたい場合、--checkオプションを使用して HEAD と空のツリーを比較できます:

    $ echo "test  " > readme.md
    $ git add . && git commit -m "init"
    [master 6d8e897] init
     1 file changed, 1 insertion(+), 3 deletions(-)
    $ git diff $(git hash-object -t tree /dev/null) HEAD --check -- readme.md
    readme.md:1: trailing whitespace.
    +test

git hooksを書く際にも、空のツリーハッシュは非常に便利です。次のようなコードを使用して、新しいコミットを受け入れる前に検証することがかなり一般的です:

    for changed_file in $(git diff --cached --name-only --diff-filter=ACM HEAD)
    do
      if ! validate_file "$changed_file"; then
        echo "Aborting commit"
        exit 1
      fi
    done

以前のコミットがある場合、これは正常に機能しますが、コミットがない場合、HEAD参照は存在しません。この問題を解決するために、初期コミットのチェック時に空のツリーハッシュを使用できます:

    if git rev-parse --verify -q HEAD > /dev/null; then
      against=HEAD
    else
      # Initial commit: diff against an empty tree object
      against="$(git hash-object -t tree /dev/null)"
    fi
    
    for changed_file in $(git diff --cached --name-only --diff-filter=ACM "$against")
    do
      if ! validate_file "$changed_file"; then
        echo "Aborting commit"
        exit 1
      fi
    done

参考#

https://git-scm.com/book/en

https://floatingoctothorpe.uk/2017/empty-trees-in-git.html


最初に掲載されたのは個人ブログです:方寸之间

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。