#navi_header|技術| 細かいところとかが結構「あれれ?」となりやすいので、diffの使い方を今一度自分用にメモ。 - GNU Diffutils -- http://www.gnu.org/software/diffutils/ - GNU Diffutils HTML ドキュメント(1ページ版) -- http://www.gnu.org/software/diffutils/manual/html_mono/diff.html - Linux jman の diff -- http://www.linux.or.jp/JM/html/gnumaniak/man1/diff.1.html - FreeBSD の diff (FreeBSD 5.0) -- http://www.jp.freebsd.org/cgi/mroff.cgi?subdir=man&lc=1&cmd=&man=diff&dir=jpman-5.0.0/man§=0 - Solaris10 の diff -- http://docs.sun.com/app/docs/doc/819-1210/diff-1?a=view なお、normal/context("-c")/unified("-u")の3種類は、上記Linux/FreeBSD/Solaris10の各diffでサポートされている。 Solaris10のdiffのみ、新ファイルに関連する"-N"オプションが記述されていない。 #more|| #outline|| ---- * 基本 diff 旧ファイル 最新ファイル →"normal"形式で出力される。 normal形式の変更内容表示フォーマット: < : 旧ファイルの内容 > : 新ファイルの内容 --- : 変更があった場合の、旧/新の境目 normal形式の行番号表示フォーマット: : LaR1[,R2] : 新ファイルのR1(-R2)行目が、旧ファイルのL行目の直後に追加。 : L1[,L2]cR1[,R2] : 旧ファイルのL1(-L2)行目が、新ファイルのR1(-R2)行目に変更。 : L1[,L2]dR : 旧ファイルのL1(-L2)行目が、新ファイルのR行目の直前で削除。 例: $ cat file1 1 2 3 4 5 $ cat file2 2 3.5 4.5 5 6 $ diff file1 file2 1d0 < 1 3,4c2,4 < 3 < 4 --- > 3.5 > 4.5 > 5a6 > 6 ** 代表的なオプション:"-r", "-N", "-X" : "-r"オプション : サブディレクトリも再帰的にdiffする。 : "-N"オプション : 新/旧のディレクトリのどちらか片方にしかファイルが無い場合、もう片方には同名の空ファイルがあるように動作する。 : "-X" オプション : "--exclude-from=FILE"として、ディレクトリ単位で比較中に、無視したいファイル名を指定する。 なお"-N"オプションおよび"-X"オプション("--exclude-from=FILE")は Solaris10 の diff manページには記載が無い。 patch生成の場合は"-uNr"の組み合わせを使う場合が多い。Solaris10の場合どうしているのかは謎。 ** 空白処理について diffにおける空白処理についてメモ。なお、diffでの「空白」というのは0x20(スペース)と0x09(水平方向TAB)の両方を指しているようだ。 よく見かけるオプションは "-b", "-B", "-E", "-w" だが、プラットフォームにより対応状況が異なる。 | "-b" | GNU diff, FreeBSD5, Solaris10 | | "-B" | GNU diff, FreeBSD5 | | "-E" | GNU diff, FreeBSD(6.3以上) | | "-w" | GNU diff, FreeBSD5, Solaris10 | また実際にmanページの記述を調べてみると、同じオプションでも微妙な表記の違いがあり、不安を駆り立てる。 "diff タグ スペース"などで検索すると $ diff -BbwE old new とすればよい、という記事も見つかるが、これはSolaris10およびFreeBSD6.2以前では動作しないものと思われる(未検証、あくまでも日本語の各プラットフォームのmanの記述に依る)。 ここではCentOS 5.2に含まれているdiff (GNU diffutils) 2.8.1 を使って実際のC言語ソース想定で各オプションの動作を見てみる。 なおここでは"-E"オプションは検証しない。"404 Blog Not Found:タブのスペース化はタブ幅よりも重要である"( http://blog.livedoor.jp/dankogai/archives/50475459.html )にあるようにタブ文字についてはソースコード中では使わない方が良いだろう、という意見に賛同しているためである。 #pre||> $ cat text1 printf("Hello, World!(0)"); printf("Hello, World!(1)"); printf("Hello, World!(2)"); printf("Hello, World!(3)"); printf("Hello, World!(4)"); printf("Hello, World!(5)"); printf("Hello, World!(6)"); printf("Hello, World!(7)"); printf("Hello, World!(8)"); $ cat text2 printf("Hello, World!(0)"); printf("Hello, World!(1)"); printf("Hello, World!(2)"); printf("Hello, World!(3)"); printf("Hello, World!(4)"); printf("Hello, World!(5)"); printf("Hello, World!(6)"); printf("Hello, World!(7)"); printf("Hello, World!(8)"); ||< 分かりづらいが、"(2)"と"(5)"は行末に空白が一文字追加されている C言語のソースコードという見方に立つと、diffで検出して欲しいのは"(3)"と"(4)"である。これは文字列中の空白の数が変更されているので、diffで検出され、最終的にはpatchで適用できなければならない。 また出来れば空白行の差異も検出したい。上の例はC言語ソース想定であるが、Perl/PHPなどのスクリプト言語やシェルスクリプトでのヒアドキュメントでの空行差異は検出されるべきと思われる。 それ以外の行については、行頭・行末の空白追加なので検出されなくても良いと思われる。 ''結論:オプション無しが最も要求を満たしている。次点で"-B"オプションだが、空行差異が検出されないのでヒアドキュメント中の空行に敏感な場合は使用できない。'' オプション無し:全て検出される。 #pre||> $ diff text1 text2 2,6c2,6 < printf("Hello, World!(1)"); < printf("Hello, World!(2)"); < printf("Hello, World!(3)"); < printf("Hello, World!(4)"); < printf("Hello, World!(5)"); --- > printf("Hello, World!(1)"); > printf("Hello, World!(2)"); > printf("Hello, World!(3)"); > printf("Hello, World!(4)"); > printf("Hello, World!(5)"); 8d7 < 9a9 > ||< "-b"オプション:"(3)"が検出できないので要求を満たせない。 #pre||> $ diff -b text1 text2 2c2 < printf("Hello, World!(1)"); --- > printf("Hello, World!(1)"); 5c5 < printf("Hello, World!(4)"); --- > printf("Hello, World!(4)"); 8d7 < 9a9 > ||< "-B"オプション:"(3)", "(5)"が検出できたが、空行差異は検出できない。 #pre||> $ diff -B text1 text2 2,6c2,6 < printf("Hello, World!(1)"); < printf("Hello, World!(2)"); < printf("Hello, World!(3)"); < printf("Hello, World!(4)"); < printf("Hello, World!(5)"); --- > printf("Hello, World!(1)"); > printf("Hello, World!(2)"); > printf("Hello, World!(3)"); > printf("Hello, World!(4)"); > printf("Hello, World!(5)"); ||< "-w"オプション:空行差異しか検出できない為要求を満たせない。 #pre||> $ diff -w text1 text2 8d7 < 9a9 > ||< * 出力形式 normal形式の他に、GNU/FreeBSD/Solaris10 全てで以下の形式をサポートしている。 ** context形式 ("-c" オプション) 1981年リリースの2.8BSDで"-r"(recursive)オプションと共にリリースされた形式(出典: http://en.wikipedia.org/wiki/Diff )。 現在はLinux/FreeBSD/Solaris10でそれぞれサポートされている。 ヘッダ: *** 旧ファイル名 旧ファイルの更新日時 --- 新ファイル名 新ファイルの更新日時 中身: *************** *** OLD1,OLD2 **** ← 変更箇所の旧側の行番号範囲 旧ファイルの中身 - 旧ファイルの中身 ← 削除行 旧ファイルの中身 ! 旧ファイルの中身 ← 変更行 ... --- NEW1,NEW2 ---- 新ファイルの中身 ← 変更箇所の新側の行番号範囲 新ファイルの中身 ! 新ファイルの中身 ← 変更行 新ファイルの中身 + 新ファイルの中身 ← 追加行 ... 例: #pre||> $ diff -c file1 file2 *** file1 2009-11-18 15:23:54.000000000 +0900 --- file2 2009-11-18 15:24:02.000000000 +0900 *************** *** 1,5 **** - 1 2 ! 3 ! 4 5 --- 1,6 ---- 2 ! 3.5 ! 4.5 ! 5 + 6 ||< ** unified形式 ("-u" オプション) 1991年1月リリースのGNU diff 1.15で追加された。オリジナルは Wayne Davison により 1990年8月 にundiffツールで実装され、1ヶ月後に Richard Stallman によりGNU diffツールにもunified形式サポートが追加される。リリースされたのが前述の日付とGNU diffバージョン、ということらしい。(出典: http://en.wikipedia.org/wiki/Diff )。 現在はLinux/FreeBSD/Solaris10でそれぞれサポートされている。 ヘッダ: --- 旧ファイル名 旧ファイルの更新日時 +++ 新ファイル名 新ファイルの更新日時 中身: @@ -L1,L2 +R1,R2 @@ ← 旧・新の変更箇所行番号の範囲 - 旧ファイル中身 ← 旧ファイルから削除された行 + 新ファイル中身 ← 新ファイルで追加された行 変更については、削除+追加という取り扱いになる。 例: #pre||> $ diff -u file1 file2 --- file1 2009-11-18 15:23:54.000000000 +0900 +++ file2 2009-11-18 15:24:02.000000000 +0900 @@ -1,5 +1,6 @@ -1 2 -3 -4 +3.5 +4.5 + 5 +6 ||< #navi_footer|技術|