こんにちは。アドベントカレンダー21日目を担当する佐藤(ま)です。
アライドでは「大佐」と呼ばれております。
前回に引き続き、git-scm.com の Book をみながら Git (.git) の中身をみていってみようと思います。今回は、前回も少し触れましたが、Git オブジェクトについてです。
※ 10.2 Git Internals – Git Objects
Git Objects の種類
Git オブジェクトは、blob(ファイルに対応)、tree(ディレクトリに対応)、commit、tag の4種類ありますが、今回は主に、こちらでふれられてる blob、tree、commit object について確認していきます。
Blob Objects
Blob object は前回も少し触れましたが、ファイルに対応しているオブジェクトで、hash-object コマンドでオブジェクトを格納したり、cat-file コマンドでオブジェクトのタイプや中身を確認することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
% echo 'version 1' > test.txt # オブジェクト格納 % git hash-object -w test.txt 83baae61804e65cc73a7201a7252750c76066a30 # タイプ確認 % git cat-file -t 83baae61804e65cc73a7201a7252750c76066a30 blob % echo 'version 2' > test.txt # オブジェクト格納 % git hash-object -w test.txt 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a # タイプ確認 % git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a blob |
Tree Objects
The next type we’ll look at is the tree, which solves the problem of storing the filename and also allows you to store a group of files together.
A single tree object contains one or more tree entries, each of which contains a SHA-1 pointer to a blob or subtree with its associated mode, type, and filename.
Blob object だけだとコンテンツだけを格納していて、ファイル名が格納できないなどの問題がでてきますが、それらを解決するのに、tree object が使われます。では、適当にファイルとディレクトリをコミットし cat-file コマンドで tree object を確認してみます。
1 2 3 4 |
% git cat-file -p master^{tree} 100644 blob e845566c06f9bf557d35e8292c37cf05d97a9769 README 100644 blob a04c7c20de8de69bf52cdcc59ac4e1f0c8e4aaca Rakefile 040000 tree 577fe82790326b830f92b67706f208dbef6b4740 lib |
master^{tree} と指定することで master 配下の tree object を確認することができます。
配管コマンドで独自のツリーを作る
次に、配管コマンドだけで、blob object で書き込んだオブジェクトをステージングし、独自のツリーオブジェクトをつくってみます。ここでは、上記 Blob Objects で作成したオブジェクトを利用していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# タイプ確認 % git cat-file -t 83baae61804e65cc73a7201a7252750c76066a30 blob # オブジェクトをステージングにセット % git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt # ステータスチェック % git status On branch master Initial commit Changes to be committed: (use "git rm --cached ..." to unstage) new file: test.txt |
ステージングされました。続いて tree object に書き出します。
1 2 3 4 5 6 7 8 9 |
# オブジェクトの指定は不要 % git write-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 # タイプ確認 % git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579 tree # ツリーオブジェクトを確認 (blob object があります) % git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579 100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt |
これで tree object が書き込まれました。続いてもう一つ、同じファイルと新しいファイルを追加し新しい tree object をつくってみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 新しいファイル追加 % echo 'new file' > new.txt # version 2 の test.txt ファイルをインデックスへアップ % git update-index test.txt # 新しいファイルを インデックスへアップ % git update-index --add new.txt # tree object 書き込み % git write-tree 0155eb4229851634a0f03eb265b69f5a2d56f341 # tree object 確認 % git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt |
この時点で2つの tree object が作成できました。さらにこれらは read-tree コマンドを使うことで、既存のツリーを、サブツリーとして読み込むことができます。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 1つ目のオブジェクトに prefix を付け 2つ目のオブジェクトのサブツリーにセット % git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree object 書き込み % git write-tree 3c4e9cd789d88d8d89c1073707c3585e41b0e614 # tree object 確認 % git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614 040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt % ls new.txt test.txt |
Commit Objects
Commit object は、他のオブジェクトが持っていない、いつ、どのような理由で、誰が保存したのかについての情報を保持します。これまでの tree objects にはそのような情報がないので commit-tree コマンドを使って付与していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# オブジェクト一覧確認(この時点では tree object と blob object が3つずつ存在) % find .git/objects -type f .git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2 .git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # blob version 2 .git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3 .git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # blob version 1 .git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1 .git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # blob new.txt # tree object を指定してコミット % echo 'first commit' | git commit-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 70b8032643d7b81ff7a223102b2cf5d594e0a3c8 # commit object を確認 % git cat-file -p 70b8032643d7b81ff7a223102b2cf5d594e0a3c8 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 author sato.masaki 1419062431 +0900 committer sato.masaki 1419062431 +0900 first commit |
commit object には、その時点のスナップショットが記載されます。続いてコミットツリーを繋げる形でコミットしていきます。-p オプションで直前のコミットを参照します。
1 2 3 4 5 6 7 |
# 直前の first commit object を指定 % echo 'second commit' | git commit-tree 0155eb4229851634a0f03eb265b69f5a2d56f341 -p 70b8032643d7b81ff7a223102b2cf5d594e0a3c8 a92c028ddcb579b58ef967ab16c2ef2a54ade953 # second commit object を指定 % echo 'third commit' | git commit-tree 3c4e9cd789d88d8d89c1073707c3585e41b0e614 -p a92c028ddcb579b58ef967ab16c2ef2a54ade953 2ad2f7ebc8de7c27e11f907442f2ba450ee5ff04 |
このようにすることで、3つの commit object が1つのツリーとなるので、git log で一連のコミットログとして確認することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
% git log --stat 2ad2f7ebc8de7c27e11f907442f2ba450ee5ff04 commit 2ad2f7ebc8de7c27e11f907442f2ba450ee5ff04 Author: sato.masaki Date: Sat Dec 20 17:05:02 2014 +0900 third commit bak/test.txt | 1 + 1 file changed, 1 insertion(+) commit a92c028ddcb579b58ef967ab16c2ef2a54ade953 Author: sato.masaki Date: Sat Dec 20 17:04:31 2014 +0900 second commit new.txt | 1 + test.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) commit 70b8032643d7b81ff7a223102b2cf5d594e0a3c8 Author: sato.masaki Date: Sat Dec 20 17:00:31 2014 +0900 first commit test.txt | 1 + 1 file changed, 1 insertion(+) |
また、オブジェクト一覧をみてみると、blob、tree、commit object が3つずつできる為、合計9つのオブジェクトができているのが分かります。(ドキュメントでは 10個できていますが、ここでは’test content’を除いているので9つとなります。)
1 2 3 4 5 6 7 8 9 10 |
% find .git/objects -type f .git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2 .git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # blob version 2 .git/objects/2a/d2f7ebc8de7c27e11f907442f2ba450ee5ff04 # commit 3 .git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3 .git/objects/70/b8032643d7b81ff7a223102b2cf5d594e0a3c8 # commit 1 .git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # blob version 1 .git/objects/a9/2c028ddcb579b58ef967ab16c2ef2a54ade953 # commit 2 .git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1 .git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # blob new.txt |
これで git add から git commit までの流れを確認することができました。ここまでくると Git のコアの仕組みや、配管コマンドがどういうものなのかがなんとなく見えてきますね。
明日は、aoyama の番です!
よく使うであろうことをできるだけ分かりやすく発信していきます。 Gitの情報を無駄にキャッチアップしてます。