Programming log - Shindo200

イベント参加記録とプログラミング系の雑記

git リポジトリの中身を覗いてみる

git リポジトリの中身にちょっと興味を持ったので、調べたメモを残しておく。

git においてのオブジェクト

コミット情報やファイルの情報は、オブジェクトと呼ばれるファイルに格納される。オブジェクト名は、オブジェクトの中に格納されているデータのハッシュ値となっている。オブジェクト名の先頭2文字でオブジェクトファイルの格納先ディレクトリが分けられて、オブジェクト名から先頭2文字を除いた残りの文字がオブジェクトファイル名となっている。オブジェクトの格納場所は .git/objects ディレクトリ。各ブランチのコミット情報のオブジェクト名は .git/refs/heads/[ブランチ] に記録されている。
実際にリポジトリを作って、ファイルをコミットして、コミット情報のオブジェクト名を見てみる。

$ git init
$ echo test > test_file
$ git add test_file
$ git commit -m 'First commit'
$ cat .git/refs/heads/master
08c1457dd8899e8bf678dd37aa77471562cf0f30

このコミット情報のオブジェクト名は「08c1457dd8899e8bf678dd37aa77471562cf0f30」。
オブジェクトファイルの格納場所: .git/objects/08
ファイル名: c1457dd8899e8bf678dd37aa77471562cf0f30
になっている。

$ ls .git/objects/08/c1457dd8899e8bf678dd37aa77471562cf0f30
.git/objects/08/c1457dd8899e8bf678dd37aa77471562cf0f30

ファイルの中身は zlib で圧縮されているので、そのまま開いても人間には読めない。

カレントのブランチ

.git/HEAD にカレントのブランチが記録されている。

$ cat .git/HEAD
ref: refs/heads/master
$ git checkout -b work master
$ cat .git/HEAD
ref: refs/heads/work

試しに .git/HEAD ファイルのブランチ名部分を書き換えてみたらカレントブランチが変わった。

$ git branch
  master
* work
$ vi .git/HEAD
# refs: refs/heads/work を refs: refs/heads/master に変更
$ git branch
* master
  work

ファイルのオブジェクトの確認

git rev-parse :[ファイル名] でオブジェクト名を確認できる。

$ git rev-parse :test_file
9daeafb9864cf43055ae93beb0afd6c7d144bfa4

このファイルのオブジェクト名は「9daeafb9864cf43055ae93beb0afd6c7d144bfa4」。
オブジェクトファイルの格納場所: .git/objects/9d
ファイル名: aeafb9864cf43055ae93beb0afd6c7d144bfa4
になっている。

$ ls .git/objects/9d/aeafb9864cf43055ae93beb0afd6c7d144bfa4
.git/objects/9d/aeafb9864cf43055ae93beb0afd6c7d144bfa4

git cat-file -p [オブジェクト名] でオブジェクトの内容を出力できる。オブジェクト名は全て入力しなくても、検索して結果を出力してくれる。しかし、短過ぎると検索に失敗する。

$ git cat-file -p 9daeaf
test
  • p 以外のオプションを指定することで、オブジェクトの様々な情報を出力できる。git cat-file コマンドのオプションを調べてみた。
-t オブジェクトの種類を出力
-s オブジェクトのファイルサイズを出力
-e オブジェクトが存在する場合に 0 を出力
-p オブジェクトの内容を人間が読める形で出力

オブジェクトの種類

git cat-file -t [オブジェクト名] でオブジェクトの種類を出力できる。オブジェクトの種類を調べてみた。

blob ファイル情報を持つオブジェクト
tree ディレクトリ情報を持つオブジェクト
commit コミット情報を持つオブジェクト
tag タグ情報を持つオブジェクト

先ほど試した test_file のオブジェクトの種類は blob になっている。

$ git cat-file -t 9daeaf
blob

blob の場合は「ファイルサイズ」と「ファイルの中身」を情報として持っている。

tree オブジェクト

tree オブジェクトにはディレクトリの情報が格納されている。現在の tree オブジェクトを確認する前にファイルをいくつか作成し、コミットする。

$ echo foo > text_foo
$ mkdir dir
$ echo bar > dir/text_bar
$ git -am 'Add text_foo dir/text_bar'

コミットログを出力する。

$ git log
commit fbbf68e96fef1c6bb899a19db1ca4ff0b855e0de
Author: user <mail@example.com>
Date:   Sun Feb 24 23:26:27 2013 +0900

    Add text_foo dir/text_bar

commit 08c1457dd8899e8bf678dd37aa77471562cf0f30
Author: user <mail@example.com>
Date:   Sun Feb 24 21:42:48 2013 +0900

    First commit

コミットログから現在のリポジトリのオブジェクト名がわかったので、このオブジェクトの情報を出力する。

$ git cat-file -p fbbf68
tree 5d304f975e68af45adebce26d1fe411f689be81f
parent 08c1457dd8899e8bf678dd37aa77471562cf0f30
author user <mail@example.com> 1361715987 +0900
committer user <mail@example.com> 1361715987 +0900

Add text_foo dir/text_bar

tree オブジェクトの情報を出力する。

$ git cat-file -p 5d304f
040000 tree c6e7e8127dd9bdece46af994b13cff2baa2f0ad3	dir
100644 blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4	test_file
100644 blob 257cc5642cb1a054f08cc83f2d943e56fd3ebe99	text_foo

「ファイルモード」「オブジェクトの種類」「オブジェクト名」「実際のファイル名」が出力される。ちなみに、ファイルモードとはファイルの種類を表す番号のことらしい。それぞれの番号の意味を調べてみた。

040000 ディレクトリ
100644 実行権限がないファイル
100755 実行権限があるファイル
120000 シンボリックリンク

commit オブジェクト

コミット情報は commit という種類のオブジェクトに格納されている。直前のコミット情報は git cat-file コマンドにオブジェクト名を指定しなくても、git cat-file commit HEAD コマンドで出力できる。

$ git cat-file commit HEAD
tree 5d304f975e68af45adebce26d1fe411f689be81f
parent 08c1457dd8899e8bf678dd37aa77471562cf0f30
author user <mail@example.com> 1361715987 +0900
committer user <mail@example.com> 1361715987 +0900

Add text_foo dir/text_bar

オブジェクトに「tree オブジェクト名」「parent」「ファイルを変更したユーザ (author)」「コミットしたユーザ (committer)」「コミットメッセージ」の情報が格納されている。parent は1つ前のコミット情報のオブジェクト名を表している。この parent を使ってファイルの世代管理を行っている。試しに1つ前のコミット情報を出力してみる。

$ git cat-file -p 08c145
tree ca93b49848670d03b3968c8a481eca55f5fb2150
author user <mail@example.com> 1361709768 +0900
committer user <mail@example.com> 1361709768 +0900

First commit

1つ前のコミットが最初のコミットなので parent はない。

参考

Git ユーザマニュアル http://cdn8.atwikiimg.com/git_jp/pub/Documentation.ja/user-manual.html
岩松信洋・上川純一・まえだこうへい・小川伸一郎 (2011) Gitによるバージョン管理