2017/12/12更新

[git] Gitの内部(ツリーオブジェクト)

このエントリーをはてなブックマークに追加      

こんにちは、@yoheiMuneです。
今回は、Gitのデータ格納方法のうち、ツリーオブジェクトについてブログを書きたいと思います。

画像

Special Thanks to http://flic.kr/p/kxC7Bm



この記事を読む前に

この記事は、Gitの内部について書かれた、以下の記事の続きとして書いています。 以下の記事を先にお読み頂くと、今回の記事をより理解頂けるのではないかと思います。

Gitの内部(はじめに) Gitの内部(データ格納 - データオブジェクト)


この記事で伝えたいこと

この記事の目的は、Gitのツリーオブジェクトとは何か、を解説することです。 ツリーオブジェクトの中身を見る、ツリーオブジェクトを作成するという内容を通して、ツリーオブジェクトに対する知識を深められると思います。



ツリーオブジェクト

以下の内容は、Pro Gitを参照して、記載しています。

ツリーオブジェクトは、ステージングされたデータ関する情報を保持するオブジェクトです。 ツリーオブジェクトは、ステージングされたファイルの名前やそのハッシュ値などを保持します。 ファイルシステムに例えると、データオブジェクトがファイルであり、ツリーオブジェクトがディレクトリのようなものです。


ツリーオブジェクトの中身を見る

それではまずは、ツリーオブジェクトの中身を見てみましょう。 以下では、masterブランチ上での最後のコミットが参照しているツリーオブジェクトを表示しています。
$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859  README
100644 blob 8f94139338f9404f26296befa88755fc2598c289  Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0  lib
このツリーオブジェクトには、2つのデータオブジェクトと1つのツリーオブジェクトが含まれていることが分かります。 それぞれのデータに対して、以下の情報が格納されています。
モード
100644などの数値部分。そのデータがどのようなものかを示す。例えば、100644はファイル、100755は実行可能ファイル、120000はシンボリックリンクを表します。
データ種類
blobなどの表示部分。Gitにおけるデータの種類を示します。
ハッシュ値
そのデータを参照するためのSHA-1で作成した40文字のハッシュ値
名前
READMEなどの表示部分。ファイル名やディレクトリ名を示します。
上記のツリーオブジェクトには、libというツリーオブジェクトが含まれています。その内容を表示してみましょう。
$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b  simplegit.rb
libツリーオブジェクトには、1つのBlogデータが含まれていることが分かります。
上記の内容から、master^{tree}の中身を、以下のように図示することができます。
画像

引用:http://git-scm.com/book/en/Git-Internals-Git-Objects

以上が、ツリーオブジェクトが保持する内容です。


ツリーオブジェクト作成する

それでは続いて、ツリーオブジェクトを作成してみましょう。 Gitでは通常、git addなどのタイミングでツリーオブジェクトが作成されます。 しかしここでは、ツリーオブジェクトをより理解するために、 git update-indexgit write-treeなどの内部コマンドを使って、ツリーオブジェクトを作成してみましょう。

以降で利用するレポジトリは、前回の記事で作成したレポジトリを利用しています。


前回の記事では、test.txtというファイルで"version 1""version 2"という2つのバージョンのファイルを作成しました。
$ find .git/objects -type f
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30  # test.txtのバージョン1
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a  # test.txtのバージョン2
# 他のハッシュは省略しています

$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30
version 1

$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
version 2
最初に、"version 1"のtest.txtのみを持つツリーオブジェクトを作成してみたいと思います。 まずは、git update-indexを使って対象ファイルをステージング状態にします。
git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt
--addオプションは、最初のバージョンのtest.txtが新規ファイルでステージングエリアに存在しないため、付ける必要があるオプションです。 --cacheinfoは、ディレクトリには存在せずデータベースにのみ存在する(ディレクトリにはtest.txtという同名のファイルがありますが、内容は"version 2")ため、データベースからステージングに追加するために必要なオプションです。 100644は、データの種類を指定しています。

現在の状態は、以下の通りです。
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#   new file:   test.txt  #ステージングしたファイル
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   test.txt  #前回作成した"version 2"のファイル
#
ステージングされているtest.txtが、git update-indexで追加したもので、ステージングされていないファイルが、前回の最後に作成したtest.txtです。

この状態で、ツリーオブジェクトを作成しましょう。git write-treeという内部コマンドを利用します。
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
これでツリーオブジェクトが作成されました。中身を確認しましょう。
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30      test.txt
また、-tオプションを付けることで、オブジェクトの種類を知ることができます。
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree
無事にツリーオブジェクトを作成することができました。

それでは続いて、もう1つツリーオブジェクトを作りましょう。
ここでは、現在ステージングされていない"versio 2"のファイルをステージングエリアに追加します。
$ git update-index test.txt
さらにもう1つ新規ファイルを追加して、ステージング状態にします。
$ echo 'new file' > new.txt
$ git update-index --add new.txt
現在の状況は、以下となります。
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#   new file:   new.txt
#   new file:   test.txt
#
この状態で、ツリーオブジェクトを作成します。
$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341
これで、2つ目のツリーオブジェクトを作成する事ができました。中身を確認してみます。
$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
100644 blob fa49b077972391ad58037050f2a75f74e3671e92  new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a  test.txt
このツリーは2つのデータオブジェクトを保持しています。 そしてtest.txtは、ハッシュ値から分かる通り、"version 2"のデータです。


それではここで、最初のツリー(d8329fc1cc938780ffdd9f94e0d364e0ea74f579)を2つ目のツリーのサブディレクトリとして、ステージングエリアに追加してみましょう。 git read-treeという内部コマンドを使うことで、ステージングエリア内にツリーを読み込む事ができます。 また、--prefix=(ディレクトリ名)オプションを付けることで、ステージングエリアの中に、既存のツリーをサブディレクトリとして読み込むことができます。 そしてその後、git write-treeでツリーを作成します。
$ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git write-tree
3c4e9cd789d88d8d89c1073707c3585e41b0e614
$ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579  bak
100644 blob fa49b077972391ad58037050f2a75f74e3671e92  new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a  test.txt
さてここで作成したツリーは、2つのBlobデータと、1つのツリーオブジェクトを保持しています。 これらの構造を概念的に示すと、以下のようになります。
画像

引用:http://git-scm.com/book/en/Git-Internals-Git-Objects




最後に

今回は、Gitの内部におけるデータ格納方法のうち、ツリーオブジェクトについて扱いました。 ツリーオブジェクトは、ステージングされた状態を保持するための大切な情報であり、次に説明するコミットオブジェクトで利用されます。 前回、今回、そして次に説明するコミットオブジェクトを学ぶことで、Gitのデータストアの基本を知ることができると思いますので、乞うご期待下さいw。

最後までご覧頂きましてありがとうございました。





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。