最近、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 内部で保存されるデータは次のようなものです。各オブジェクトには対応するハッシュ値があります:
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://floatingoctothorpe.uk/2017/empty-trees-in-git.html
最初に掲載されたのは個人ブログです:方寸之间