ushumpei’s blog

生活で気になったことを随時調べて書いていきます。

Pro Git 2nd Edition 読んでる

最近手が痛くてプログラミング時間を少々減してて、久しぶりに本でも読もうかという気分になっています。

git ちゃんとわかっていなかったので、 Pro Git 2nd Edition を読み始めました。ちょっと面白いことがあったのでメモします。

前提: git は差分ではなくファイルのスナップショットを保持している

git が既存の VCS (Version Control System) と大きく異なった点として、ファイル変更履歴の管理方法が 変更されたファイルの差分ではなく変更されたファイル全体のスナップショット であることだと書かれています。自分の理解だと、ファイル容量などを考えると変更差分を保持していた方がいいと思いますが、ぶっちゃけテキストファイルが主だしまあスナップショットでも大丈夫なんだろうなー、くらいの軽い感じでした。しかしこのことが結構重要で、 git がめちゃくちゃ速い理由につながっているようです。

リポジトリ内のファイル全体のスナップショットが作成されるのは commit 時で、次の要素が .git/objects 以下にファイルとして作成されます。ファイル形式は blob でファイル名はそのデータのハッシュ値です。

  • blob オブジェクト: 変更後のファイル
  • tree オブジェクト: ディレクトリツリー。変更対象のファイルを持っていたディレクトリに対して、ツリーのノードの参照先 (tree, blob オブジェクトのハッシュ値) を書き換えた tree オブジェクトが作成される。(毎回リポジトリルートの tree は更新される)
  • commit オブジェクト: 新しく作成されたリポジトリルートの tree への参照と、parent commit オブジェクトへの参照と作成者情報やコミットメッセージ。

言葉だとややこしいですが実物は以下のようになります(ハッシュ値は適当です)。

blob オブジェクト: .git/objects/f5/83c304ea36b6fa554eb01381e781b04e45477f

# Pro Git

URL: [https://git-scm.com/book/ja/v2](https://git-scm.com/book/ja/v2)

tree オブジェクト: .git/objects/20/4bfe00b89a265e7c16e8688a90dfb86e52c5eb

100644 blob f583c304ea36b6fa554eb01381e781b04e45477f README.md

commit オブジェクト: .git/objects/31/ba323616cc8cbc543882c5a4eef6aa95eef803

tree 204bfe00b89a265e7c16e8688a90dfb86e52c5eb
author ushumpei <mail@example.com> 1528220889 +0900
committer ushumpei <mail@example.com> 1528220889 +0900

Initial commit.

(.git/objects 以下のファイルは git cat-file コマンドに -p オプションをつけてハッシュ値の頭から 6 文字を引数にして実行すると、中身を閲覧することができます: .git/objects/f5/83c304ea36b6fa554eb01381e781b04e45477f なら git cat-file -p f583c3)

要するに?

要するに私が面白いと感じたのは、特定のコミットに入っているファイルを取り出したいときは、commit オブジェクトのハッシュを使って、

commit オブジェクト -> tree オブジェクト -> (ツリーの探索) -> blob オブジェクト

という風に取り出してくれるので、めっちゃ速い、すげー、ということです。これは過去のコミットでも、分岐してある程度進んだブランチのコミットでも同じように行われます。(多分)

感想

「git 速い」って何と比較して、というと svn なのですが、ベンチマークとか取っていないので (どう比較するか謎ですが) 怒られそうですね。「考え方の変化で処理が変わった」ということにテンションが上がっただけです。