Pythonコードを3-way mergeする場合にKDiff3とP4Mergeのどちらを使うか検討

もう何年もKDiff3ユーザーですが、無料のマージツールならP4Mergeも良いよと聞いたので、少し試してみました。
MercurialやGitのマージツールとして使うため、3-way mergeが前提です。
できるだけ自動解決したいので、うまく自動解決する手段があればそちらを優先します。

マージ対象のコード

今回試したマージ対象のコードは以下の3つです。意図的にコンフリクトが発生するような内容にはしていますが、多人数で同じモジュールを変更するような開発をしていると、しばしばこのような状況は起こるかと思います。

base.py
class User:
    """ユーザークラス
    """
    def __init__(self, name):
        self.name = name
local.py

base.pyに対してGroupクラスをファイルの末尾に追記しています。

class User:
    """ユーザークラス
    """
    def __init__(self, name):
        self.name = name


class Group:
    """グループクラス
    """
    def __init__(self, name):
        self.name = name
        self.users = []
other.py

base.pyに対してPermissionクラスをファイルの末尾に追記しています。

class User:
    """ユーザークラス
    """
    def __init__(self, name):
        self.name = name


class Permission:
    """パーミッションクラス
    """
    def __init__(self, name):
        self.name = name

3-way mergeの比較

KDiff3とP4Mergeで前述の3つのファイルをマージしてみます。

KDiff3の場合

KDiff3は0.9.96のcjk対応のものを使っています。
yuja / kdiff3-mq-cjk — Bitbucket

未解決のコンフリクトは、クラス定義とその次の行になります。また、__init__コンストラクタとself.nameへの代入の行は同一とみなされて、自動解決されています。
この場合、未解決のコンフリクト部分でB(local.py)とC(other.py)の内容を単純に選択しても、正しくマージできないため、コピー&ペーストで編集することになります。

P4Mergeの場合

P4Mergeは2015.2を使っています。
Visual Merge and Diff Tools | Perforce

P4Mergeの場合も、未解決のコンフリクト位置と同一な位置の検出はKDiff3と同様でした。
P4Mergeでもこの場合はコピー&ペーストで編集することになります。

ベースを少し工夫した場合の3-way mergeの比較

前述の通り、マージ対象のコードのままではどちらを使っても自動解決できませんでした。
あらかじめ同じモジュールを編集することがわかっているのであれば、ベースのファイル(base.py)に少し手を加えることで、マージツールの差分検出にヒントを与えられます。

base.py(工夫したやつ)

分岐前にあらかじめ、追記する部分にコメントを入れておきます。

class User:
    """ユーザークラス
    """
    def __init__(self, name):
        self.name = name

# ここにGroupクラス

# ここにPermissionクラス
local.py(工夫したbase.pyからの編集)

編集する場合には、base.pyの該当箇所のコメントを削除した上でコードを記述します。この際、追記しない部分のコメントは、残しておくことに注意します。

class User:
    """ユーザークラス
    """
    def __init__(self, name):
        self.name = name

class Group:
    """グループクラス
    """
    def __init__(self, name):
        self.name = name
        self.users = []

# ここにPermissionクラス
other.py(工夫したbase.pyからの編集)
class User:
    """ユーザークラス
    """
    def __init__(self, name):
        self.name = name

# ここにGroupクラス

class Permission:
    """パーミッションクラス
    """
    def __init__(self, name):
        self.name = name
KDiff3の場合(対象を工夫した場合の3-way merge)

KDiff3では、この場合、変更がうまく検出されてコンフリクト部分は自動解決されました。

P4Mergeの場合(対象を工夫した場合の3-way merge)

一方P4Mergeでは、大変残念な結果になりました。マージ結果にコメントが残ったり、チグハグな状態です。

P4Mergeだと、同じ内容の行が衝突の前後位置に来ると差分検出で同一と判定されてしまうのでしょうか?

結論

  • KDiff3とP4Mergeのどちらでも自動解決できないパターンはある。
  • KDiff3の場合、コメント等でマージのヒントになるキーワードを事前に入れておけば、自動解決できることがある。

やっぱりKDiff3を使い続けることになりそう。