GitのオブジェクトDBと索引
Gitについて勉強した事がなかったので調べた。 Gitの内部構造の概略と調べるのに必要な箇所を整理する。 なおソースへのリンクはv2.1.0-rc2を参照している。
Gitの概要
Gitは分散型バージョン管理システムと呼ばれている。
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
ref. https://git-scm.com/
2層のアーキテクチャ
-
- 連想記憶ファイルシステム(content-addressable filesystem)
- Gitは対象をオブジェクトの集まりとして捉え管理している。
-
- VCS ユーザ・インタフェース(VCS user interface)
- 参照・シンボリック参照と呼ばれる要素を用いてbranch, tag などを表現している
これらは、プロジェクトルートに存在する.git
ディレクトリの中に収まっている。
コアファイルの構成(.git/)
.git ディレクトリは以下の様になっている。 行った操作履歴により存在するファイルは異なるが基本的な構造は変わらない。 自分の環境を例に出す。
|
|
上記のファイル・ディレクトリが<連想記憶ファイルシステム(CAF)/VCS ユーザ・インタフェース(VCSUI)>に関係が強いか? ファイル、ディレクトリなら格納されるファイルの種類、目的などの概要を整理すると以下にになる。
名前 | 関連 | ファイルタイプ | 概要 |
---|---|---|---|
HEAD | VCSUI | file: ref | sym_ref |
index | CAF | file: index | stage に対応するcacheされているblobへの参照が保持されている |
logs | VCSUI | dir : log | commit が作成された時の変化(前後のcommit)を記録している |
objects | CAF | dir : object | オブジェクトを保持する |
refs | VCSUI | dir : ref | commit, tag への参照を保持する |
ORIG_HEAD | VCSUI | file: ref(only?) | マージなど危険な操作の直前のコミットを指す |
COMMIT_EDITMSG | VCSUI | file: text | 前回のコミットメッセージが含まれている |
hooks | VCSUI | dir : scripts | gitにhookする実行ファイルを配置 |
branches | VCSUI | dir : ? | ブランチ |
description | ? | file: ? | 調べてない |
info | ? | dir : ? | 調べてない |
システムの骨格を理解するのが目的なので触れるのは ORIG_HEAD より上に並べたファイル群に絞る。
Git システムを構成するファイルの種類
前節でファイルタイプとして書いたように、Gitシステムは幾つかの種類のファイルで作られている。 ここでは、ファイル・タイプを2層のアーキテクチャで区分けして説明する。
content-addressable filesystem を支えるファイル
Content-Addressable Filesystemは、作業ディレクトリのツリー情報(ファイル内容・メタ情報・ディレクトリの格納関係)・変更履歴(コミット)・タグ、を全て格納しており、 格納しているデータベース部分・次に格納する情報をまとめたインデックス、の2つで構成されている。
- Git Objects 管理対象になっているファイル・ディレクトリ・コミット・タグなど全てを表現する
- Index git 運用でのstage に対応するObjectの一覧を保持する
Git Objects
Git はプロジェクトの歴史を4種類のオブジェクトの組み合わせで表現している 。 各構造体の定義を見るとstruct object を最初のメンバに含んだ形になっている。
タイプ名 | 表現対象 | 関連ソース |
---|---|---|
blob | ファイルの内容 | https://github.com/git/git/blob/v2.1.0-rc2/blob.h#L8-L10 |
tree | ディレクトリが保持するファイル名とファイルのメタデータ | https://github.com/git/git/blob/v2.1.0-rc2/tree.h#L8-L12 |
comit | コミット | https://github.com/git/git/blob/v2.1.0-rc2/commit.h#L16-L23 |
tag | タグ(オブジェクトに対するコメント付きの参照) | https://github.com/git/git/blob/v2.1.0-rc2/tag.h#L8-L13 |
Objectはタイプ名と表現対象を連結しzlibで圧縮した内容を持つファイルになる。
そのファイルの置かれる場所はファイルの内容をsha1を取ることで名前が一意に決まる。
具体的にはハッシュ値の上位2桁が.git/objects
以下のディレクトリ名に対応し残りの38桁がファイル名になる。
tree .git/objects
とかするとハッシュに対応するようにファイルが置かれている事がわかる。
このディレクトリ名+ファイル名をgit-cat-file(plumbing)
すると中身を読むことが出来る。
|
|
ファイルの内容はblobが保持する。blob 以外のオブジェクトは他のオブジェクトのハッシュ値を経由し関係を保持する。
作業ディレクトリのツリー構造はtree オブジェクトが保持するが以下の様にリンクで実現されている。
コミット履歴についても同様でcommitオブジェクトの持つリンクで実現される。
ref. 10.2 Git Internals - Git Objects
tag オブジェクトの役割はgit-tag で作られるタグと即座には対応しない。 公式資料にある通り実態は2種類ある。
タグは後述する参照がオブジェクトのハッシュ値をrefs/tags
以下に保持することで実現される。
この時どのようなオブジェクトでもタグ付けが可能だが、
タグ生成時にコメントを記述した場合は対象オブジェクトを指すtag
オブジェクトが生成される。
この時、タグに対応した参照はtagオブジェクトを指している。
Index
stage に対応するblob オブジェクトへの一覧が格納されている。 サブディレクトリが存在してもtreeを介した表現にはならない。 登録されたファイルのプロジェクトルートからの相対パスとblobオブジェクトの名前(sha1)が格納されている。 これがGitでは空ディレクトリをトラック出来ないという制約に繋がる。
git ls-files --stage
で内容を確認できる。
|
|
VCS User Interface を支えるファイル
- 参照(Git References) Commitオブジェクトの名前を含むテキストファイルになっている
- シンボリック参照 参照ファイルのファイルパスを含む
- ログ Commitの変更を記録しているテキストファイル
参照
オブジェクトの名前に対応するハッシュ値を保持したテキストファイル
オブジェクトは名前がハッシュ値になってしまうので人間から指定が識別しづらい。 そこで、重要なオブジェクトを人間が扱えるように保持するファイルが役に立つ。 後述するシンボリック参照と共にVCS ユーザ・インタフェースを支えている。
主に.git/refs
以下に配置される。
|
|
ref. 10.3 Git Internals - Git References
シンボリック参照
参照に対するエイリアスとして働き
ref:
を先頭に持つテキストファイル
|
|
Log
特定のブランチ, HEADなどの指す先が変更された時の記録で、各変更前後のcommitを保持している。