複数ルートなブランチの全ての差分の取得
本ブログエントリで最初に思い付いた暫定版を公開してから、色々指摘を受けて修正した結果、以下の方法なら行けそう、という結論に達しました。
# 対象ブランチ名を指定 BRANCH=foo hg log -r "roots(branch($BRANCH))" --template "{rev}\n" | while read root do # 複数ヘッド化を検出する場合はここで実施 hg log -r "heads($root:: and branch($BRANCH) and not ($root:: and not branch($BRANCH))::)" --template "$root {rev}\n" done | while read start end do hg diff -r "p1($start)" -r "$end" done
外部スクリプト無しで、ぎりぎりワンライナー化できる範囲ではありますね。
(2014-08-22 追記) テンプレート関数 revset()
を使った書き換えに関するエントリを公開しました。
上記は POSIX 系環境での B shell 系シェルによる実行を前提にしたものですが、いわたさんが Windows 向けのものを公開しています。
記述内容解説
2つの revsets 記述のうち:
"roots(branch($BRANCH))"
最初の方は以下のような意味を持ちます。これは簡単ですね。
$BRANCH
に属して、且つ- (対象リビジョン= $BRANCH なリビジョンの中に)親を持たない
一般的な運用では、上記条件に該当するリビジョンは1つになるのですが、Mercurial 自体は「複数ルートな名前付きブランチ」を扱うことができます。詳細はこのエントリを参照してください。
この図で言うと、最初の revsets 記述は B1 と B3 を抽出します。
本エントリの発端となった元々の要望が、このような複数ルートな状況を想定してるので、最初の revsets で抽出したルート情報を元に、2つ目の revsets 記述を適用します。
"heads($root:: and branch($BRANCH) and not ($root:: and not branch($BRANCH))::)"
root が B3 であれば、$root:: and branch($BRANCH)
によって、B3, B4 が抽出されますが、root が B1 の場合、B1, B2 以外に、一旦 default を経由する B3, B4 も含まれてしまいます。
さて、"$root:: and not branch($BRANCH))
" は:
$root
の子孫で、且つ$BRANCH
に属さない
ですので、root が B1 なら D3 〜 D6 が抽出されますが、これの末尾に "::
" を付けることで、D3, D4 の子孫である B3, B4 も抽出対象に含まれます。
冒頭2つの述語で抽出したものに対して、この結果を and not
することで、(B1, B2, B3, B4) and not (D3, D4, D5, D6, B3, B4)
⇒ (B1, B2)
が抽出されます。
最終的には、これに heads()
を適用しますので、B2 が抽出されることになります。
以上で、複数ルートな名前付きブランチに対して、それぞれの部分ツリーの root/head 対を抽出することができるようになりました。
再利用向けに、revsetalias での定義も載せておきます。
[revsetalias] # $1 を起点とする子孫のうち # $2 の条件を満たす集合の中で、 # $1 から連続しているもののみを抽出 descendantsin($1, $2) = (($1)::) and ($2) and not (((($1)::) and not ($2))::)
この revsetalias 定義を使えば、2つ目の revsets 記述を "heads(descendantsin($root, branch($BRANCH)))
" と簡略化できます。
revsetalias 定義で括弧を多様してるのは、任意の revsets 表記に対して、演算子の優先順位の問題を回避するためです。C/C++ でのマクロ引数の扱いみたいなものですね(笑)