彷徨えるフジワラ

年がら年中さまよってます

Mercurial の Automatic Pooled Storage for Clones

本エントリでは、Mercurial 3.5 から導入された "Automatic Pooled Storage for Clone" について説明します。

なお、この機能の開発を主導した、"Mozilla の中の人" こと Gregory Szorc 氏自身も、"My Contributions to Mercurial 3.5" と題したブログエントリで、この機能について言及しています。

share 機能の概要

同一のプロジェクトにおいて、複数の作業を平行して行う場合に、hg cloneリポジトリを複製し、複製先で作業することもあるでしょう。

$ hg clone src cloned1
$ hg clone src cloned2
$ hg clone src cloned3

しかし、hg clone での複製による運用は、履歴情報(図中の色つき矩形部分)が各リポジトリに散在してしまうことから、場合によっては以下の様な点が気になるかもしれません:

  • リポジトリで保持される履歴情報には共通部分も多いので、ディスク領域の無駄がある
  • 作業成果(= 新規リビジョン)も各リポジトリに散在するため、反映忘れの可能性がある

前者の問題に関しては、ハードリンクが使用できる環境で、且つ同一ファイルシステム上での hg clone であれば、履歴情報の大部分はハードリンクを使って共有されるのですが、それであっても、大量のファイルや履歴を扱う大規模プロジェクトの場合は、無視できないケースが多いでしょうねぇ。

ここで、hg clone コマンドの代わりに、標準同梱される share エクステンションが提供する hg share コマンドを使うと:

$ hg share src cloned1
$ hg share src cloned2
$ hg share src cloned3

複製先リポジトリは、複製元 src の履歴情報を参照しつつ、作業領域での作業は、独立して実施できるようになります。

この状態で複製先で追加された新規リビジョンは、全て複製元リポジトリである src の履歴管理領域に書き込まれます。履歴情報がこのように集約されることにより、先述した問題は解消されます。

また、通常のリポジトリ複製は、多数の履歴情報ファイルの複製を伴うため、(ハードリンクを使う場合であっても)非常にコストが高いのですが、履歴管理領域を共有する場合は、複製先の管理領域に、履歴情報参照先を書き込むだけなので、非常に高速に処理を終えることができます。

ちなみに、上記の構成例において、 cloned1 の作業領域がリビジョン R を参照している状態で、cloned2 等でリビジョン R を破棄するような履歴改変操作 (e.g. rebase や histedit、strip 等) を実施すると、cloned1 は未知のリビジョンを参照することになってしまいます。

また、「直前の操作1つ分」しか取り消しのできない hg rollback は、「履歴管理領域あたり」での「直前の操作1つ分」になりますから、通常構成時の使い勝手との違いに、戸惑うことになるかもしれません。

一見しただけだと、良い事尽くめに見える share エクステンションが、現状ではデフォルトで無効化されているのは、こういった点への理解が無いまま使用した場合に、ユーザが混乱してしまうことへの配慮と思われます (上記のような留意点に関しては、 hg help -v share でも言及されています)。

リモートリポジトリ連携での share 利用

hg share コマンドに指定する複製元リポジトリは、ローカルホスト上のものでなければなりません。

そのため、リモートホスト上のリポジトリ src と連携するケースで:

$ hg clone URL-of-src cloned1
$ hg clone URL-of-src cloned2
$ hg clone URL-of-src cloned3

履歴情報を集約するためには、一工夫する必要があります。最初に「共有用途」専用のリポジトリhg clone した上で、そこから hg share を実行するのです。

$ hg clone URL-of-src shared
$ hg share shared cloned1
$ hg share shared cloned2
$ hg share shared cloned3

但し、この運用方法を採用した場合、作業用リポジトリの複製の際には、以下の様な手順を踏む必要があります。

  1. src リポジトリに対応する共有用リポジトリの有無を確認
  2. 無ければ hg clone で作成
  3. 共有用リポジトリから hg share で作業用リポジトリを複製

最初にルールを決めた上で、きちんと運用できれば良いのですが、多少なりとも手間の掛かる運用は、確認忘れやうっかりミスで、次第に徹底されなくなってしまう可能性が高いと言えます。

Automatic Pooled Storage for Clones

Mercurial 3.5 で導入された "Automatic Pooled Storage for Clones" により、share エクステンション有効時の hg clone の挙動は、以下のようになります。

  1. [share] pool 設定で指定されるディレクトリ直下を確認
  2. 複製元 src に対応する共有用リポジトリ (以下 "shared") が無ければ新規作成
  3. 複製元 src から shared に履歴を取り込み
  4. 共有用リポジトリ shared の履歴情報を参照する、複製先リポジトリを作成

必要なことは、事前に以下の2つの設定を、$HOME/.hgrc 等のユーザ毎設定ファイルに記述するだけですから、これなら確認忘れやうっかりミスの心配は無さそうです。

  • share エクステンションを有効化
  • 共有用リポジトリ作成先を [share] pool で設定

なお、この挙動は全ての hg clone 実行において適用されますので、hg clone の複製元が「ローカルホスト上のリポジトリ」であっても、複製先リポジトリは、共有用リポジトリと履歴を共有する構成になります。

共有リポジトリの識別方法

デフォルトの設定では、[share] pool ディレクトリ直下に作成される共有用リポジトリには、履歴の最初(= リビジョン番号 0)のリビジョンのハッシュ値が使用されます。

「複製元 src に対応する共有用リポジトリ」の有無は、このハッシュ値を使って判定されますが、これは、hg pushhg pull 等において、連携先が「由来が同じリポジトリ」か否かを判定する手法と同じものです。

このため、由来が同じ複数のリポジトリに対する hg clone は、全て単一の共有用リポジトリに集約されます。

一般的には、この挙動で問題ないと思いますが、場合によっては「URL 違いの連携先は由来が同じでも別連携先」扱いしたいことがあるかもしれません。

そのような場合は、[share] poolnaming 設定項目に remote を指定してください。この設定により、共有用リポジトリは、連携先 URL 毎に分離されるようになります。

なお、[share] poolnamingremote を設定した場合でも、[share] pool ディレクトリ直下に作成される共有用リポジトリの名前は、40桁のハッシュ値になります。

デフォルト設定の場合、このハッシュ値は「リビジョン 0 のハッシュ値」そのものですが、remote 設定の場合は、「連携先 URL 文字列をハッシュした値」なので、同じ40桁ハッシュ値であっても、意味が異なります。