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

# はじめに

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

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

パッチ等他の形式については [Linuxエンジニアらしいパッチのつくりかた](http://qiita.com/astro_super_nova/items/e30dcaf4d106deebc63c) がよくまとまっていると思いますので、そちらをご覧ください。

# 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行目で改行+`jumps`の`jumped`への変更(`c`: changed)がされた
- a.txtの8行目とb.txtの8行目で改行の追加(`a`: added)がされた
- a.txtの10行目とb.txtの10行目で`dog`の`dogs`への変更(`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)のコマンドに色付け](http://qiita.com/ironsand/items/b308b3c689d69b94bae5) の記事にまとまっています。

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

## 導入方法

### Homebrewの場合

```
% brew install colordiff
```

### Debian系の場合

```
# apt-get install colordiff 
```

### RHEL系の場合

```
# yum install colordiff
```

## 使い方

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

```bash:.bashrc
alias diff='colordiff'
```

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

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

# icdiffを利用する

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

## 導入方法

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

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

## 使い方
利用してみます。
![スクリーンショット 2016-08-29 7.52.17.png](https://qiita-image-store.s3.amazonaws.com/0/72505/f951659c-1068-4dc5-c773-31019b884c76.png)

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

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

```bash:.bashrc
alias icdiff='icdiff -U 1 --line-numbers'
```

# まとめ

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