はじめに
Linus Torvaldsさんによるgitの1st commit README拙訳です。
Initial revision of "git", the information manager from hell
社内のSVNからの移行を促すために資料を整備していたのですが、SVNでやっていたことを移し替えたりコマンドを覚えたりするより内部構造を知ったほうが早いことに気づきました。
https://www.slideshare.net/kou/postgresql-conference-japan-2017
それで、gitの内部構造についての解説資料を色々見ていたのですが、内部構造については原作者のこのREADMEに言い尽くされている気がします。のみならず、gitを使うものが抱くべき精神性のようなものが示されており、深い感銘を覚えました(ヒャッハー)。
※現在のディレクトリ名と相違しているところは現在の物に修正しています。
GIT - 馬鹿コンテンツトラッカー
コミットメッセージ:git, 地獄からきたインフォメーションマネージャ
gitの意味
"git" は何を意味することも出来る、お前の気分次第だ。
3文字で、発音可能で、現実のUNIXシステムでコマンドとして使われていないものであればなんでも良かったのだ。事実は'get'の発音間違いだ、関係していようがいまいが。
バカ。情けなくて浅ましい。単純。その他お前のスラング辞典から拾ってきやがれ。
グローバル・インフォメーション・トラッカー(global information tracker):
お前はいい気分だ、実際にお前の役に立つ。天使達は歌い、光が突如お前の部屋を満たす。くそったれ間抜けなトラック満載のクソ(goddamn idiotic truckload of sh*t):
壊れた場合。
概要
これは馬鹿(しかしとても早い)ディレクトリ・コンテンツ・マネージャだ。
沢山の事はしないが、効率的にディレクトリの内容を追跡する。
2つのオブジェクト構造がある
- オブジェクト・データベース(.git/objects/)
- カレントディレクトリキャッシュ(.git/index)
オブジェクト・データベース(SHA1によるファイル・ディレクトリ)
オブジェクト・データベースとは文字通りコンテンツによってアドレス指定可能なオブジェクトの集まりだ。全てのオブジェクトはそのコンテンツによって命名される。それ自体のSHA1ハッシュ値によって近似される。オブジェクトは他のオブジェクトを(SHA1ハッシュ値を参照することにより)参照することができ、それによりお前はオブジェクトの階層構造を構築できる。
このデータベースには複数の種類のオブジェクトの種類がある。それらは全てzlibにより圧縮されており、先頭はファイルタイプによるタグとデータに関するサイズ情報で始まる。SHA1ハッシュ値は圧縮後のものであり、元データのものではない。
特にオブジェクトの一貫性はコンテンツもしくはオブジェクトの種類とは独立して常にテスト出来る。全てのオブジェクトは次のようにして確かめられる。
それらのハッシュ値がファイルのコンテンツにマッチすること
オブジェクトが次の形式のバイトストリームとして復号に成功すること
(スペース無しのascii文字によるタグ) + (space) + (サイズ情報のasciiによる10進文字列) + (ヌル文字) + (バイナリオブジェクトデータ)
BLOB
ブロブオブジェクトはバイナリ・ブロブデータ以外の何をも意味せず、また他への参照も持たない。データの署名や他の検証は持たず、オブジェクトが一貫している限り(それはsha1ハッシュ値によりインデックスされているので,データはそれ自体は正しいとする)それは他になんの属性も持たない。名前の関連付けも、パーミッションもない。それは純粋にデータのブロブだ。(別の言い方をすると、ファイルの中身だ)
TREE
次の階層のオブジェクトの種類は、ツリーオブジェクトだ。ツリーオブジェクトはパーミッション・名前・ブロブデータからなるリストで、名前によりソート済みだ。別の言い方をするとツリーオブジェクトの一意性はコンテンツのセットで決定される。だから2つの別々だが同等のツリーは同じオブジェクトを共有する。
もう一度言うが、ツリーオブジェクトはただの純粋なデータ構造だ。それは履歴を持たず、署名も持たず、正当性の検証も持たない。コンテンツがハッシュ値によって保護されている以外には。それでツリー内のコンテンツの出処がわからなくとも、ブロブを信頼するのと同じ理屈でツリーオブジェクトを信頼できる。
※1
ツリーオブジェクトがファイル名+コンテンツでソートされているため、お前は2つのツリーを展開せずともdiffを作り出すことが出来る。共通の部分を無視するだけでお前のdiffの見栄えは良くなるだろう。別の言い方をするとお前は2つのどんなランダムなツリーの比較でも、ツリーのサイズではなく、差分の大きさをnとしてO(n)の計算量で効果的かつ効率よくdiffを表現できるというわけだ。
※2
「ブロブ」の名前は全く独占的にその内容に依存しているため(名前やパーミッションも関係ない)、ブロブの内容が変わらなければお前は細々としたリネームやパーミッションの変化を見る事ができる。しかしながら、データを変えつつリネームというのはもっと賢いdiffが要求されよう。
チェンジセット
チェンジセットオブジェクトは履歴を反映するための概念を導入するオブジェクトだ。他のオブジェクトとは対象的に、それはツリーの物理的な状態を表現するだけでなく、どうやって、そしてなぜそこにたどり着いたのかを表現する。
チェンジセットはツリーオブジェクトによって定義され、結果として(0,もしくは1以上)の親チェンジセットや何が起こったのかを表記するコメントを含む。もう一度言うが、チェンジセットそれ自体が認証されたりしているわけではない。コンテンツは明確で全てのレベルにおいて強固な暗号署名のゆえに「安全」であるが、しかしながらツリーが「良い」とかマージ情報が意味を成すとか信じる理由などない。例えば親チェンジセットはその結果について何の関連も持つ必要はない。
注釈
本物のSCMとは違い、チェンジセットにはリネーム情報やファイルモードの変更情報は含まれない。それらのことはツリー(結果のツリー、及び結果のツリーの親)が明示的に関わることだ。この馬鹿ファイルマネージャがそれらを表現する意味はない。
「信頼」について
「信頼」と言う概念は"git"の全く外側の概念だが、次の事を覚えておけ。まず第一に、全てのものがSHA1によりハッシュ値を与えられており、それゆえお前はオブジェクトがそのままであり外部の源により弄くられていないことを信頼出来る。それ故にオブジェクト名は知られている状態を一意的に識別出来る -お前が信頼したいであろう状態ではない。
さらに、チェンジセットのSHA1による署名はツリーのSHA1署名と、それは更にその親と関連付けられており、単一の名前のチェンジセットが全コンテンツと共に全ての履歴のセットを規定する。一度お前がチェンジセットの名前を得たなら、後ほど履歴のステップを誤魔化す事はできない。
それで幾つかのシステムにおける現実的な信頼を話すとすれば、お前がしなければならないのはトップレベルのチェンジセット名を含む特別な記述一つをデジタル的に署名することだけだ。お前のデジタル署名は他の者にそのチェンジセットが信頼できることを示し、そしてそのチェンジセットの履歴の不変性は他の者に全ての履歴が信頼できることを示すであろう。
言い換えるならば、お前はGPG/PGP等のデジタル署名されたemailを使い(SHA1のハッシュ値である)トップのチェンジセットを送るだけで全てのアーカイブの検証を行うことが出来る。
特に、お前や他のものが信頼するドキュメントのトラスト・ポイントやタグの分割されたアーカイブを持つことが出来る。もっともそれらの「信頼証明」はgitそのものを使っていることからもたらされるわけだが、別に特段gitがお前に何かするというわけでもない。
別の方法で言うならば、gitそれ自体はコンテンツの整合性のみあつかい、信頼は外部から付与されるということである。
カレント・ディレクトリ・キャッシュ
カレントディレクトリキャッシュとは単純なバイナリファイルであり、任意の時点での仮想的なディレクトリのコンテンツを効率よく表現したものである。それは名前、日付、パーミッション、コンテンツ(またはブロブ)を一緒にセットしたものの単純な配列である。キャッシュはいつでも名前順に並べられているが、キャッシュは長い期間に渡って保持されることを意味してはおらず、いつでも部分的に更新できる。
特に、カレントディレクトリキャッシュは確実に現在のディレクトリの内容と一致しなければならないと言うわけではないが、それには2つの重要な属性がある。
キャッシュしている全ての状態が再生成可能であること
(ディレクトリ構造だけでなく、ブロブオブジェクトを通して全てのデータも再生成可能であるということ)
特殊な場合として、明確で重複しないカレントディレクトリキャッシュからツリーオブジェクトへの単一のマッピングが有り、他のデータを見る必要も無くカレントキャッシュディレクトリから効率よく生成できる場合がある。それでディレクトリキャッシュはいつどの時点でもただひとつのユニークに定義されたツリーオブジェクトである(しかしながらツリーオブジェクトとディレクトリ内で何が起こったかをすり合わせるため追加的なデータを持つ)キャッシュされた状態(裏付けされるのを待っているツリーオブジェクト)と現在の状態との不整合を検出するための効率の良いメソッドを持つ。
これらはディレクトリキャッシュが行っているたった2つのことである。それはキャッシュであり、既存のツリーオブジェクトからの完全な再生成や、開発ツリーの更新、既存ツリーとの比較を行うためのものである。もしお前がディレクトリキャッシュを全く吹き飛ばしたとしても、キャッシュで表現されているツリーを保持している限り、何のデータも失うことはない。
(しかしながらディレクトリキャッシュには実データが入ることもある。特に、まだ裏付けされていない中間状態のツリーの表現を持つことがある。それでキャッシュ本来の意味や用法とは違う場合がある。カレントディレクトリキャッシュをツリーコミットへの作業中の状態と考えることも出来る)。