あなたの知らない(筈の) Mercurial テンプレート
このエントリは、Mercurial Advent Calendar 2012 の 19 日目です。例によって、エントリ公開の時点で日付が変わってしまっていますが、私が寝るまでが 12 月 19 日です。
暫く前になりますが、開発者用MLに『テンプレート機能を拡張したよ!』という内容のメールがMatt から投函されました。
これまでは、スタイルファイルを書いたり、場合によってはフィルタ関数を自前のエクステンション経由で追加したりしないとできなかったことが、かなりの部分まで有りモノで、しかもコマンドラインから、できるようになる機能拡張です。
default ブランチで実装されたこの拡張機能、メジャーリリースを経て無事 stable にもマージされたのですが、一向に使用方法が文書化される兆しがありません。
うーん、暫くは隠し機能のままにしておくのかな?
でも、そういう非公開機能を使うのって、なんだかわくわくしますよね?(笑)
※ 2013/02 にリリースされたバージョン2.5から、オンラインヘルプへの記述が追加されましたので、『知らない(筈)』ではなくなってます
マップ機能
『マップ』(map)機能は、従来の一覧系テンプレートキーワードに対する拡張になります。
例えば、従来のテンプレート機能での {files}
指定は、『ファイル一覧の空白文字区切り』でした。
区切り文字を変更するためには、スタイルファイルを書いたりする必要があったのですが、マップ機能を使うことで以下のような指定が可能になります。
$ hg log -r 0b241d7a8c62 \ --template "files:\n{files % ' {file}\n'}" files: mercurial/templatekw.py mercurial/templater.py tests/test-command-template.t
肝は {files % ' {file}\n'}
部分で、{テンプレートキーワード % フォーマット文字列}
という形式になっています。
テンプレートキーワード
部分は、従来通りのテンプレートキーワードです。詳細は hg help template
か、その HTML 版を参照してください。
但し、マップ機能は、列挙("list of")系のキーワードでしか効果を持ちません。date
とか author
とかを指定しても、通常指定と同じ挙動になります。
フォーマット文字列
には、テンプレートキーワード
の各要素を、どのような形式でフォーマットするかを指定します。
文字列の中には、テンプレートキーワード
ごとに、列挙要素を指す以下のキーワードを指定できます。
bookmarks
:{bookmark}
children
:{child}
file_add
:{file}
file_copy
:{file}
file_del
:{file}
file_mod
:{file}
tags
:{tag}
parents
が抜けていますが、実は parents
にマップ機能を適用した場合、要素の表示は多分期待通りのものでは無い筈です。
$ hg log -r c8326ffdcb4f \ --template "{parents % '{parent}\n'}" rev18036node8b846dbc57b6c25247e733e00f0e7766b900a3ff rev18030nodeebc0fa067c07808b77f060e285d0c9d8d25c6750 $
また、通常の parents
キーワードの挙動と同様に、親とリビジョン番号が連続している場合は、何も表示されませんので注意してください。
# 親リビジョンの表示には p1rev
や p1node
がお勧め
フォーマット文字列
部分には、マップ機能で指定された テンプレートキーワード
と関係ないキーワードでも、通常のテンプレートキーワード(例えば author
)に合致すれば、そのキーワードは置換されます(テンプレートフィルタも適用可能)。
ちなみに、内部解析処理に一塊の文字列として認識させるために、フォーマット文字列
部分は必ず引用符で囲まれている必要があります。
しかし、Windows のコマンドプロンプトでは、ダブルクォートのみが引用符として認識されるため、テンプレート指定全体はダブルクォートで囲むことになります。そのため、可搬性のあるテンプレートを書こうとすると、フォーマット文字列部分はシングルクォートで囲まざるをえません。
普段から、このような癖をつけておくと、どの環境でも正しく動くテンプレート指定を書けることでしょう(笑)。
もっとも、Unix 環境ではシェルによる置換の可能性があるので、個人的には外側にダブルクォートを使うのは、好きでは無いのですが……
join
機能
結構な範囲のことは、マップ機能でできてしまうのですが、マップ機能は『最初の要素』とか『最後の要素』を区別できないので、ぶっちゃけた話をすれば、カンマ区切りの列挙とかができません。
そんな場合には join
機能を使いましょう。
$ hg log -r 0b241d7a8c62 \ --template "{join(files, ', ')}\n" mercurial/templatekw.py, mercurial/templater.py, .....
例によって、肝となるのは {join(files, ', ')}
部分です。
見れば何となく分かるとは思いますが、{join(連結対象, 区切り文字列)}
という形式になります。
連結対象
には、通常のテンプレートキーワードでも良いですし、先述したマップ機能も使用できます。もっとも、join
で結合するなら、わざわざマップ機能を使う必要はないと思いますが……
さらに突っ込んだ話としては、波括弧({ }
)内部の、引用符によって囲まれていないものは、テンプレートキーワード/フィルタ、あるいは本エントリで説明しているマップ記述や各種関数とみなされます。
例えば『コミットユーザのメールアドレスで区切ったファイル一覧』などという、アホなテンプレート指定も可能です。
$ hg log -r 0b241d7a8c62 \ --template "{join(files, author|email)}\n"
sub
機能
sub
機能は、正規表現による文字列置換機能です。
$ hg log -r 0b241d7a8c62 \ --template "{sub('mercurial/', 'hg/', files)}\n" hg/templatekw.py hg/templater.py tests/test-command-template.t
sub
の引数は順に、置換対象正規表現("mercural/"
)、置換結果("hg/"
)、置換対象(files
)となります。
この場合の files
は、デフォルト挙動の『空白区切りで連結』されたものが、置換対象として使用されます。別な文字で区切りたい場合は、join
適用なり、マップ機能なりを使用してください。
$ hg log -r 0b241d7a8c62 \ --template "{sub('mercurial/', 'hg/', join(files, ', '))}\n" hg/templatekw.py, hg/templater.py, tests/test-command-template.t
if
および ifeq
機能
if
は if(条件, 成立時出力, 非成立時出力)
形式で、ifeq
は ifeq(条件, 判定対象, 成立時出力, 非成立時出力)
という形式で使用します。
非成立時出力
引数部分は省略可能です。
if
では、条件
部分が空で無い場合に、ifeq
では、条件
と 判定対象
が一致していた場合に、条件成立とみなされます。
例えば、変更ファイルが無いリビジョンでは files:
見出し自体を出したくない、という場合は:
{if(files, ' files:\n')}{files % ' {file}\n'}
あるいは、stable ブランチのコミットに対して "bugfix" を表示する場合は:
{ifeq(branch, 'stable', 'bugfix')}
といった使い方ができます。
ここでも、各引数部分が引用符で囲まれていない場合は、テンプレートキーワード/フィルタ、マップ記述や各種関数とみなされます。
date
と fill
のパラメータ化
テンプレートフィルタの date
と fill
に対して、パラメータ指定ができるようになりました。
これに関しては、Matt のメールでの実行例を見れば、特に説明がなくてもわかると思います。
$ hg log -r 0 --template '{date(date, "%Y")}\n' 2005 $ hg log -r 0 --template '{fill(desc, "30")}' Add back links from file revisions to changeset revisions Add simple transaction support Add hg verify Improve caching in revlog Fix a bunch of bugs Self-hosting now that the metadata is close to finalized $
まとめ
一般ユーザ向けには文書化されていない、Mercurial の新しいテンプレート機能を使ってみましたが、どうでしたでしょうか?
新しい機能の導入により、テンプレートの適用範囲がこれまでよりも広がることと思われます。