彷徨えるフジワラ

年がら年中さまよってます

ファイル内容をマージしないマージ

Mercurial 2.1 リリース絡みのドタバタで間があいてしまったけれど、TokyoMercurial #1 で話題になった件の詳細シリーズ - その5。

"hg merge" でリビジョンの枝分かれをマージする際に、一般的な設定であれば、いわゆる『3-way マージ』と呼ばれる方法で、共通の祖先時点の内容に対して、それぞれの枝分かれにおける変更内容が統合される。

しかし、場合によっては、『マージ先の枝』(第1親側)ないし『マージ対象の枝』(第2親側)での変更内容を破棄してしまいたい (= 反対側の内容のみを残したい) 事もある。

以下は、そういった場合におけるコマンド実行の方法についての説明。

マージ実施時点で内容を残したい枝が確定している場合

まずは『マージを実施するが、マージ結果は完全に、どちらか一方の枝の内容のみを残したい』というケース。

単に『内容を破棄する方の枝を、マージしないで放置』しておけば良いのだから、こんなケースはちょっと想像が付かないのだけれど、close できる名前付きブランチ ("hg commit --close-branch") と違って、名前無しブランチは "hg heads" とかにも表示されてしまうから、ツリーの綺麗さに拘る人だと、『マージして抹消してしまいたい』と思うかもね。
このケースでは、"hg merge" によるマージ実行時点で残したい枝が確定している事から、残したい方の枝に合わせて、以下のいずれかを --tool オプションで指定してやれば良い。

  • 第1親側を残したい ⇒ "internal:local"
  • 第2親側を残したい ⇒ "internal:other"

以下は第2親側を残す場合の実行例:

$ hg merge --tool internal:other マージ対象リビジョン

上記の指定方法だと、全てのファイルにおいてどちらか一方の内容のみが保持されてしまう。

『どちらの内容を残すかがファイル毎に異なる』場合は "internal:prompt" を指定すればよい。

常時使用するマージ方式であれば、設定ファイル等で記述しておいた方が便利だけれど、上記のような特殊ケースに関しては、--tool オプションでの一時的な指定の方が妥当な筈。

衝突したファイルに対してのみ一方の枝の内容を残したい場合

"hg merge" によるマージで衝突が発生した場合に、衝突が検出されたファイルに対して、一方の枝の内容だけを残したい場合は、先述したマージツール ("internal:〜") を --tool オプションで指定しつつ "hg resolve" を実施すれば良い。

以下は第1親側を残す場合の実行例:

$ hg resolve --tool internal:local 対象ファイル

"hg merge" と異なり対象ファイルが指定できる "hg resolve" の場合は、"internal:prompt" の出番は無い筈。

ちなみに、『衝突未解決の全てのファイル』を "hg resolve" の実施対象とするために、対象ファイル名を省略した場合は、--all 指定が必要なので注意 (※ タイプミス等による予期せぬ実行の防止)。

なお、衝突していないファイルに対して、上記のような対処を行う場合、衝突したファイルの場合と同様に、マージ方式指定付きで "hg resolve" を実施しても良いけれど、残したい方の(親)リビジョンを指定して:

$ hg revert -r REV ファイル名

上記のように実行する方が簡単 (= タイプ量が少ない) だと思う。

revset 機能を使用すれば、第1親は ".^1"、第2親は ".^2" で指定できるので、わざわざリビジョン番号等を調べる必要も無いし。