Git で再帰的ストラテジでマージする時のストラテジオプション ours と theirs の違い


2 つのブランチをマージする時にデフォルトで実行されるストラテジ, recursive (再帰的). その recursive で指定できる少しややこしい次の 2 つのオプション ours と theirs の違いをできるだけわかりやすく解説します.

Git のバージョンは 2.18.0 を前提にしております.

ourstheirs というオプションは, Git の再帰的マージストラテジで指定できるオプションです.

なので git merge -s recursive -X <option> <branch> という構文の <option> にあたります.

また -s recursive の部分は, git merge <branch> と 1 つの <branch> をマージする場合はデフォルトで再帰的マージになりますので省略可能です.

つまりは ours もしくは theirs オプションを指定する場合, git merge -X <option> <branch> という構文が普段使いに最適です.

でお次は本題の ours もしくは theirs オプションを使うとどのようになるのかということを説明します.

まず次のようなヒストリがあるとします:

            A <- feature
           /
          /
    B -- C --- D <- master <- HEAD

そして何もオプションを付けないで, feature ブランチを master ブランチにマージさせます:

git merge feature

すると次のようなメッセージが表示され:

Auto-merging example.txt
CONFLICT (content): Merge conflict in example.txt
Automatic merge failed; fix conflicts and then commit the result.

example.txt に次のようなマージコンフリクトが発生したとします:

The first line by branch 'master'.
The second line by branch 'master'.
<<<<<<< HEAD
The third line by branch 'master'.
=======
The third line by branch 'feature'.
>>>>>>> feature

このマージコンフリクトの内容から HEAD (master ブランチ) の The third line by branch 'master'.feature ブランチの The third line by branch 'feature'. が競合しているというのか確認できます.

このようなマージコンフリクトが ours もしくは theirs オプションを使うとどのように解決されるのか見てみたいと思います.

ours オプションを使った場合

まず ours オプションを使った場合, マージする時に競合する部分はマージされるブランチのものを優遇します. つまりは git merge -X ours <branch><branch> をマージする時に, <branch> の方ではなく, HEAD の方を優遇します.

マージするブランチではなくマージされるブランチの方がが本流と捉えられるので, ours (私たちのもの) となります.

なので先ほどのヒストリの場合, ours オプションを使って feature ブランチをマージすると:

git merge -X ours feature

次のようなメッセージが表示され:

Auto-merging example.txt
Merge made by the 'recursive' strategy.

マージコンフリクトは発生せず, example.txt は次のようになります:

The first line by branch 'master'.
The second line by branch 'master'.
The third line by branch 'master'.

このように ours オプションを使うと, 競合する部分はマージされるブランチ (master) の方のバージョンを優遇します.

theirs オプションを使った場合

一方の theirs オプションを使うと, ours オプションとは逆になり, マージする時に競合する部分はマージするブランチのものを優遇します. つまりは git merge -X theirs <branch><branch> をマージする時に, HEAD ではなく <branch> の方を優遇します.

マージされるブランチが本流と捉えられるのに対し, マージするブランチは支流と捉えられるので, theirs (彼ら彼女らのもの) となります.

同じく先ほどのヒストリの場合, theirs オプションを使って feature ブランチをマージすると:

git merge -X theirs feature

次のようなメッセージが表示され:

Auto-merging example.txt
Merge made by the 'recursive' strategy.
 example.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

マージコンフリクトは発生せず, example.txt は次のようになります:

The first line by branch 'master'.
The second line by branch 'master'.
The third line by branch 'feature'.

このように theirs オプションを使うと, 競合する部分はマージするブランチ (feature) の方のバージョンを優遇します.

まとめ

再帰的マージでマージコンフリクトが発生した場合, マージされる方, する方どちらのブランチを優遇するかというのは次のようになります:

  • git merge -X ours <branch> の場合, <branch> をマージする HEAD の方を優遇します.

  • git merge -X theirs <branch> の場合, HEAD にマージする <branch> の方を優遇します.

ところで競合が発生しないマージの場合, ours もしくは theirs オプションのどちらを使ってもマージ結果に違いが出ることはありません.

あくまで競合が発生した場合どちらの方のブランチを優遇させるかというオプションになりますので.

あと別のマージストラテジとして ours というものがありますが, そちらはマージストラテジのオプションではなく, マージストラテジの一つなので, 再帰的マージストラテジの ours オプションとの混同に注意していただければと思います.

例えば ours マージストラテジで feature ブランチを HEAD にマージする場合:

git merge -s ours feature

git merge -X ours feature の場合と異なり, feature ブランチのバージョンのものが HEAD のバージョンのものと競合していようがしていまいが, feature ブランチの内容を HEAD にマージさせるということは一切行いません.

feature ブランチの内容のものは全て無視されマージされません.

じゃあその ours マージストラテジでマージする意味ってなんなのということですが, この場合 feature ブランチに HEAD が取って代わりますよという時に使われます.

このようにマージストラテジの ours は形だけのマージですので, 再帰的マージの ours オプションとは全く異なるものです.

と少し今回の内容から逸れてしまいましたが, 今回の内容が再帰的マージストラテジの 2 つのオプション ourstheirs の違いを理解するお役に立つことができましたでしょうか.

参考資料