2011/03/26

Mercurialのリポジトリ内のデータについて

Mercurialではリポジトリであるディレクトリ.hgの中に管理している全ファイルの履歴に関するデータが入っています。それがどのように保存されているのかを例を用いて簡単に紹介します。

概要

ディレクトリ.hg内にはいろいろファイルが含まれています。ほとんどのデータは.hg/store内にあり、その中にチェンジセット、マニフェスト、ファイルの履歴が含まれています。

  • changelog
    • ファイル: .hg/store/00changelog.iと.hg/store/00changelog.d
    • マニフェストIDとコミット者、コメント、変更したファイル一覧などの各チェンジセットの情報を持ちます。
  • manifest
    • ファイル: .hg/store/00manifest.iと.hg/store/00manifest.d
    • リポジトリで管理しているファイルの一覧です。
  • filelog

次のようなリビジョングラフを持つリポジトリがあるとします。

この時点でのリポジトリの.hg/store内のファイルは次のようなものが含まれています。

  • チェンジログ 00changelog.i
  • マニフェスト 00manifest.i
  • ファイルログ data/readme.i
  • ファイルログ data/hello.c.i

まずチェンジログの情報を見てみましょう。これには、hg debugindexを用います。

> hg debugindex .hg/store/00changelog.i
   rev    offset  length   base linkrev nodeid       p1           p2
     0         0      93      0       0 534511281f45 000000000000 000000000000
     1        93     101      0       1 6292b118cc08 534511281f45 000000000000
     2       194      96      2       2 027b81ff6ac3 6292b118cc08 000000000000
     3       290      95      3       3 199d45709b22 534511281f45 000000000000
     4       385      90      4       4 f0df7b3fe2df 027b81ff6ac3 199d45709b22

この中で、revはリビジョン番号、nodeidはチェンジセットIDとなっています。そして、p1とp2は親のチェンジセットIDであり、rev 4では2つの親を持っており、全体としては上のリビジョングラフと同じ関係になっていることがわかります。

特定のリビジョンにおける、より詳しい情報を見るにはhg debugdataを用います。チェンジセットのリビジョン4の出力結果は次のようになります。出力結果は1行目からマニフェストID、メールアドレス、日時、変更のあったファイル(空行まで)、コミットメッセージになっています。

> hg debugdata .hg/store/00changelog.i 4
b6323997ef85fb93cb3dc645ae6ca7e2b8161cea
foobar@example.com
1300547811 -32400
hello.c

Merge

続いてマニフェストの情報を見ます。hg debugindexでチェンジログのときと同じような情報を見ることができます。マニフェストでもリビジョンごとにチェンジログとは異なるIDが振られていますが、リビジョンの親子関係が同じであることがわかります。また、先ほどチェンジセットのリビジョン4の情報にあったマニフェストIDのb6323997ef85がマニフェストのrev 4に対応していることがわかるでしょう。

> hg debugindex .hg/store/00manifest.i 
   rev    offset  length   base linkrev nodeid       p1           p2
     0         0      50      0       0 47eb6be8d357 000000000000 000000000000
     1        50      50      1       1 2f3afa09f4f8 47eb6be8d357 000000000000
     2       100      60      1       2 b0e834b727ad 2f3afa09f4f8 000000000000
     3       160      50      3       3 08b4087d6b4f 47eb6be8d357 000000000000
     4       210      96      3       4 b6323997ef85 b0e834b727ad 08b4087d6b4f

マニフェストの詳細情報を見るのにはhg debugindexを利用してもよいですが、hg --debug manifestのほうがより詳細な情報を見ることができます。次の例ではマニフェストのrev 4に含まれるファイルの情報を出力しています。

> hg --debug manifest 4
94e1e9804aa05b8a7874c4316c7eec59e87c9c36 644   README
600deb1ed703b78db22e5020acb13242430c5543 644   hello.c

ファイルhello.cに対するファイルログhello.c.iの概要は次の通りです。マニフェストに出現している600deb1ed703がrev 3にあるのがわかります。

> hg debugindex .hg/store/data/hello.c.i 
   rev    offset  length   base linkrev nodeid       p1           p2
     0         0      71      0       0 edb191afdd3d 000000000000 000000000000
     1        71      43      0       1 ec1d99d13de0 edb191afdd3d 000000000000
     2       114      26      0       3 8e675a97a9fe edb191afdd3d 000000000000
     3       140      43      0       4 600deb1ed703 ec1d99d13de0 8e675a97a9fe

hg debugdataを用いると指定したリビジョンのデータを取得することができます。

> hg debugdata .hg/store/data/hello.c.i 3
#include <stdio.h>

int main(int argc, char** argv) {
    printf("Hello, world!\n");
    return 1;
}

ファイルREADMEは追加したとき以外の変更がないのでリビジョン0のままです。

> hg debugindex .hg/store/data/_r_e_a_d_m_e.i
   rev    offset  length   base linkrev nodeid       p1           p2
     0         0      22      0       2 94e1e9804aa0 000000000000 000000000000

これらのファイルのリビジョン同士の関係を示したものが次の図です。黒矢印はファイルのリビジョン関係を、赤点線の矢印は(チェンジセットのリビジョン4における)参照可能な関係を示しています。hg updateなどではこの矢印を辿ることで特定リビジョンのファイルを取得しています。

Revlog形式

チェンジログ、マニフェスト、ファイルログはファイルの履歴を保存するためにRevlogと呼ばれるMercurial独自のフォーマットで記録されています。Revlogには次のような特徴があります。

  • データファイルとインデックスファイルに分かれており、それぞれ拡張子 .d と .i を持つ
  • データとインデックスはファイル末尾に追加されていく (トランザクション化が容易になる)
  • 直前のバージョンからのバイナリでの差分 (difflibで用いられているRatcliff-Obershelpアルゴリズム) によるデータを圧縮したものを持つ。ただし、再構築に必要なデータ量がしきい値を超えたときは差分ではなくフルデータを取るようにする (リビジョン数によらない)
  • 小さいサイズのファイルの場合.dはなく.iの中に.dのデータが含まれている (.iと.dが交互に出現する; データインタリーブ)

コマンドラインdebugindexでインデックスファイルを、debugdataでデータファイルを見ることができます。これらをRevlogのファイル構造のサンプルが次の図です。例えば、リビジョン12のデータを取得するためには、まずベースとなるリビジョン10からフルデータを取得し、そこからリビジョン11と12の差分データを順番に適用することで得られます。

なお、Revlogのインデックスファイルは次のようなフォーマットになっています。

  • 6 bytes: 差分データ開始位置のオフセット
  • 2 bytes: フラグ
  • 4 bytes: 差分データの圧縮サイズ
  • 4 bytes: オリジナルデータのサイズ
  • 4 bytes: ベースリビジョン: どのリビジョンのフルデータから差分を適用すればよいか
  • 4 bytes: リンクリビジョン: どのチェンジセットに対するデータなのかをチェンジセットのリビジョン番号で示す
  • 4 bytes: 親1のリビジョン
  • 4 bytes: 親2のリビジョン
  • 32 bytes: ノードID: ユニーク識別子で改竄検知などにも用いられる (データと親ノードIDによるハッシュ値)

これを見ればわかる通り、各ファイルはそれぞれリビジョンごとにユニークなノードIDを持ち、最大2つまでの親を取ることができます。Mercurialではマージ操作では2つ以上のファイルからマージされることはないため、このようにシンプルなデータ構造となっています。

また、これによって各リビジョンごとのインデックスのサイズが固定長になるため、あるリビジョンのインデックスを得るためには決まった位置へファイルシークするだけで済みます (データインタリーブしている場合を除く)。

まとめ

Mercurialのリポジトリ内のデータについて簡単に紹介しました。

関連項目

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。