GNU Diffutilsを使い倒す!diff3を使って他人の修正ブランチを自分のブランチに3ウェイマージする

    はじめに

    会社で使用している版数管理システムではブランチ間のマージができない。。。他人の修正を自分のブランチに取り込めないぽい。。ヽ( ゚д゚)ノ

    ブランチ間でマージをするためには、両者のファイルを比較して、手動でマージシなければいけいない。そんなバカな、なにか方法があるのだろうと思い、調べてみた。

    やりたいこと

    • ブランチ間の自動マージ。
    • コマンドライン上からサクッとできる。
    • ただし、競合があるときや慎重にやりたいときは、手動マージ。

    3 way mergeアルゴリズム

    調べてみると、svnやcvsもできないらしい。git やMercurialだとできる。

    その違いは何かというと、マージのためのアルゴリズムが違うのだった。

    マージ (バージョン管理システム) – Wikipedia

    • svn、cvs ・・・ 2 way merge(2ウェイマージ)。2つのファイルの差分を比較してマージ。競合の解決は手動。
    • git、mercural・・・3 way merge(3ウェイマージ)。2つのファイルの共通の祖先との差分を比較してマージ。競合の解決は自動。

    競合を自動マージするためには、3-way mergeを使う必要がある。

    GNU Diffutilsを使い倒す!

    3 way merge をサクッとこなすコマンドを探しいてたら、diff3というものを発見!これは、GNU Project のDiffutilsに含まれるコマンドの一つらしい。ということで、このDiffutilsを調べた。

    Diffutils – GNU Project – Free Software Foundation

    diffutils とは、cmp, diff, diff3 そして sdiff のプログラムを含むパッケージ。これらのツール群を駆使して、差分比較やマージをコマンドラインからすることができる。

    実験

    以下のようなファイルをもとに、ツールを検証してみる。

    比較対象

    # test1 ファイル
    a
    a
    a
    a
    a
    

    test2 ファイル

    a a b a a

    diff

    まずはdiffから。これはさくっと2つのファイルの差分をみるためにつかう。

    [tsu-nera]% diff test1 test2
    3c3
    < a
    ---
    > b
    

    Man page of DIFF

    sdiff

    対話的にマージするためのツール。まずは、なにも指定しないと、両者を並べてdiffしてくれる。見やすい。

    [tsu-nera]% sdiff test1 test2
    a                                                               a
    a                                                               a
    a                                                             | b
    a                                                               a
    a                                                               a
    

    差分だけをみるには、’-s’をつける。

    [tsu-nera]% sdiff -s test1 test2
    a                                                             | b
    

    マージするには、-o “出力ファイル” をオプションでつける。マージする部分で、%のプロンプトがでるので、一つづつどうマージするか答える。手動マージで自動マージはしてくれない。

    ed:     両方の版にヘッダーで飾って、利用し、編集。
    eb:     両側の版を使用して編集。
    el:     左側の版を使用して編集。
    er:     右側の版を使用して編集。
    e:      新版を編集。
    l:      左側の版を使用。
    r:      右側の版を使用。
    s:      共通行を寡黙に含む。
    v:      共通行を饒舌に含む。
    q:      終了。
    

    こんな感じでマージ。

    [tsu-nera]% sdiff -o output test1 test2
    a                                                               a
    a                                                               a
    a                                                             | b
    %r
    a                                                               a
    a                                                               a
    

    Man page of SDIFF

    diff3

    3 つのファイル間にある違いを探す 。つまり、

    3 way mergeができる!(^_^)v

    比較のためのアルゴリズムがdiffと異なるのだ。。基本は以下。

    %  diff3 MINE OLDER YOURS

    • MINE ・・・自分のファイル
    • OLDER ・・・ 2つのファイルの共通の先祖
    • YOURS ・・・他人のファイル

    共通の祖先を指定することがキモ。これで他人の修正を自分のファイルに取り込むことができる。

    OLDER — –MINE

                |

                 —YOURES

    まずは実行。暗号めいた出力がでる。

    [tsu-nera]% diff3 test1 test1 test2
    ====3
    1:3c
    2:3c
      a
    3:3c
      b
    

    ====3で3つめのファイルに差分があることを示す。

    マージするには、-mオプションをつける。すると標準出力にマージ結果が出力されるので、リダイレクトでファイルに書き出せばよい。

    [tsu-nera]% diff3 -m test1 test1 test2 > output
    [tsu-nera]% less output
    a
    a
    b
    a
    a
    

    Man page of DIFF3

    まとめ

    • 差分を確認したいときは、sdiff -s で。
    • 自動マージするときは、diff3 -m で。
    • 手動マージするときは、sdiff -o で。

    なんとか頑張れそう。