50
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Gitオブジェクトの中身

はじめに

.git/objectsフォルダの中に大量にできる謎のファイルについて調べてみました。
Gitをただツールとして使うだけなら必要のない知識ですが、Gitの設計を理解すると、Gitを難しいと思わなくなるかもしれません。

Gitオブジェクト

Gitのデータは、Gitオブジェクトという形で.git/objectsの中に格納されます。
例えば、以下のようにファイルを1つだけコミットしてみると、

$ git init
$ echo "hello" > sample.txt
$ git add sample.txt
$ git commit -m "Initial commit"

.git/objectsフォルダの中にファイルが3つできていることが分かります。

$ find .git/objects -type f
.git/objects/71/9448a65a4412c7dcdfa3845dbaf755f14207ae
.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
.git/objects/e3/d14d7340059b5852f32f29574e98ff73eb3c47

Gitオブジェクトは40文字のハッシュが付けられ、先頭の2文字をサブフォルダ名、残りの38文字をファイル名として、1つのオブジェクトごとに1つのファイルに保存されます。

cat-fileコマンドで見てみる

git cat-fileコマンドでGitオブジェクトの内容を確認できます。
-tオプションでオブジェクトのタイプを表示します。見たいオブジェクトのハッシュを指定しますが、40文字すべて入力する必要はなく、先頭の4文字で十分です。

$ git cat-file -t 7194
commit
$ git cat-file -t ce01
blob
$ git cat-file -t e3d1
tree

3つのオブジェクトは、それぞれcommitblobtreeというタイプであることが分かります。

blobオブジェクト

-pオプションでオブジェクトの中身を表示できます。

$ git cat-file -p ce01
hello
$ cat sample.txt
hello

blobオブジェクトはファイルデータそのものです。ファイル名や属性(実行可能かどうか)の情報は含まれていません。

treeオブジェクト

$ git cat-file -p e3d1
100644 blob ce013625030ba8dba906f756967f9e9ca394464a    sample.txt
$ ls
sample.txt

treeオブジェクトはフォルダ構造を表します。ファイルの属性、blobオブジェクトへの参照(サブフォルダの場合はtreeオブジェクトへの参照)、ファイル名の情報を記録しています。

commitオブジェクト

$ git cat-file -p 7194
tree e3d14d7340059b5852f32f29574e98ff73eb3c47
author nkshigeru <nkshigeru@example.com> 1434542915 +0900
committer nkshigeru <nkshigeru@example.com> 1434542915 +0900

Initial commit

commitオブジェクトには、トップレベルのtreeへの参照、コミットしたユーザーの情報、タイムスタンプ、コミットメッセージが含まれます。また、これは最初のコミットなので親コミットがありませんが、2回目以降のコミットでは親コミットへの参照も含まれます。

pythonで見てみる

実はGitオブジェクトはzlibで圧縮されているだけなので、pythonや何か適当なツールでも見ることができます。

$ python
>>> f = open('.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a', 'rb')
>>> import zlib
>>> zlib.decompress(f.read())
'blob 6\x00hello\n'

つまり、blobオブジェクトは、オブジェクトタイプ'blob' + ' ' + ファイルコンテンツの長さ + null文字'\0' + ファイルコンテンツという構造になっていることが分かります。

さらに、Gitオブジェクトのハッシュは、圧縮されていないデータのSHA1ハッシュを計算することで求められます。

>>> import hashlib
>>> hashlib.sha1('blob 6\x00hello\n').hexdigest()
'ce013625030ba8dba906f756967f9e9ca394464a'

blobオブジェクトやtreeオブジェクトの場合は、同じ内容ならハッシュが同じになるため、ディスク容量を節約することができます。(commitオブジェクトの場合は、ユーザー情報やタイムスタンプが含まれるため、別のcommitオブジェクトと全く同じになることはたぶんないと思います。)
一方、少しでも内容が異なれば、全く異なるハッシュになるため、オブジェクトが衝突することを心配する必要はありません。また、ハッシュによってデータの破損を検出することが可能です。

まとめ

Gitのデータは、zlib圧縮とSHA1ハッシュを使って.git/objectsフォルダの中に格納されています。そのしくみはとてもシンプルですが、データを効率的に安全に扱う工夫がされています。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
50
Help us understand the problem. What are the problem?