2017/12/12更新

[git] Gitの内部におけるデータ格納方法について

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

こんにちは、@yoheiMuneです。
先日に引き続き、gitの内部の仕組みについてブログを書きたいと思います。

画像

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




この記事で伝えたいこと

この記事の目的は、なぜGitが連想記憶ファイルシステム(content-addressable filesystem)なのかを理解してもらう、ということです。 前回から引き続き、Gitの内部の仕組みを扱います。



Gitのオブジェクト

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


Gitの内部(はじめに)でも簡単に触れましたが、gitは連想記憶ファイルシステム(content-addressable filesystem)上に構築されたバージョン管理システムです。 これは、Gitのコア部分が、キーバリューから成るデータストアである、ということを意味しています。そしてそのデータは、.git/objects/で管理されます。

.git/objects/にデータが増えるタイミングは、通常、git commitなどのコマンドが実行された時で、コミット対象のファイルのデータが格納されます。 この記事ではgit commitコマンドではなく、内部コマンドを利用することで、Gitデータストアの仕組みについて触れたいと思います。

まずは、新規にGitレポジトリを作成し、.git/objects/の中を見てみましょう。
$ mkdir test
$ cd test
$ git init
Initialized empty Git repository in /tmp/test/.git/

# 中身を全部確認してみる
$ find .git/objects
.git/objects
.git/objects/info
.git/objects/pack

# ファイル(Notディレクトリ)の存在を確認する
$ find .git/objects -type f
$
infopackというディレクトリが存在し、ファイルは存在していません。 つまりデータは何も格納されていない、空のレポジトリということです。


Gitデータストアにデータを保存する

それでは、Gitの内部コマンドであるgit hash-objectを使って、Gitデータベースにデータを保存してみましょう。
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
-wオプションは、hash-objectコマンドに、オブジェクトを格納するように伝えます。-wを付与しない場合には、受け取ったデータに対応するキーは何かを返すだけです。--stdinは標準入力をデータとして受け取るためのオプションです。 これを指定しない場合には、hash-objectはファイルパスを探そうとします。

さて、hash-objectを実行した結果、40文字のハッシュ値を得られました。この値はSHA-1で生成した値で、Gitデータベースに保存したデータのキーです。このハッシュ値は、git logなどで見慣れたものではないでしょうか。

それでは、格納したデータが.git/objects/に存在するか確認しましょう。
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects/に保存されていることが分かります。見ると分かりますが、40文字のハッシュ値のうち、最初の2文字をディレクトリ名に割り当て、残りの38文字をファイル名に割り当てています。


Gitデータストアからデータを取り出す

このファイルをcatコマンドで表示してみましょう。
$ cat .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
xK??OR04f(I-.QH??+I?+?K?
ううう、文字化けした値が返ってきました。これは、格納されたデータは圧縮されているため、文字列として表示すると文字化けしてしまうのです。 具体的な中身を確認するためには、内部コマンドであるgit cat-fileを利用します。
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
今度はちゃんと、保存したデータが表示されました。-pオプションを付けることで、コンテンツを分かりやすく表示する事ができます。


簡単なバージョン管理を行ってみる

今までの内容を利用することで、簡単なバージョン管理を行う事ができます。 ここでは、test.txtに複数回書き込んだ後に、最初の書き込みに戻す、ということをやってみましょう。
# test.txtを新規に作成します
$ echo 'version 1' > test.txt

# Gitデータベースに書き込みます
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
これでtest.txtのバージョン1のデータがGit上に作成されました。 続いて、バージョン2を作成します。
# 内容を上書きする
$ echo 'version 2' > test.txt

# Gitデータベースに書き込みます
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
これで、バージョン2が作成できました。.git/objects/の格納状況を確認してみます。
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a  # test.txtのバージョン2
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30  # test.txtのバージョン1
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4  # 最初の説明で作ったもの
ちゃんとデータが保存されていますね。 ここで、text.txtのバージョン1の内容に戻して(revertして)みましょう。git cat-fileコマンドを利用します。
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
$ cat test.txt
version 1
ちゃんと戻りましたね。バージョン2の内容にするには、以下のように行います。
$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt
$ cat test.txt
version 2
Gitデータベースから任意の状態のtest.txtが復元できますね。


なぜGitが連想記憶ファイルシステムなのか

以上、簡単な説明ですが、Gitのデータ保存の仕組みについて説明しました。 説明で触れた通り、SHA-1で生成した40文字のハッシュ値を使うことで、任意のファイルの任意の状態にアクセスすることが出来ます。 つまり、40文字のハッシュ値がキーであり、保存されたデータがバリューであり、 それを保存するGitはキーバリューストア(Key-Value Store)なのです。

さて、この記事では主にデータを対象に、.git/objects/に保存しましたが、他にもツリーオブジェクトやコミットオブジェクトというオブジェクトも存在します。今後の記事でそれらも扱えればと思います。



参考資料

Gitの内部挙動を理解するために、以下の資料を参照しました。ありがとうございます。

Pro Git | git-scm.com/book



最後に

今回は、Gitの内部の仕組みについて、Objectを取り上げて説明しました。Gitがなぜ連想記憶ファイルシステムなのか、少しでも理解頂けていたら幸いです。 Gitの内部構造、学び始めると楽しいですね。

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





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

RSS画像

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