「入門TortoiseHg+Mercurial」発売中(詳細は「執筆情報」参照)


マージプログラム

注意: 他環境での Mercurial インストール経験があっても、 別環境で Mercurial をインストールする際には、 以下の節を一通り確認することを強くお勧めします。

Mercurial 1.0 以降とそれ以前では、 マージプログラムに関する設定が大きく変更されています。

マージツール設定が無い場合、 1.0 版以降であれば、 (CVS や Subversion と同程度の) 非対話的 3-way マージを行う "internal:merge" (Python 実装による内部マージツール) が選択されます。

但し、 パッケージ管理ツールでインストールされた場合や、 バイナリパッケージをインストールした場合、 パッケージ作成者によって、 デフォルト設定が追加されている場合がありますので、 設定確認に多少の手間を掛ける必要があります: 設定確認は以下のコマンド実行で行います。

# (1)
$ echo ${HGMERGE}

# (2)
$ hg showconfig merge-patterns
# (3)
$ hg showconfig ui
ui.username=FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# (4)
$ hg showconfig merge-tools
# (5)
$ which hgmerge
which: no hgmerge in ............
$
マージコマンドの設定確認

確認項目は以下の通りです:

  1. 環境変数 "${HGMERGE}" が設定されていない
  2. Mercurial の "merge-patterns" 設定において、 対象ファイルと合致するパターンに対して、ツールが設定されていない
  3. Mercurial の "ui.merge" に対する設定が無い
  4. Mercurial の "merge-tools" 設定において、 何も設定されていない
  5. "hgmerge" コマンドが存在しない

以上の状況であれば、非対話的 3way マージが実施されます。

(4) 以降に何らかの設定が有るけれども (1) 〜 (2) での設定が無い場合は、 設定ファイルにおいて、 "[ui]" セクションの "merge" 項目を "internal:merge" に設定することで、 マージ実施のデフォルト挙動を、 非対話的 3way マージに設定することができます。

[ui]
merge = internal:merge
非対話的 3way マージ実行の設定

マージコマンド設定の詳細に関しては、 "hg help merge-tools" を参照してください。 日本語の言語設定が行われていれば、 日本語翻訳済みのヘルプが出力されます。

備考: 基本的には 1.0 版以降の使用を強くお勧めしますが、 0.9.5 版以前の Mercurial を使用されている方は「旧版利用時の設定」節をお読みください。

1.0 版対応ということで一旦削除した内容ですが、 メジャーな Linux ディストリビューションの 2008/05 時点での配布が 1.0 版以前のものであったりするらしいため、 再掲載することにしました。

なお、設定記述ファイルの格納先等に関する詳細は、 次節「設定ファイル記述」を参照してください。

簡易設定

以下のテーブルから、 各自の事情に適した「マージプログラム形式」を選択してください。

OS hg コマンド形式 マージプログラム形式
Linux/Unix Python スクリプト Linux/Unix コマンド
Windows Cygwin Python スクリプト Cygwin コマンド
Widnows コマンド
バイナリ(*.exe) Cygwin コマンド
Windows コマンド

Unix 系環境での同環境コマンドの利用

Linux/Unix での「Python スクリプト版 Mercurial ⇒ コマンド」利用や、 Windows での「Cygwin Python スクリプト版 Mercurial ⇒ Cygwin コマンド」 利用の場合、 設定は簡単です。

diff3 コマンドを使用する場合の設定例を以下に示します。

[ui]
merge = diff3

[merge-tools]
diff3.args = -m $local $base $other > $output
diff3 コマンド利用時の設定

衝突時におけるマージ結果ファイルの可読性の点からは、 "diff3.args" 指定に "-L local -L base -L other" を追加することをお勧めしますが、 GNU diffutils 以外の diff3 コマンド (例: OS 添付のものを使用する場合)は、 "-L" オプションによるラベル指定を受け付けない可能性がありますので、 注意が必要です。

Cygwin Python スクリプト ⇒ Widnows コマンド

Cygwin の Python スクリプト版 Mercurial から Widnows コマンドのマージプログラムを起動する場合、 パス名変換等の前処理が必要になります。 諸々の設定等が面倒であれば、 Windows バイナリ版の hg コマンドを使用しましょう。

パス名変換には、 (1) Cygwin の cygpath コマンドを用いてスクリプトか、 (2) cygwin32_conv_to_full_win32_path()cygwin32_conv_to_win32_path といった Cygwin のパス変換 API を使用した仲介コマンドを自作する必要があります。

注意: パス名に日本語を使用する場合、 「バックスラッシュ」と同じコード値を持つ文字の変換問題があるため、 cygpath コマンドも、 Cygwin パス変換 API も正しく機能しません。

個人的には、 このケースに該当する人はあまり居ないと思っていますので、 これ以上の解説は省略しますが、 パス変換する仲介コマンドの実装(Cygwinwrap の逆)は幾分面白そうな話なので、 機会があれば対応するかもしれません。

Windows バイナリ ⇒ Cygwin コマンド

Windows バイナリ版 Mercurial から Cygwin コマンド(ないしスクリプト)のマージプログラムを起動する場合、 マージコマンド起動の際に指定される Windows 形式のパス名を、 Cygwin コマンドは直接解釈できませんので、 パス名変換等の前処理が必要になります。

諸々の設定等が面倒であれば、 Cygwin の setup.exe 経由なり、 ソースからビルドするなりして、 Cygwin の Python スクリプト版の hg コマンドを使用しましょう。

Mercurial 導入の際と同様に、 Cygwinwrap を使用すればこの問題を解決可能です。 但し、Cygwinwrap はスクリプト起動専用なので、 実行バイナリを起動する場合は、 中間スクリプトが必要になります。

Windows 環境での同環境コマンドの利用

Windows バイナリ版の hg コマンドから、 Windows コマンドをマージツールとして使用する場合の設定は簡単です。

例えば Windows 版 GNU diffutils の diff3 を使用する場合の設定は:

[ui]
merge = diff3

[merge-tools]
diff3.executable = diff3.exe
# 必要に応じてフルパスを指定

diff3.args = -m $local $base $other > $output
diff3 利用向け設定

とすれば良いでしょう。 ちなみに、可読性の点からは、 "diff3.args" に "-L local -L base -L other" 指定を追加することをお勧めします。

kdiff3 を使用する場合の [merge-tools] セクション設定は、 Windows のバイナリ版 1.0 でインストールされる Mercurial.ini ファイルで既に設定済みですので:

[ui]
merge = kdiff3
kdiff3 利用向け設定

という記述だけで十分です。

なお、マージ対象のファイル名に空白文字が含まれる場合、 正しく機能しない可能性がありますので、 リポジトリ作成位置には注意が必要です。

マージ処理の詳細

Mercurial でのマージは、 各ファイルごとに以下の手順で実施されます。

  1. ツールの選択
  2. 事前マージの実施[merge-tools]premerge 設定) (NEW)
  3. マージの実施(= マージツールの実行)
  4. 衝突確認の実施[merge-tools]checkconflicts 設定) (NEW)
  5. マージ成否の問い合わせ[merge-tools]check 設定) (NEW@1.6)
  6. 変更確認の実施[merge-tools]checkchanged 設定) (NEW)

NEW」は、 Mercurial 1.0 以降で新たに追加されたものを表します。

ツールの選択

Merge Program」の 「How Mercurial decides which merge program to use」によれば、 「ツールの選択」は以下の手順で実施されます。

  1. コマンド行での "--tool" による指定(NEW@1.7)
  2. HGMERGE 環境変数による指定
  3. [merge-patterns] セクションの設定(NEW)
  4. [ui] セクションの merge 指定
  5. [merge-tools] セクションの priority 順(NEW)
  6. hgmerge コマンド (存在する場合)
  7. 内部的なマージツール(NEW)

Mercurial 1.7 版で merge/resolve コマンドそれぞれに "--tool" オプション指定が追加されたことで、 わざわざ設定ファイルを改変しなくても、 コマンド実行時に簡単にマージツールの切り替えが出来るようになりました。

Mercurial 1.0 (= 以後「新形式」)での改善点は以下の通りです。

引数形式が指定可能になった:

これまで(= 以後「旧形式」)は、 マージツールに指定される引数が "local base other" 固定でしたので、 (1)この順序での引数を受け付けられなかったり、 (2) "local" に対する結果上書きを行わないツールを使用する際には、 中間スクリプトやバッチファイルを作成して、 引数の入れ替えやバックアップファイルの作成を行う必要がありました。

しかし新形式では、 [merge-tools] セクションで "xxxx.args" 設定を記述することにより、 ツールに応じて柔軟に対処することが可能です。

例えば 「xxxx.args = -o $output $other $base $local」 と記述することで、 マージに xxxx が使用される際には、 (1)引数順序の入れ替えと、 (2)"-o" オプションによる出力先の指定が行われます。

先にも触れましたが、 引数指定に "$output" が記述されない場合は "$local" が上書きされることが期待されます。

ファイル形式でのツール使い分けが可能になった:

旧形式では、 全てのマージが単一のツール起動に集約されたため、 ファイル形式に応じたツールの使い分けは、 自前で振り分けを行う必要がありました。

しかし新形式では、 [merge-patterns] セクションでの拡張子毎設定に応じて、 異なるマージツールが起動されるようになっています。

例えば、 画像や音声のような「バイナリデータ」や、 HTML や PostScript といった「可視化した方が編集しやすいもの」は、 専用のツールでマージすることが可能になります。

内部マージツールの強化:

旧形式では、 常に外部(= hg コマンド以外) のマージツールを用意する必要がありました。

新形式では、 マージツールとして、 以下の「内部」ツールを指定することが可能です。

「事前マージ」は 「事前マージの実施」を、 「事後確認」は 「衝突確認の実施」/ 「マージ成否問い合わせの実施」/ 「変更確認の実施」を意味します。

「無し」とあるものは、 設定の有無に関わらず、 それらを実施しません。

名称 機能 事前マージ 事後確認
internal:local 作業ディレクトリ側(local) のファイルを残す 無し 無し
internal:other マージ対象チェンジセット側(other) のファイルを残す 無し 無し
internal:prompt local と other の選択を対話的に実施 (NEW@1.3) 無し 無し
internal:fail 常にマージを失敗させる 無し 無し
internal:merge Python 実装による非対話的 3way マージ 有り 有り
internal:dump 共通祖先/マージ対象/作業領域側の3つのファイルを個別に書き出す (NEW@1.3) 有り 無し

例えば、 [merge-patterns] 設定と組み合わせることで、 「特定拡張子のファイルのマージは常に失敗させる」 といったことも可能になります。

ちなみに、 [merge-patterns] セクションでのパターンマッチングで決定されたマージツールは、 [merge-tools] セクションにおける binary 可否設定が無視されます。これは:

などという無意味な設定を無視するためなのでしょう。

[merge-patterns] セクションおよび [merge-tools] セクションの記述に関しては、 "hg help config" 出力を参照してください。 日本語の言語設定 (※ 環境変数 LANG=ja 設定等) が行われていれば、 日本語翻訳済みのヘルプが出力されます。

事前マージの実施

テキストファイルの場合、 [merge-tools] において、 選択されたツールに対する "premerge" 設定が False になっていないのであれば、"internal:merge" 相当による非対話的 3way マージによるマージが実行されます (デフォルト値は True)。

ここで衝突(conflict)が検出されない場合、 当該ファイルに対するマージは 「成功」とみなされ終了してしまいますので、 指定ツールを必ず実行したい場合は、 当該ツールに対する "premerge" 設定を False にしてください。

マージの実施

選択されたツールを使用して、 マージ処理が実施されます。

この時の「マージの成功」とは、 「ファイル内容の衝突が解消された状態」を指します。

例えば、 merge コマンド(ないし diff3 コマンド)が衝突を検出した場合:

<<<<<<< local.txt
good morning world
||||||| base.txt
hello world
=======
good night world
>>>>>>> other.txt
merge コマンドによる衝突検出

衝突部分に上記のような 「衝突マーク」+衝突内容を記録したものをファイルに保存し、 終了コード 1 で終了します。 つまり、 merge コマンドの実行自体は成功していたとしても、 「人手を介さずにはファイル内容の衝突を解消できない」 = マージ失敗と判断しているわけです。

Mercurial はマージツールに対して、 「人手を介さずにはファイル内容の衝突を解消できない」 状態が残っている場合に、 終了コード非0で終了することを期待しています。

衝突確認の実施

選択されたツールが「マージ成功」 を報告した(= 終了コードが 0 だった)場合でも、 当該ツールに以下の設定がある場合、 結果ファイルの内容に「衝突マーク」が残されていないかを確認します。

ファイル I/O の成否のみで終了コードが決定されるようなツールの場合、 この設定を有効にすることで、 マージ成否の判定が可能になります。

ちなみに、 わざわざそんなファイルを作ることはないと思いますが、 本来のファイル内容に、 行頭が以下の文字列で始まる行が含まれる場合は、 当然「マージ失敗」とみなされますので注意してください (1.0 版時点では "|||||||" はチェック対象に含まれていない模様)。

マージ成否問い合わせの実施

当該ツールの "check" 設定で "prompt" が列挙 (NEW@1.6) されている場合、 マージ実施後に常に対話的な「マージ成否の問い合わせ」が実施されます。

注意: この問い合わせが実施された場合は、 当該ツールの設定状況に関わらず、 次に述べる「変更確認の実施」は行われません。

つまり、 "check" 設定での "prompt" 列挙は 結果的に "changed" 列挙を破棄します。

変更確認の実施

選択されたツールが「マージ成功」 を報告した(= 終了コードが 0 だった)場合でも、 当該ツールに以下の設定がある場合、 結果ファイルの内容がマージ前後で更新されているこを確認します。

更新されていない場合、 マージ処理が不適切であった可能性があるとみなし、 コマンド行で「マージの成否」を問い合わせてきますので、 非対話的に実行したい場合(例えば Emacs から利用する場合等)は注意してください。

旧版利用時の設定

以下の情報は、 Mercurial 1.0 版以前の環境向けに書いたものです。

基本的には 1.0 版以降の使用を強くお勧めしますが、 環境要因(OS バージョン・管理者の気分等々) で制約を余儀なくされている場合に、 以下の情報がお役に立てば幸いです。

diff3 ベースの非対話的マージプログラム

Mercurial に標準添付のマージプログラム hgmerge は、 チェンジセット間で衝突が検知されると、 diff3 コマンドの実行結果を元にエディタを起動して、 衝突ファイルごとに対話的に衝突の解決を求めてきます。

「こまめにコミットすれば衝突は最小限」 という方針なのかもしれませんが、 個人的には衝突があったファイル全般を見渡しながら解決を行いたいので、 CVS のように diff3 ベースで非対話的にマージを行うプログラムを作成してみました。

注意: 下記のソースは、 私の心境変化もあり、 当初公開時のものとは微妙に変更してあります。

衝突検出時の戻り値の変更:
Mercurial を使い出した当初は、 マージツールが衝突を検出しても正常復帰 (exit コード 0)の方が好みだったのですが、 最近は相応の情報が表示された方が安心できる、 という心境になっています。
バックアップファイル名の簡略化:
当初は CVS に倣う形で、 ".#元ファイル名" としていましたが、 revert 時等で ".org" 付きファイルが作成されたりしているので、 ここだけ頑張る必要性が(私の中で)薄れた感があります。
#!/bin/sh

case $# in
0|1|2)
    echo "USAGE: $0 own base another"
    exit 1
    ;;
*)
    ;;
esac

own=$1
base=$2
another=$3
backuped="${own}.org"

label_own="recent in local"
label_base="base in repository"
label_another="recent in another"

mv "${own}" "${backuped}" || exit 1

diff3 -m \
    -L "${label_own}" "${backuped}" \
    -L "${label_base}" "${base}" \
    -L "${label_another}" "${another}" > ${own}

# ATTENTION:
#   DIFF/DIFF3 RETURN
#     0: same(or no conflict)
#     1: found conflict
#     2: otherwise(= error)

case $? in
0)
    rm "${backuped}"
    exit 0;;
1)
#    rm "${backuped}"
    exit 1;;
*)
    # restore original file
    mv "${backuped}" "${own}"
    exit 1;;
esac
diff3merge.sh

マージスクリプト起動用補助プログラムのインストール

Windows バイナリ版 Mercurial を使用する場合、 マージプログラムには Windows 形式のパス名が渡されます。

そのため、 スクリプトのマージプログラムを Windows 環境下で使用する場合には、 マージプログラム起動に CygwinWrap を経由させるなどして、 起動時に指定されたパス名を Windows 形式から POSIX 形式に変換しなければなりません。

Cygwinwrap のアーカイブをダウンロード/展開した後に、 以下の手順でインストールしてください。 なお、 マージスクリプトは sh によるスクリプトと仮定 (その他のスクリプトの場合は、 アーカイブに同梱される README.ja.txt 等を参照してください)し、 ファイル名を "${MERGE}.sh"、 このスクリプトが格納されているディレクトリを "${MERGE_BIN}" と表記した場合の導入方法を以下に示します。

% make shwrap.exe
% cp ./shwrap.exe ${MERGE_BIN}/${MERGE}.exe

次節「設定ファイル記述」へ