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. http://git-scm.com/

2層のアーキテクチャ

Gitは次の2層アーキテクチャで実現されている。

  1. 連想記憶ファイルシステム(content-addressable filesystem)

    Gitは対象をオブジェクトの集まりとして捉え管理している。
  2. VCS ユーザ・インタフェース(VCS user interface)

    参照・シンボリック参照と呼ばれる要素を用いてbranch, tag などを表現している

これらは、プロジェクトルートに存在する.gitディレクトリの中に収まっている。

コアファイルの構成(.git/)

.git ディレクトリは以下の様になっている。 行った操作履歴により存在するファイルは異なるが基本的な構造は変わらない。 自分の環境を例に出す。

 1 2 3 4 5 6 7 8 91011121314
  $ (cd path/to/proj_root  && tree -L 1 .git)
  .git
  ├── COMMIT_EDITMSG
  ├── HEAD
  ├── ORIG_HEAD
  ├── branches
  ├── config
  ├── description
  ├── hooks
  ├── index
  ├── info
  ├── logs
  ├── objects
  └── refs

上記のファイル・ディレクトリが<連想記憶ファイルシステム(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) すると中身を読むことが出来る。

123
$ (cd path/to/proj_root && tree .git/objects)
# とてもたくさんのファイルが流れてく
$ git cat-file -p ${SHA1_FILE_NAME}

ファイルの内容はblobが保持する。blob 以外のオブジェクトは他のオブジェクトのハッシュ値を経由し関係を保持する。

作業ディレクトリのツリー構造はtree オブジェクトが保持するが以下の様にリンクで実現されている。

image

コミット履歴についても同様でcommitオブジェクトの持つリンクで実現される。

image

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で内容を確認できる。

1
$ git ls-files --stage

VCS User Interface を支えるファイル

  • 参照(Git References) Commitオブジェクトの名前を含むテキストファイルになっている
  • シンボリック参照 参照ファイルのファイルパスを含む
  • ログ Commitの変更を記録しているテキストファイル

参照

オブジェクトの名前に対応するハッシュ値を保持したテキストファイル

オブジェクトは名前がハッシュ値になってしまうので人間から指定が識別しづらい。 そこで、重要なオブジェクトを人間が扱えるように保持するファイルが役に立つ。 後述するシンボリック参照と共にVCS ユーザ・インタフェースを支えている。

主に.git/refs 以下に配置される。

12345
$ find .git/refs -type f
.git/refs/heads/master
.git/refs/remotes/origin/master
.git/refs/tags/test
$ git cat-file -p `cat .git/refs/heads/master`

image

ref. 10.3 Git Internals - Git References

シンボリック参照

参照に対するエイリアスとして働きref:を先頭に持つテキストファイル

12
$ cat .git/HEAD
ref: refs/heads/master

Log

特定のブランチ, HEADなどの指す先が変更された時の記録で、各変更前後のcommitを保持している。

comments powered by Disqus