diffは、ファイルの内容を比較して違いを報告するシェルコマンドです。 diffという用語は、コマンドの出力を識別するためにも使用され、コマンドを実行する動詞としても使用されます。 ファイルを比較するには、diff を実行して差分を作成します。[ 1 ]
通常、このコマンドはテキスト ファイルの比較に使用されますが、バイナリ ファイルの比較もサポートしています。入力ファイルのいずれかに非テキスト データが含まれている場合、コマンドはデフォルトで簡易モードになり、ファイルが異なるかどうかの概要のみを報告します。--textオプションを使用すると、常に行単位の差異を報告しますが、バイナリ データは一般的にテキストのように行単位で構造化されていないため、出力が理解しにくい場合があります。[ 2 ]
このコマンドは主に 2 つのファイル間の変更を分析するためにアドホックに使用されますが、特別な用途として、diff 出力レポートをパッチ ファイルとして使用するように特別に設計された コマンドで使用するパッチ ファイルを作成する場合があります。POSIXは、およびコマンドとその共有ファイル フォーマットを標準化しました。[ 3 ]patchdiffpatch
オリジナルのdiffユーティリティは、1970 年代初頭にニュージャージー州マレーヒルのベル研究所で Unix オペレーティングシステム向けに開発されました。これは 1974 年にリリースされた Unix 第 5 版の一部であり[ 4 ] 、ダグラス・マキルロイとジェームズ・ハントによって書かれました。この研究は、 diffの初期プロトタイプを開発したジェームズ・W・ハントと共著した 1976 年の論文で発表されました[ 5 ]。この論文で説明されたアルゴリズムは、ハント・シマンスキー アルゴリズムとして知られるようになりました。
McIlroy の研究は、GECOS上でのSteve Johnsonの比較プログラムとMike Leskの証明プログラムに先行され、影響を受けたものでした。ProofもUnix 上で開発され、diffと同様に行ごとの変更を出力し、プログラムの出力では行の挿入と削除を示すために山括弧 (">" と " < ") も使用していました。しかし、これらの初期のアプリケーションで使用されていたヒューリスティックは信頼性に欠けると判断されました。diff ツールの潜在的な有用性から、McIlroy は、PDP-11のハードウェアの処理能力とサイズ制限の下で十分に機能する、さまざまなタスクに対応できるより堅牢なツールを研究し、設計するに至りました。この問題に対する彼のアプローチは、 Alfred Aho、Elliot Pinson、Jeffrey Ullman、Harold S. Stoneを含む Bell Labs の個人との共同作業によって実現しました。
Unix のコンテキストでは、ed行エディタの使用により、 diff は機械で使用可能な「編集スクリプト」を作成する自然な機能を備えていました。これらの編集スクリプトは、ファイルに保存されると、元のファイルとともに、edによって変更されたファイル全体に復元できます。これにより、ファイルの複数のバージョンを維持するために必要な二次記憶装置が大幅に削減されました。McIlroy は、さまざまな出力形式を設計および実装できるdiff用のポストプロセッサを作成することを検討しましたが、構文とedコマンドが受け入れる逆順の入力を生成する役割をdiffに担わせる方が、より効率的でシンプルであることがわかりました。
1984年、ラリー・ウォールはテキストファイルをパッチするためのパッチユーティリティを作成しました(ソースコードはmod.sourcesとnet.sourcesのニュースグループ[ 6 ] [ 7 ] [ 8 ]で公開)。diffの出力と変更前のコンテンツを含むdiff入力ファイルを使用して、変更後のコンテンツを含むファイルを作成します。
X/Openポータビリティ ガイド 1987 年版 第 2 号には、diff が含まれています。コンテキスト モードは POSIX.1-2001 (第 6 号) で追加されました。統合モードは POSIX.1-2008 (第 7 号) で追加されました。[ 9 ]
diffの初期の頃は、ソフトウェアのソース コードと技術文書のマークアップの変更を比較したり、プログラムのデバッグ出力を検証したり、ファイルシステムのリストを比較したり、コンピュータのアセンブリ コードを分析したりすることが一般的な用途でした。edの出力は、ファイルに加えられた一連の変更を圧縮することを目的としていました。[ 10 ]ソースコード管理システム(SCCS) とその改訂版をアーカイブする機能は、 diffから編集スクリプトを保存することの結果として 1970 年代後半に登場しました。
他の目的で使用される編集距離の概念とは異なり、diffこれは文字指向ではなく行指向ですが、一方のファイルから他方のファイルを作成するために最小限の削除と挿入のセットを決定しようとする点でレーベンシュタイン距離に似ています。
diffの操作は、最長共通部分列問題を解くことに基づいています。[ 5 ]この問題では、2 つの項目のシーケンスが与えられます。
a b c d f g h j q z
a b c d e f g i j krxy z
そして、両方の元のシーケンスに同じ順序で存在する項目の最長シーケンスを見つけたいのです。つまり、最初の元のシーケンスからいくつかの項目を削除し、2番目の元のシーケンスから他の項目を削除することによって得られる新しいシーケンスを見つけたいのです。また、このシーケンスが可能な限り長いことも望んでいます。この場合、
abcdfgjz
最長共通部分列からdiffのような出力を得るには、ほんの少しのステップで済みます。部分列には存在しないが最初の元のシーケンスには存在する項目は、削除されたに違いありません(下の「-」マークで示されています)。部分列には存在しないが2番目の元のシーケンスには存在する項目は、挿入されたに違いありません(「+」マークで示されています)。
ehiqkrxy + - + - + + + +
このdiffコマンドは、次のような 2 つの引数を受け取ります。通常、引数はそれぞれ通常のファイルを指定しますが、2 つの引数がディレクトリを指定する場合は、コマンドはディレクトリ内の対応するファイルを比較します。オプションを使用すると、一致するサブディレクトリを再帰的にたどり、対応する相対パスを持つファイルを比較します。diff originalnew-r
以下の例は、元のファイルと新しいファイルの内容、およびdiffデフォルト形式での出力結果を示しています。出力は読みやすさを向上させるために色付けされています。GNU diff は--color、オプションを使用すると出力に色を付けることができます。--colorオプションが指定されていない場合、デフォルトで色付き出力は無効になります。[ 11 ]
このデフォルト形式では、aは追加、dは削除、cは変更を表します。元のファイルの行番号は 1 文字のコードの前に表示され、新しいファイルの行番号は後に表示されます。小なり記号と大なり記号 (追加、削除、または変更された行の先頭) は、その行がどのファイルにあるかを示します。追加行は元のファイルに追加され、新しいファイルに表示されます。削除行は元のファイルから削除され、新しいファイルには表示されません。
デフォルトでは、両方のファイルに共通する行は表示されません。移動した行は、新しい場所では追加されたものとして、古い場所では削除されたものとして表示されます。[ 12 ]ただし、一部の差分ツールでは移動した行が強調表示されます。
最新バージョンのdiffでは、オプションを指定することで編集スクリプトを生成できます-e。この例で生成される編集スクリプトは以下のとおりです。
24 aこの段落には、この文書への重要な追加事項が含まれています。 。 17 cこの文書を確認してください。 。 11,15 d 0 aこれは重要な通知です!したがって、この文書の冒頭に記載する必要があります! 。
edを使用して元のファイルの内容を新しいファイルの内容に変換するには、この diff ファイルに 2 行を追加します。1 行には(書き込み) コマンドが、もう 1 行には(終了) コマンドが含まれます (例: )。ここでは diff ファイルにmydiffという名前を付け、 を実行すると変換が行われます。wqprintf"w\nq\n">>mydiffed-soriginal<mydiff
Unixのバークレー版では、コンテキスト形式(-c)とファイルシステムのディレクトリ構造を再帰的に処理する機能( )を追加することに重点が置かれ-r、これらの機能は1981年7月にリリースされたBSD 2.8で追加されました。バークレーで導入されたdiffのコンテキスト形式は、最小限の変更しか加えられていない可能性のあるソースコードのパッチを配布するのに役立ちました。
コンテキスト形式では、変更された行は、変更前後の変更されていない行と並べて表示されます。変更されていない行をいくつでも含めることで、パッチのコンテキストが提供されます。コンテキストは、2つのファイル間で変更されていない行で構成され、変更されたファイル内で該当行の位置を特定し、変更を適用する意図した場所を見つけるための参照となります。行番号が一致しているかどうかは関係ありません。コンテキスト形式は、パッチ適用時の可読性と信頼性を向上させ、パッチプログラムへの入力として受け入れられる出力を提供します。このようなインテリジェントな動作は、従来のdiff出力では実現できません。
変更箇所の上と下に表示される変更されていない行の数は、ユーザーが定義できます(0行でも可)。ただし、通常は3行がデフォルトです。変更されていない行の範囲が隣接する変更箇所と重なる場合、diffは変更されていない行の重複を避け、変更箇所を1つの変更箇所に統合します。
「!」は、2 つのファイルで対応する行間の変更を表し、「+」は行の追加、「-」は行の削除を表します。空白は変更されていない行を表します。パッチの先頭には、タブ文字で区切られた完全なパスとタイムスタンプを含むファイル情報があります。各ハンクの先頭には、ファイル内の対応する変更に適用される行番号があります。3 つのアスタリスクのセットで囲まれた番号範囲は元のファイルに適用され、3 つのダッシュのセットは新しいファイルに適用されます。ハンクの範囲は、それぞれのファイルの開始行番号と終了行番号を指定します。
このコマンドdiff -c original newを実行すると、以下の出力が得られます。
*** /path/to/元のタイムスタンプ --- /path/to/新しいタイムスタンプ *************** *** 1,3 **** --- 1,9 ---- + これは重要な+ 通知です!したがって、この + 文書の先頭に配置する必要があります! +この文書のこの部分は バージョンからバージョンまで変更されていません *************** *** 8,20 **** 変更内容のサイズを圧縮する 。 この段落には、古い情報が含まれています。近いうちに削除されます。スペルは重要です !このドキュメントを確認してください。一方、 スペルミスは 世界の終わりではありません。--- 14,21 ----変更のサイズを圧縮します 。 スペルミスは重要です !この文書を確認してください。とはいえ、 スペルミスがあっ ても世界が終わるわけではありません。 *************** *** 22,24 **** --- 23,29 ----この段落は 変更する必要があります。 この後に何かを追加できます。 + + この段落には、この文書への重要な新しい追加事項が含まれています。統合フォーマット(またはunidiff)[ 13 ] [ 14 ]は、コンテキストフォーマットによって行われた技術的な改善を継承していますが、古いテキストと新しいテキストがすぐ隣に表示される、より小さな差分を生成します。統合フォーマットは通常、コマンドラインオプション-uを使用して呼び出されます。この出力は、パッチプログラムへの入力としてよく使用されます。多くのプロジェクトでは、特に「diff」を統合フォーマットで提出するように要求しているため、統合 diff フォーマットは、ソフトウェア開発者間の交換で最も一般的なフォーマットとなっています。
統合コンテキスト差分は、もともと1990年8月にウェイン・デイヴィソンによって開発されました(comp.sources.misc第14巻に掲載されたunidiff )。リチャード・ストールマンは、その1か月後にGNUプロジェクトのdiffに統合差分サポートを追加しました。この機能は、1991年1月にリリースされたGNU diff 1.15で初めて搭載されました。その後、GNU diffはコンテキスト形式を一般化し、任意の形式で差分をフォーマットできるようにしました。
フォーマットはコンテキストフォーマットと同じ2行のヘッダーで始まりますが、元のファイルの前に「---「そして新しいファイルの前には「+++「. この後には、ファイル内の行の差分を含む1 つ以上の変更チャンクが続きます。変更されていないコンテキスト行の前にはスペース文字が、追加行の前にはプラス記号が、削除行の前にはマイナス記号が付きます。」
チャンクは範囲情報で始まります。これは、行の追加、行の削除、および任意の数のコンテキスト行の直前に配置されます。範囲情報は二重のアットマークで囲まれ、コンテキスト形式(上記)で2行に分かれている内容を1行にまとめます。範囲情報行の形式は次のとおりです。
@@ -l,s +l,s @@オプションのセクション見出し
ハンク範囲情報には、2 つのハンク範囲が含まれます。元のファイルのハンクの範囲にはマイナス記号が付き、新しいファイルの範囲にはプラス記号が付きます。各ハンク範囲はl,sの形式で、 lは開始行番号、sは変更ハンクが各ファイルに適用される行数です。GNU diff の多くのバージョンでは、各範囲でカンマと末尾の値sを省略できます。この場合、s のデフォルト値は 1 になります。実際に興味深い値は、最初の範囲のl行番号のみであることに注意してください。他のすべての値は diff から計算できます。
元のファイルのチャンク範囲は、コンテキストチャンクと削除チャンク(変更されたチャンクを含む)のすべての行の合計である必要があります。新しいファイルのチャンク範囲は、コンテキストチャンクと追加チャンク(変更されたチャンクを含む)のすべての行の合計である必要があります。チャンクサイズ情報がチャンク内の行数と一致しない場合、差分は無効とみなされ、拒否される可能性があります。
オプションとして、チャンク範囲の後に、そのチャンクが含まれるセクションまたは関数の見出しを付けることができます。これは主に、差分を読みやすくするために役立ちます。GNU diff を使用して差分を作成する場合、見出しは正規表現マッチングによって識別されます。[ 15 ]
行が変更されると、削除と追加として表現されます。元のファイルと新しいファイルのチャンクは同じチャンク内に現れるため、このような変更は隣接して表示されます。[ 16 ] 次の例でその例を示します。
-この文書を確認してください。 +この文書を確認してください。
このコマンドdiff -u original newを実行すると、以下の出力が得られます。
--- /path/to/original timestamp +++ /path/to/new timestamp @@ -1,3 +1,9 @@ +これは重要な+通知です!この文書の +先頭に +配置する必要があります! +この文書のこの部分は、 バージョンから @@ -8,13 +14,8 @@変更のサイズを圧縮するため に変更されています 。-この段落には古いテキストが含まれています。 -近い将来削除されます。-この文書のスペルチェックを行うことが重要です 。 +この文書をチェックしてください。一方、 スペルミスは 世界の終わりではありません。 @@ -22,3 +23,7 @@この段落は 変更する必要があります。 その後に何かを追加できます。 + +この段落には、この文書への重要な新しい追加が含まれています。ファイル名とタイムスタンプを正しく分離するために、タブ文字が区切り文字として使用されます。これは画面上では見えないため、コンソール/ターミナル画面から差分をコピー&ペーストする際に失われる可能性があります。
diff形式には、特定のプログラムや状況で使用・理解されるいくつかの修正や拡張が存在します。例えば、Subversionなどの一部のバージョン管理システムでは、 diffのヘッダーセクションにタイムスタンプの代わりに、またはタイムスタンプに加えて、バージョン番号、「作業コピー」、あるいはその他のコメントを指定します。
一部のツールでは、複数の異なるファイルの差分を1つにマージすることができ、変更された各ファイルに次のようなヘッダーが付けられます。
インデックス: path/to/file.cpp
改行で終わらないファイルの特殊なケースは処理されません。 もunidiffPOSIXdiff標準も、このタイプのファイルを処理する方法を定義していません。 (実際、このようなファイルは厳密な POSIX 定義では「テキスト」ファイルではありません。[ 17 ] ) GNU diff と git は診断として「\ ファイルの末尾に改行がありません」(またはその翻訳版) を生成しますが、この動作は移植性がありません。[ 18 ] GNU patch はこのケースを処理しないようですが、git-apply は処理します。[ 19 ]
パッチプログラムは、実装固有の差分出力を必ずしも認識するとは限りません。ただし、GNU patch は git パッチを認識し、少し異なる動作をします。[ 20 ]
1975 年以降の変更点には、コア アルゴリズムの改善、コマンドへの便利な機能の追加、新しい出力形式の設計が含まれます。基本アルゴリズムは、Eugene W. Myersによる論文An O(ND) Difference Algorithm and its Variations [ 21 ] およびWebb Miller と Myers によるA File Comparison Program [ 22 ] で説明されています。このアルゴリズムは、Esko Ukkonen による Algorithms for Approximate String Matching [ 23 ]で独自に発見さ れ、説明されました。diff プログラムの最初のバージョンは、テキスト ファイルの行比較用に設計されており、行の区切り文字として改行文字が想定されていました。1980 年代までに、バイナリ ファイルのサポートにより、アプリケーションの設計と実装が変更されました。
GNU diffとdiff3は、他のdiffおよびパッチ関連ユーティリティとともにdiffutilsパッケージに含まれています。[ 24 ]
ポストプロセッサであるsdiffとdiffmkは、それぞれ差分リストを並べて表示し、印刷文書に変更マークを適用する。どちらも1981年以前にベル研究所の別の場所で開発された。
Diff3 は、2 つの差分を調整することで、1 つのファイルを他の 2 つのファイルと比較します。元々は、共通のソースを編集する 2 人のユーザーによる変更を調整するために Paul Jensen によって考案されました。また、マージのためにRCSなどのリビジョン管理システムでも使用されています。[ 25 ]
EmacsにはEdiffという機能があり、パッチによってもたらされる変更を、パッチファイルの対話型編集機能とマージ機能を組み合わせたユーザーインターフェースで表示します。
Vim は、2 ~ 8 つのファイルを比較し、違いを色で強調表示するvimdiff を提供しています。 [ 26 ] 歴史的には diff プログラムを呼び出そうとしていましたが、現代の vim は、速度と機能が向上したgitの xdiff ライブラリ (LibXDiff) のフォークを使用しています。[ 27 ]
GNU Wdiff [ 28 ]は、単語の折り返しや列幅の違いがあっても、書かれた言語のテキスト文書で変更された単語やフレーズを表示する diff のフロントエンドです。
colordiff は 'diff' の Perl ラッパーで、同じ出力を生成しますが、追加および削除された部分に色を付けます。[ 29 ] diff-so-fancy と diff-highlight は、より新しい類似のものです。[ 30 ] "delta" は、変更と基となるコードを同時に強調表示する Rust の書き換えです。[ 31 ]
Patchutilsには、コンテキスト差分と統合差分を結合、再配置、比較、修正するツールが含まれています。[ 32 ]
ソースファイルを構文構造に基づいて比較するユーティリティは、主に一部のプログラミング言語の研究ツールとして開発されてきました。[ 33 ] [ 34 ] [ 35 ]一部は商用ツールとして利用可能です。[ 36 ] [ 37 ]さらに、構文を考慮した差分比較を行う無料ツールには以下のようなものがあります。
spiff は、丸め誤差と空白を含む浮動小数点計算の差異を無視するdiffの派生版です。これらは一般的にソースコードの比較には関係ありません。オリジナル版はBellcore が作成しました。 [ 43 ] [ 44 ] HPUXポートが最新の公開リリースです。spiff はバイナリ ファイルをサポートしていません。spiff は標準出力に標準 diff 形式で出力し、C、Bourne シェル、Fortran、Modula-2、Lispプログラミング言語で入力を受け付けます。[ 45 ] [ 46 ] [ 43 ] [ 47 ] [ 44 ]
LibXDiff は1998 年以来多くのアルゴリズムへのインターフェースを提供するLGPLライブラリです。当初はRabin フィンガープリントを備えた改良版 Myers アルゴリズムが実装されていました (2008 年の最終リリース時点)、[ 48 ]しかし、gitおよびlibgit2のフォークにより、リポジトリが拡張され、独自のアルゴリズムが多数追加されています。「histogram」と呼ばれるアルゴリズムは、速度と品質の両方において、元の Myers アルゴリズムよりも優れていると一般的に考えられています。[ 49 ] [ 50 ]これは Vim で使用されているLibXDiffの最新バージョンです。[ 27 ]
スタイルの diff では、各パッチの "before" 状態は、ファイルを変更する前の初期状態を指します。
モードで編集を開始する最も簡単な方法は、「vimdiff」コマンドを使用することです。これにより、通常どおり Vim が起動し、引数間の差分を表示する設定も行われます。
これは以下と同等です。
vimdiff file1 file2 [file3] [file4] [...file8]vim -d file1 file2 [file3] [file4] [...file8]
これは確かに、histogram diff が Myers よりわずかに速く、patience は他のものよりはるかに遅いことを示しています。