Gitが連想配列記憶装置であることを低レイヤーな操作を通して体感しよう!

  • 45
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

誤解を恐れずに言えば、Gitとは一つの連想配列ストレージシステムです。
かっこよく言うと Key Value Storeです。

Gitがやっていることの本質は、あなたのコンテンツを圧縮してDBに保存し、あなたの要求に応じてDBから取り出して解凍する、ただそれだけです。

具体的には
KVSでのsetにあたる命令がgit add,git commit
KVSでのgetにあたる命令がgit checkout
です。

そしてデータベースとは.git/object/ ディレクトリ配下にあるファイル群のことです。

さて今回はgit checkoutコマンドを使わずに低レイヤーなコマンドだけを使って同じことを実現してみましょう。

準備

新規レポジトリを作成して、"hello world"と書かれたファイルをコミットします。

git init dqneo
echo hello world > hello.txt
git add hello.txt
git commit -m "initial commit"

次にそのファイルを削除してコミットします。

git rm hello.txt
git commit -m "second commit"

2コミット実行した結果、いまワークツリーにはファイルが何もない状態になりました。

過去のファイルはどこへ行った?

さてhello.txtはワークツリーから消えてしまったわけですが、いったいどこにあるのでしょうか?

git rev-parsegit cat-fileなどの低レベルコマンドを駆使して連想配列オブジェクトを順番にたどっていけばわかります。

まず現在のHEADが指し示すコミットハッシュ値を調べます。

$ git rev-parse HEAD
3d4387e272a02ccd42d63570053ec317197c8da9

現在のHEADが指し示すコミットハッシュ値は3d4387e272a02ccd42d63570053ec317197c8da9です。
(ちなみにこの値は、あなたの環境では違う値になるはずです。なぜならコミットした日時とauthorの名前が、私とあなたでは違うからです)

次に、このコミットの親コミットを調べます。
親コミットというのは「ひとつ前のコミット」という意味です。

$ git cat-file -p 3d4387e272a02ccd42d63570053ec317197c8da9
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
parent 3d18808630d928c2f4b826569cc92d05bc25fa73
author DQNEO <dqneoo@xxxx.xxx> 1386165103 +0900
committer DQNEO <dqneoo@xxxx.xxx> 1386165103 +0900

second commit

親コミットはparentの右に書いてある 3d18808630d928c2f4b826569cc92d05bc25fa73であることがわかります。
親コミット(初回コミット)の中身をのぞいてみましょう。

git cat-file -p 3d18808630d928c2f4b826569cc92d05bc25fa73
tree 68aba62e560c0ebc3396e8ae9335232cd93a3f60
author DQNEO <dqneoo@xxxx.xxx> 1386165089 +0900
committer DQNEO <dqneoo@xxxx.xxx> 1386165089 +0900

initial commit

上記のtreeのハッシュ値を使って、treeオブジェクトの中身を覗いてみると

git cat-file -p 68aba62e560c0ebc3396e8ae9335232cd93a3f60
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad    hello.txt

あった!ありました!
hello.txtを表すblobオブジェクトが見つかりました。

過去のファイルはここにあった!

このオブジェクトが実際に.git/objects/内に存在することを確認しておきましょう。

ls -l .git/objects/3b
合計 4
-r--r--r-- 1 vagrant vagrant 28 12月  4 22:50 2013 18e512dba79e4c8300dd08aeb37f8e728b8dad

たしかに存在しています。

git checkoutと同じことを低レイヤーにやる

冒頭の定義によれば、git checkoutの本質はデータベースからコンテンツを取り出すことです。
具体的に言うと.git/objects/の中にあるファイル群からそれを見つけ出してカレントディレクトリ(ワークツリーとも言う)に展開することです。

さて、ではこのハッシュ値を使ってコンテンツを取り出してみましょう。

$ git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
hello world

キターーーー!!

ではこの結果をリダイレクトして、

git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad > hello.txt

はい、これでgit checkout みたいなことができました!
(実際のgit checkoutはHEADの参照先を変更したりとかもうちょっといろいろやってる)