hg now を作ったよ(バグあり)

Mercurial 界の海老蔵こと @h0k0r0bi です(……いつの間にそういうことに?)
この記事は、 @cointoss1973 さんの パッチ管理リポジトリ入門 〜MQ(パッチ)もMercurialで管理できるよ〜 - cointoss.log のバトンを受け取った Mercurial Advent Calendar 2012 の5日目です。

なにこれ

hg now というエクステンションを作りまして、こんなふうに考えて作ったよ、こんなバグがあるよというお話をします。
なんとなく動くものができたというだけの状態なので、おかしいよ! まずいよ! という突っ込み大歓迎!
元ネタは tmp コミットのための独自サブコマンド git-now - アジャイルSEを目指すブログ です。

参考

万年初心者なので、何かエクステンションを作ってみようと思うたびにお世話になっています。

方針

hg now [TEXT] の実行で、コミットメッセージに [from now] 2012-12-08 12:34:56 TEXT と diff の内容を入れてコミットできるようにします。
その他オプションはほしくなったらおいおい追加。

じゃあ作ってみよう

diff の取得

Mercurial のソースから diff を探してみます…… mercurial/commands.py で def diff を発見。

def diff(ui, repo, *pats, **opts):

実体は cmdutil.diffordiffstat を実行しているようなので、mercurial/cmdutil の diffordiffstat を見てみます。

def diffordiffstat(ui, repo, diffopts, node1, node2, match,
                   changes=None, stat=False, fp=None, prefix='',
                   listsubrepos=False):

fp にファイルを指定して cmdutil.diffordiffstat を実行すれば diff をファイルにはき出してくれるみたいです。

commit の実行

やっぱり mercurial/commands.py の commit を覗いてみます。

def commit(ui, repo, *pats, **opts):

コミットメッセージのオプションは、以下のような感じだから opts['message'] に diff の内容を入れて commands.commit(ui, repo, *pats, **opts) を実行すればいいんでしょう。

commitopts = [
    ('m', 'message', '',
     _('use text as commit message'), _('TEXT')),
    ('l', 'logfile', '',
     _('read commit message from file'), _('FILE')),
]

そんな感じで 最初の動くバージョン ができあがります。

cp932 意外の文字コードは NG

「こいつ……動くぞ!」と使っていたら案の定エラー発生。

M:\nowtest>hg now --debug
[win32mbcs] activated with encoding: cp932
nowtest_utf8N.txt
トランザクションを中断します!
ロールバックを完了しました
中断: decoding near '
+縺ゅ>縺・∴縺・
': 'cp932' codec can't decode bytes in position 224-225: illegal multibyte sequence!

UTF-8 のファイル nowtest_utf8N.txt の diff がコミットメッセージに入っているけど、cp932 ではデコードできないよと。うん、確かに。

diff の中身を cp932 に変換する

diff の文字コード変換の話をツイートしていたら yuja / hgext-textful / source / - Bitbucket でやってるよと教えていただいたので hgext/textful/encoding.py の filterencoding を 拝借

これでバッチリじゃなイカ! と思っていた時期が私にもありました。

BOM あり UTF-8 はやっぱり駄目

BOM あり UTF-8 だと同じようなエラーが出てしまいます。

M:\nowtest>hg now --debug
[win32mbcs] activated with encoding: cp932
nowtest_utf8.txt
トランザクションを中断します!
ロールバックを完了しました
中断: decoding near ' +1,1 @@
+・ソ縺ゅ>・: 'cp932' codec can't decode bytes in position 214-215: illegal multibyte sequence!

これは、バイナリの BOM もテキストとして diff 出力してしまうので、その後に文字コード変換をしてもどうしようもないよということだと思います。
なので、diff 出力時点での変換が必要なんでしょうね。
hgext-textful では、そこもうまいことやっているようですが、私にはどうしたら良いのかわかりませんでした。

まとめ

  • こんなゆるふわな感じでエクステンションは作成できます。
  • 誰かバグ取りして!!

明日は @yujauja さんの「リポジトリ変換 or hgweb」です。