Mercurial でセントラルリポジトリのマルチプルヘッドを禁止する


2011年 09月 09日

問題

Mercurial 独特の概念として、マルチプルヘッドというものがあります。

@  2:d9ae2b51cade | head b
|  default
|
| o  1:8141de67f6ee | head a
|/   default
|
o  0:6af850d9f57f | initial commit
   default
図1. defaultブランチがマルチプルヘッド(先端(HEAD)が複数存在している状態)

マルチプルヘッドは hg merge でマージして一つのヘッドに統合してからpushするのがマナーですが、push -fで強制的にpushすることができてしまいます。

[troter:~/tmp/multiple-head]
% hg push
pushing to /home/troter/tmp/central
searching for changes
abort: push creates new remote head 8141de67f6ee!
(did you forget to merge? use push -f to force)
[troter:~/tmp/multiple-head]
% hg push -f
pushing to /home/troter/tmp/central
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files (+1 heads)
[troter:~/tmp/multiple-head]
%

解決

mercurialにはリポジトリに変更を加える前、加えた後にフックスクリプトを実行できます。
フックスクリプトの実行のタイミングはhgrcのhooksの項で確認できます。

今回の例では「リモートからリポジトリに変更が加えられた際」に実行される pretxnchangegroup が利用できそうです。pretxnchangegroupは呼び出したフックスクリプトの終了ステータスが0以外の場合(正常終了以外の場合)トランザクションをロールバックします。

pushされた変更でマルチプルヘッドが作成されているかを検知し、マルチプルヘッドが作成されていたら exit 1する

スクリプトを作成しました。(参考にしたスクリプト)

元のスクリプトを改造し、「ユニファイしたブランチ数」と「ユニファイしていないブランチ数」を比較し、数が異なった場合はexit 1しています。

フックに登録する

セントラルリポジトリの.hgrcでフックを登録します。

[hooks]
pretxnchangegroup.forceonehead = /path/to/force-one-head

フックスクリプトを.hgディレクトリ以下に置いてある場合は次のように相対パスでしていもできます。

[hooks]
pretxnchangegroup.forceonehead = .hg/force-one-head

では改めて push -f してみましょう。


    [troter:~/tmp/multiple-head]
    % hg push -f
    pushing to /home/trote/tmp/central
    searching for changes
    adding changesets
    adding manifests
    adding file changes
    added 2 changesets with 2 changes to 2 files (+1 heads)
    There are multiple heads.
    Please 'hg pull' and get your repository up to date first.
    Also, don't 'hg push --force' because that won't work either.
    transaction abort!
    rollback completed
    abort: pretxnchangegroup.forceonehead hook exited with status 1
    [troter:~/tmp/multiple-head]
    %

禁止することができました!

謝辞

参考にしたスクリプト
を教えてくれた@voluntasさん、バグを指摘してくれた@yoppiblogさん、ありがとうございます。