はじめに
diff
コマンドには--ignore-matching-lines=RE
というオプションがあります。manを見ると"ignore changes where all lines match RE"と書かれていて、差分が正規表現にマッチしたときは無視するように見えます。
また日本語訳は「REGEXP にマッチするような行を挿入・削除するだけの変更を無視する。」となっています。
これらはかなり誤解を生む表現だと思います。(分かってから読み直すと言いたいことは分からないでもないですが…)
ここではこのオプションの正確な挙動について解説します。
具体例
以下の2つのファイルの差分を考えます。
Date: 2020/4/7
Time: 20:00:00
Date: 2020/4/6
Time: 21:00:00
普通にdiff
を取ると
$ diff file1.txt file2.txt
1,2c1,2
< Date: 2020/4/7
< Time: 20:00:00
---
> Date: 2020/4/6
> Time: 21:00:00
となります。ここでDateの違いは無視してTimeだけ差分を取りたいとします。
$ diff -I Date file1.txt file2.txt
1,2c1,2
< Date: 2020/4/7
< Time: 20:00:00
---
> Date: 2020/4/6
> Time: 21:00:00
しかし結果は全く変わりません。ここでfile1.txtとfile2.txtのDateとTimeの間に空行を入れてみます。
Date: 2020/4/7
Time: 20:00:00
Date: 2020/4/6
Time: 21:00:00
同じくdiff
を取ると
$ diff -I Date file1.txt file2.txt
3c3
< Time: 20:00:00
---
> Time: 21:00:00
正しくTimeだけ取り出せました。
これはなぜかというと--ignore-matching-lines
がマッチする対象は行ではなくhunkだからです。
hunkとは一塊の差分のことで、最初の例でいうと
1,2c1,2
< Date: 2020/4/7
< Time: 20:00:00
---
> Date: 2020/4/6
> Time: 21:00:00
が1個のhunkです。試しに空行を入れた状態で普通にdiff
を取ると
$ diff file1.txt file2.txt
1c1
< Date: 2020/4/7
---
> Date: 2020/4/6
3c3
< Time: 20:00:00
---
> Time: 21:00:00
と2個のhunkに分かれています。このそれぞれのhunkに対して正規表現マッチをかけて、1個目のhunkを無視するというのが--ignore-matching-lines
の正しい挙動です。
ドキュメントには
GNUのドキュメントにはこのあたりも含めて正確な挙動が書いてあります。
However, -I only ignores the insertion or deletion of lines that contain the regular expression if every changed line in the hunk—every insertion and every deletion—matches the regular expression. In other words, for each nonignorable change, diff prints the complete set of changes in its vicinity, including the ignorable ones.
ここに"every changed line in the hunk"とかかれている通り、hunkの全行が正規表現にマッチしないと無視されません。
ここまで理解するとmanに書かれている"all"の意味や、日本語訳の「だけの」という文言がhunk全てを指すというのが分かってくるのですが、初見でそれに気づくのはかなり厳しいですね…。