彷徨えるフジワラ

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

(特に Git 併用ユーザには是非読んでおいて欲しい) Mercurial における『ブランチ』の概念 〜 その4

今回は "topological branch" の話。

以前は原語の "topological branch" を正確に直訳した『位相(幾何学)的ブランチ』という呼称を用いていたが、現在はオンラインヘルプも含めて『構造的ブランチ』という呼称を用いるようにしているので、旧来の呼称に馴染んでいる人は要注意。

以下、各回で説明する主なトピック:

  • その1: 名前無しブランチ
  • その2: 名前付きブランチ
  • その3: ブックマーク
  • その4: 構造的ブランチ(本エントリ)
  • その5: 名前付きブランチ運用で必要なトピック
  • その6: 名前付きブランチに関するちょっと踏み込んだ話



構造的ブランチ

再度の確認となるが、リビジョン X が『名前無しブランチ』となる条件は以下の通り。

  • X の親リビジョン P が、P と同一の『名前付きブランチ』内に、X 以外の子リビジョンを最低1つ持つ
  • リビジョン X と P が、同一『名前付きブランチ』に属する

この条件から、『名前付きブランチ』に関するものを取り除いて:

  • X の親リビジョン P が、X の他に子リビジョンを持つ

という、その1時点での定義だけで『枝分かれ』を判定したものを、『構造的ブランチ』 (topological branch) と呼ぶ。一般的な感覚で言えば、『枝分かれ』=『構造的ブランチ』であろう。

『名前無しブランチ』の作成は、新たな『構造的ブランチ』の作成と等価であるが:

『名前付きブランチ』の作成は、必ずしも新規の『構造的ブランチ』を作成するとは限らない場合がある。

例えば、単一『ヘッド』な "default" ブランチの最新成果 D2 の上に、『名前付きブランチ』として "BRANCH" を作成した場合:

この時点では履歴が直線であることから、『名前付きブランチ』"BRANCH" の作成は、新たな『構造的ブランチ』の作成とはならないのだ。

ここで:

"default" 側で開発が継続されているのであれば、早晩 "default" 側にもリビジョンが追加される筈

と考えるならば、想定上の(未来の)履歴ツリーは以下のようになるので、"BRANCH" は『枝分かれ』していると考えても差し支えない。

『名前付きブランチ』の名称における『ブランチ』は、将来的な『枝分かれ』の可能性に期待(笑)しての『ブランチ』と言える: この点は Git における『ブランチ』も同様の筈。

但し、単に『なし崩し的に』そういう呼称を使うことになった可能性も高く、その2でも書いたように、『名前付きブランチ』のことを『リビジョングループ』と呼んでいれば、もっと概念体系的には簡単になったかもしれない。

ブランチヘッドと構造的ヘッド

"hg heads" のデフォルトの挙動で表示されるリビジョンは:

同一の『名前付きブランチ』に属する子リビジョンが無いリビジョン

上記のように定義されるが、Mercurial ではこの定義に該当するリビジョンを『ブランチヘッド』と呼んでいる。

『ブランチヘッド』の条件から『名前付きブランチ』に関する条件を取り除いた:

子リビジョンを全く持たないリビジョン

その1時点での簡略化した『ヘッド』定義と同じ、上記の定義に該当するリビジョンは『構造的ヘッド』と呼ばれている: 『名前無しブランチ』〜『構造的ブランチ』の対比も参照。

"hg heads" のデフォルト挙動が、『構造的ヘッド』では無く『ブランチヘッド』の表示なのには理由がある。

例えば、先述した以下の状態において:

『構造的ヘッド』(= 子リビジョンを持たない)か否かで判定した場合、『名前付きブランチ』 "BRANCH" の最新リビジョン B1 のみが『ヘッド』と判定され、"default" からは『ヘッド』が消失してしまう: D2 は『構造的ヘッド』とはみなされない。

しかし、以下の図のような今後の履歴追加を想定した場合:

"default" 内には子リビジョンを持たない D2 も、(潜在的な)『ヘッド』とみなした方が(利用者の感覚的には)良かろう、というわけだ。

上記の例は、『名前付きブランチ』の作成による『枝分かれ』元(= "default")における『ヘッド』消失であったが、これとは逆に、『名前付きブランチ』間でのマージが発生した際も:

『構造的ヘッド』か否かで判定した場合、B3 リビジョンが作成されるまでは、マージ元となった『名前付きブランチ』 "BRANCH" 側において『ヘッド』が消失してしまうことになる。

例えば、『名前付きブランチ』内に複数の『ヘッド』があり、且つそれらが他の『名前付きブランチ』の『枝分かれ』元になるなどして、『構造的ヘッド』ではなくなっているような状況では、単に『名前付きブランチ』の最新リビジョンに移動して作業を継続すればよい、というわけにはいかない: 各『ヘッド』をきちんと把握した上で、マージ要否等を判断する必要がある。

『ヘッド消失』の何が困るかというと、上記のような状況が容易に検出できなくなってしまうところだ。

"hg heads" のデフォルト挙動が『ブランチヘッド』か否かの判定であることにより、『名前付きブランチ』内には『構造的ヘッド』が無くなってしまうようなケースでも、『ヘッド』の状況を正確に把握することが可能になる。

なお『常にこの挙動が妥当か?』というと、必ずしもそうとは限らないので、"hg heads" コマンドは、純粋に『構造的ヘッド』 のみを表示するための "--topo" ("topo"logical) というオプションを持っている。

※ 以下、"その5" に続く