フックを作ってみる。

Bazaarの特徴として、プラグインによって柔軟に機能を拡張できることが上げられます。

「フック」は、ほとんどのVCSが持っている、さまざまな処理の中に独自のチェックや処理を挟み込める仕組みのことですが、Bazaarの場合は、フックもプラグインとして実装します。

どんなフックがある?

以下のコマンドで、作ることができるフックの種類をリストアップすることができます。

bzr help hooks
また、以下のコマンドで、実際にシステムにインストールされているフックをリストアップすることができます。
bzr hooks

実装してみる。

例えば、コミットメッセージに問題が無いかチェックをするためのフックを作ってみましょう。
Bazaarのプラグインディレクトリにtest_hookディレクトリを作成し、その中に __init__,pyというファイルを置きます。
このファイルに、フックの処理を実装します。

 - Plugins
   + test_hook
     + __init__.py

さっきの bzr help hooks の出力結果を見ると、pre_commitフックについては以下のように書かれています。

BranchHooks
-----------
・・・(略)・・・

pre_commit
~~~~~~~~~~
Introduced in: 0.91

Called after a commit is calculated but before it is completed.
pre_commit is called with (local, master, old_revno, old_revid,
future_revno, future_revid, tree_delta, future_tree). old_revid is
NULL_REVISION for the first commit to a branch, (以下略)

とりあえず、pre_commitフックは、以下の引数を持つ関数にしなさいということのようです。t

def hook_name(local, master, old_revno, old_revid, 
              future_revno, future_revid, tree_delta, future_tree):
    """pre_commitフックの実装"""

この通りの関数を作って、それをインストールしてやれば、pre_commitフックになります。ヘルプによると、pre_commitはBranchHookのカテゴリに含まれているようなので、そこにインストールします。

def my_pre_commit(local, master, old_revno, old_revid, 
                  future_revno, future_revid, tree_delta, future_tree):
    """pre_commitフックの実装"""

# my_pre_commit_hookを、Branchフックとしてインストールする
from bzrlib.branch import Branch
Branch.hooks.install_named_hook(
                    'pre_commit', my_pre_commit,
                    u'私の名前はロムスカ・パロ・ウル・ラピュタ')

これでフックの雛形が完成しました。 bzr hooksをたたけば、このフックも表示されます。
あとはmy_pre_commit_hookの中身を実装するだけですね。
コミットメッセージの中にNGワードがあったらコミットを拒否するようにしましょう。

という訳で完成版(for Windows)です。
Linuxの場合は".encode('mbcs')"を削ればいいはず。

# -*- coding:utf8 -*-
from bzrlib.errors import BzrError

face = u"%s\n r;;ノヾ\n ヒ‐=r=;\n 'ヽ二/"
mega = (u"%s\n  / ̄`ー 、\n  /    E三ヽ\n l  ,.、 ⊂ニヽl\n"
         u"  〉l 。し⊂ヽ. l\n.' ヾ、ヽ,r‐、/ <ー--\n."
         u"゙、 ヾ、i、゙ー/ / l\n  ヽ--y゙ー'、_ヽ∠_")

ng_words = [(u"バルス", u"目がぁー!目がぁ〜〜〜〜", mega),
            (u"バカ ばか 馬鹿", u"馬鹿共にはいい目くらましだ。", face),
            (u"ゴミ ごみ", u"見ろ!人がゴミのようだ!", face)]

def my_pre_commit_hook(local, master, old_revno, old_revid, 
                       future_revno, future_revid, tree_delta, future_tree):
    # コミットメッセージの中に不適切な単語が含まれていたら、
    # コミットを拒否する
    msg = (local or master).repository.get_revision(future_revid).message
    for words, m, f in ng_words:
        for word in words.split(' '):
            if msg.find(word) >= 0:
                raise BzrError((f % m).encode('mbcs'))

from bzrlib.branch import Branch
Branch.hooks.install_named_hook(
                    'pre_commit', my_pre_commit_hook,
                    u'私の名はロムスカ・パロ・ウル・ラピュタ')

やっぱり仕事でコミットメッセージに「バカ」とか「ゴミ」とか使っちゃだめですよね!