わかりやすい差分(diff)の取り方いろいろメモ


はじめに

vimdiffが使える場合はこの記事は読む必要がありません。
また、はじめには読み飛ばして下さって構いません。

エンジニアにとって、2つのファイルの比較を行うことはよくあることだと思います。
ですが最近仕事で2つのファイルの差分を誰でもすぐに読み解ける方法を考え出す必要が出てきました。なお、補足をするとそのプロジェクトではgitを導入できる段階ではありませんでした。
全員がvimを使えるならvimdiffで良いと思います。
ここでは主にdiffコマンドに関することでの視覚的なことについてメモしたいと思います。

パッチ等他の形式については Linuxエンジニアらしいパッチのつくりかた がよくまとまっていると思いますので、そちらをご覧ください。

diffコマンド

一般的に差分を取るときは普通はdiffコマンドが思いつきます。diffコマンドの差分の判定についてのオプションについてはここでは省略します。

説明のために以下の2つのファイルを用意します。これ以降このファイルを使用します。

a.txt
the
quick
brown
fox

jumps
over
the
lazy
dog
b.txt
the
quick
brown
fox
jumped
over
the

lazy
dogs

この状態でdiff a.txt b.txtを実行すると以下のようになります。

% diff a.txt b.txt
5,6c5
< 
< jumps
---
> jumped
8a8
> 
10c10
< dog
---
> dogs

ここからは、以下の情報が読み取れると思います。他にも削除(d: deleted)があります。

  • a.txtの5, 6行目とb.txtの5行目で改行+jumpsjumpedへの変更(c: changed)がされた
  • a.txtの8行目とb.txtの8行目で改行の追加(a: added)がされた
  • a.txtの10行目とb.txtの10行目でdogdogsへの変更(c: changed)がされた

読み取ることはできますが誰でも読み解けるか、と言われると疑問が残ります。

Unified形式

-uオプションをつけることで、Unified形式で出力することができます。
-Uオプションで何行(デフォルトは3行)何行ずつ結合するか指定できます。

% diff -u a.txt b.txt
--- a.txt   2016-08-29 06:43:45.000000000 +0900
+++ b.txt   2016-08-29 06:43:59.000000000 +0900
@@ -2,9 +2,9 @@
 quick
 brown
 fox
-
-jumps
+jumped
 over
 the
+
 lazy
-dog
+dogs

ここからは、a.txtとb.txtの2行目から9行目にかけて以下の情報が上から順に読み取れます。

  • a.txtからb.txtで改行の削除(-)がされた
  • a.txtからb.txtでjumpsの削除(-)がされた
  • a.txtからb.txtのjumpedの追加(+)がされた
  • a.txtからb.txtで改行の追加(+)がされた
  • a.txtからb.txtでdogの削除(-)がされた
  • a.txtからb.txtでdogsの追加(+)がされた

Unified形式では同じファイルの文脈の中で上から順に差分を調べることができます。

Context形式

-cオプションをつけることで、Context形式で出力することができます。
-Cオプションで何行(デフォルトは3行)何行ずつコピーするか指定できます。

% diff -c a.txt b.txt
*** a.txt   2016-08-29 06:43:45.000000000 +0900
--- b.txt   2016-08-29 06:43:59.000000000 +0900
***************
*** 2,10 ****
  quick
  brown
  fox
! 
! jumps
  over
  the
  lazy
! dog
--- 2,10 ----
  quick
  brown
  fox
! jumped
  over
  the
+ 
  lazy
! dogs

ここからはまず、a.txtの2行目から10行目にかけて以下の情報が上から順に読み取れます。

  • 改行が削除または変更された
  • jumpsが削除または変更された
  • dogsが削除または変更された

また、b.txtの2行目から10行目にかけて以下の情報が上から順に読み取れます。

  • jumpedが変更された
  • 改行が追加された
  • dogsが変更された

Context形式ではそれぞれのファイルの文脈の中で上から順に差分を調べることができます。

2カラム形式

-yオプションをつけることで2カラムにして表示することができます。このとき-Wオプションで合計の横幅(デフォルトは130)を設定することができます。似たものにsdiffがあります。

% diff -y -W 80 a.txt b.txt
the                                     the
quick                                   quick
brown                                   brown
fox                                     fox
                                      | jumped
jumps                                 <
over                                    over
the                                     the
                                      >
lazy                                    lazy
dog                                   | dogs

読み方の説明はもはや不要に思えますが>が追加、<が削除、|が変更を表しています。
2カラム形式は、とても視覚的で誰にとっても読み易いとはと思いませんか?(あくまで私の感想ですが)
しかし、この表示には欠点があります。それは、行数がつかめないことです。
これを解決する方法をコラムの後に述べます。

コラム: diffに色をつけて表示する

色があると読み易さが格段に向上します。
他にも色をつけられるコマンドはたくさんありますが linux(CentOS)のコマンドに色付け の記事にまとまっています。

diffに色をつける場合にはcolordiffがあります。

導入方法

Homebrewの場合

% brew install colordiff

Debian系の場合

# apt-get install colordiff 

RHEL系の場合

# yum install colordiff

使い方

diffコマンドではなく、colordiffを実行すれば良いです。
毎回colordiffと打つのが面倒であれば以下のように.bashrc.zshrcなどに以下を追記します。

.bashrc
alias diff='colordiff'

このときdotfilesをgit管理していて複数の環境で使うという場合などでは以下のように追記すると良いでしょう。

.bashrc
if [[ -x `which colordiff` ]]; then
  alias diff='colordiff'
else
  alias diff='diff'
fi

icdiffを利用する

行数を表示して2カラムが表示できても行数がなければどこか特定するのは難しくなります。
そこでicdiff--line-numbersオプションを利用してみます。

導入方法

導入方法は jeffkaufman/icdiff: improved colored diff にもまとまっています。gitで利用したい場合は参照すると良いでしょう。

# pip install git+https://github.com/jeffkaufman/icdiff.git

使い方

利用してみます。

-Uオプションで表示される行を少なくしています。
また、icdiffなど色付きコマンドをlessに入れる場合は... | less -Rとすると良いでしょう。

diffコマンドでicdiffコマンドが呼び出されるのは強引な気がしますので、今回はオプションのエイリアスで止めておきます。以下のようにすれば良いでしょう。

.bashrc
alias icdiff='icdiff -U 1 --line-numbers'

まとめ

  • diffの表示形式にはUnified形式、Context形式、2カラム方式がある(RCS formatなどは視覚的でないため除く)
  • 色をつけるにはcolordiffを使うと良い
  • icdiffを利用すればは2カラム形式で色をつけ、行番号を簡単につけられる
  • 全員vimを使えるならvimdiffが...いいのです...