テキスト差分チェッカー
2つのテキストを比較して違いを瞬時に見つけます。
テキスト比較について
この差分ツールは、最長共通部分列(LCS)アルゴリズムを使って2つのテキストを行ごとに比較します。追加は緑で、削除は赤でハイライトされます。「インライン」ビューを選択すると、すべての変更が1つの列に表示され、「並べて」ビューでは元のテキストと変更されたテキストを並列に比較できます。
一般的な用途
- コミット前にコードの変更を比較
- 変更されたドキュメントや契約書をレビュー
- 翻訳の違いを確認
- 設定ファイルの変更を検証
- APIレスポンスを比較
よくある質問
文字ごとに比較しますか?
このツールは行ごとに比較します。行に少しでも変更がある場合(1文字でも)、行全体が変更済みとしてマークされます。これは、Gitとほとんどの差分ツールが使用するのと同じアプローチです。
サイズ制限はありますか?
厳密な制限はありませんが、非常に大きなテキスト(10,000行以上)は処理に時間がかかる場合があります。比較はブラウザ内で完全に実行されるためです。
「diff」とは実際何を意味するか
diffとは最小の挿入と削除のセットを使って1つのテキストを別のテキストに変換する方法を記述する。それを行う方法は無数にあり、最も単純なのは「Aの全文字を削除してからBの全文字を挿入する」だ。有用なdiffは最短編集スクリプトであり、これは最長共通部分列問題の双対だ:両テキストに(順序どおりに)現れる最長の行の列を見つけ、それ以外の全ては追加または削除だ。ほとんどのdiffアルゴリズムは異なる装いをしたLCSアルゴリズムだ。
微妙な点がある:最長共通部分列は最長共通部分文字列ではない。部分列は順序を保持するが隣接は不要だ。そのため「ABCDE」と「AXBYCZD」は3文字の連続した文字列が両方に現れないにもかかわらず、部分列「ABCD」を共有する。
diffアルゴリズムの略史
最初に広く使われたファイル比較プログラムはBell LabsのUnix用diffで、Douglas McIlroyが作成し、基礎アルゴリズムは1976年6月のBell Labs Computing Science Technical Report #41としてJames W. HuntとMcIlroyが発表した。McIlroy自身は4ヶ月の開発を「必死の努力」と表現した。Hunt-McIlroyのアプローチはファイルBの各行をハッシュ化してユニークな行の出現箇所をインデックス化し、最長の単調増加マッチチェーンを探した。Version 6 Unixに収録され、何十年もの間ほとんどのUnixシステムの/usr/bin/diffで本質的に同じアルゴリズムのまま使われ続けた。
Eugene W. Myersは1986年に現代の業界標準を発表した。Algorithmica Vol.1, No.2に掲載された「O(ND)差分アルゴリズムとその変形」だ。Myersは編集グラフを通じた最短経路問題としてLCSを再定義した:Aを上軸、Bを側軸に配置し、2行が一致するたびに対角線を引き、非対角辺の最も少ない左上から右下へのパスを探す。各非対角辺は1回の挿入または削除で、各対角線はコストゼロだ。重要なのは、アルゴリズムがDを編集スクリプトのサイズとしてO((N+M)·D)時間で動作すること、つまりファイルが類似しているほど高速化されることだ。一般的なバージョン管理のdiffではDはN+Mに比べてごく小さいため、Myersは実際にはO(N·M)の最悪ケースより劇的に高速だ。論文にはまた、分割統治の「ミドルスネーク」技法を使った線形空間改良版も含まれる。Myersのアルゴリズムは、GNU diff・git diff・Mercurial・Subversion・jsdiff・Pythonのdifflib・ほとんどのGUI diffビューアーのデフォルトバックエンドだ。
Bram Cohen(後にBitTorrentの発明者として知られるようになる)は2002年頃にBazaarバージョン管理システム向けにパティエンスdiffを設計した。動機となった問題:Myersのdiffは非常に一般的な行も含めて任意の一致行を整列させるため、Cでの関数移動が、移動した関数の閉じ括弧が無関係な関数の閉じ括弧と整列するノイズの多いdiffを生む可能性があった。パティエンスdiffは各ファイルに1回だけ現れる行(通常は関数シグネチャ・コメントヘッダー・特徴的なログメッセージ)でdiffを固定し、パティエンスソートを使ってそれらのアンカーだけのLCSを計算し、ギャップで再帰する。結果として意味のある行で整列し、ソースコードに対してより読みやすくなる。Gitではgit diff --patienceで使用できる。ヒストグラムdiffは2010年頃にgitに追加された改良版で、行の出現回数のヒストグラムを構築し、厳密なユニーク行ではなく低頻度のアンカーを優先する。git 2.45以降、多くの設定でデフォルトとなっている。
レーベンシュタインとLCS
区別する価値のある関連概念だ。レーベンシュタイン距離(Vladimir Levenshtein、1965年)は1つの文字列を別の文字列に変換するための1文字の挿入・削除・置換の最小回数を数える。LCSベースのdiffは密接に関連するが若干異なる:古典的なdiffは「変更」を単一の置換ではなく削除と挿入のペアとして扱う。これが行指向ファイルにより適しているためだ。レーベンシュタインは「これらの文字列はどれだけ異なるか?」に答えて数値を返す。LCSは「具体的に何が変わったか?」に答えて実際の編集スクリプトを返す。スペルチェッカーや「もしかして?」機能は前者を求め、diffツールは後者を求める。Damerau-Levenshteinバリアント(1964年)は誤字検出のためのトランスポジション演算子でレーベンシュタインを拡張する。
粒度:行・単語・文字
diffは異なる粒度で計算でき、それぞれにトレードオフがある:
- 行レベル:各行が1トークン。2行は完全に一致するかしないかのどちらかだ。高速で、プログラマーのコード編集方法にきれいに対応し、
patch(1)で適用できる簡潔なパッチを生成する。コスト:200文字の行の1文字を変更すると行全体が変更済みとしてマークされる。diff・git・SVN・このツールのデフォルト。 - 単語レベル:空白区切りの各トークンが単位であるため、1単語だけ変更された行はその単語だけがハイライトされる。散文・契約書・ブログ草稿に最適。
git diff --word-diff・Google Docsの「提案」モード・Microsoft Wordの「変更履歴」・Diffcheckerの単語比較モードで使用されている。 - 文字レベル:各Unicodeコードポイントが単位。1文字の誤字をキャッチでき、1単語で意味が変わる法的なレッドラインに理想的。大きな入力では遅く、長い編集では読みにくいこともある。
現代のdiff UIで一般的なパターンは、まず行レベルでdiffを計算し、「削除/追加の隣接ペア」のそれぞれに対してその2行だけに二次的な単語レベルdiffを実行し、行内の変更をハイライトすることだ。これはGitHubのPRビューとdiff-match-patchライブラリの両方の動作方法だ。
よく見かける3つのdiff表示モード
- ユニファイドdiff(全ての
.patchファイルの形式):hunksは@@ -oldStart,oldLen +newStart,newLen @@で始まり、デフォルトで前後3行のコンテキストが付く。1990年頃のWayne DavisonのGNU diffの-uフラグに由来する。コンパクトでpatch(1)が機械可読なため、git・カーネルスタイルのメールパッチ・SVNが全て使用している。 - サイドバイサイド:左にオリジナル、右に変更版を行ごとに整列して表示。
diff -yはプリミティブなASCII版を出力する。Beyond Compare・Meld・WinMergeと上記の「サイドバイサイド」ビューは全てこの形式を使用する。視覚的なレビューと散文の校正に最適。 - インライン(スタック):削除行と追加行が交互に並ぶ1列形式。GitHubのPRビューのデフォルト。狭い画面と小さな変更に最もスペース効率が良い形式だ。
便利なgit diffのバリアント
git diff:インデックスと作業ツリーの比較。git diff --staged:HEADとインデックスの比較。git diff main..feature:ブランチの先端を比較。main...featureはマージベースに対して比較し、PRがマージされる内容を示す。git diff --word-diffと--color-words:マークアップフレンドリーで色付きの単語レベルdiff。git diff --no-index a.txt b.txt:gitリポジトリ外のファイルでも動作する。パティエンスやヒストグラムアルゴリズムを使えるスタンドアロンdiffの代わりとして手軽だ。git diff -w:全ての空白を無視する(上記の「空白を無視」チェックボックスに対応)。git diff --histogram:コマンドごとにアルゴリズムを切り替える。
ブラウザdiffを使う場面
- コードレビュー。ファイルの2バージョンを貼り付け、何が変わったかを確認し、変更が理にかなっているかを判断する。
- AI編集コードのレビュー。元のコードとLLMがリファクタリングしたバージョンをdiffして、ドリフトやハルシネーションを発見する。
- 法的契約書のレッドライン。バージョン3とバージョン4の間で合意した条項だけが変更されたことを確認する。
- 翻訳品質保証。同じ段落の2つのフランス語翻訳を比較したり、ごく少数の文字列だけ変更されるべき場合の以前と更新後のローカリゼーションファイルをdiffしたりする。
- 設定のドリフト。本番の
nginx.confをステージングと比較する。 - SQLスキーマの比較。2つのデータベースから
CREATE TABLE文をダンプし、欠落したインデックスやカラム型のドリフトを見つけるためにdiffする。 - 仕様のドリフト。2バージョンのOpenAPI / Swagger YAMLをdiffして、破壊的変更がドキュメント化されているかを検証する。
- Markdownブログ草稿。共著者が何を編集したかを発見する。
- コンプライアンス監査。規制機関向けにプライバシーポリシーを月次でdiffする。
- ロックファイルの調査。
package-lock.jsonまたはyarn.lockをdiffして依存関係のアップグレードを把握する。
正直な制限事項
- プレーンテキストのdiffにはセマンティックな認識がない。ファイル全体で
fooをbarにリネームすることは、全く無関係な書き直しと同じコストに見える。difftasticのようなツールは言語のASTを解析して構造的なdiff(「関数が追加された」「引数が並び替えられた」)を生成する。このページはそれを行わず、プレーンなLCS行diffだ。 - 空白と行末文字がノイズのあるdiffを引き起こす。CRLF対LF・末尾の空白・タブ対スペース。上記の「空白を無視」トグルはgitの
-wに対応する。 - バイナリファイルには対応していない。diffはテキストの概念だ。バイナリdiffには
bsdiff・xdelta・VBinDiffのようなツールがある。 - 順序感度。行diffは文書が行ごとに編集されたと仮定する。未ソートのCSVやJSONオブジェクトのキー並び替えには、まずソートまたは正規化したい。
- 移動検出。古典的なLCS diffは関数がファイル内で移動したことを認識せず、削除と挿入として表示する。Beyond Compare・Meld・difftasticは移動検出を試みる。このページはそれを行わない。
さらなる質問
なぜこのページは変更された行内の文字レベルのハイライトを表示しないのですか?
行レベルの処理はほとんどのユースケースに十分で、長い入力でも大幅に高速なためだ。行内の単語ごとのハイライトには2段階の精製が標準的だが、レイテンシーが増加する。行全体の差異が重要な場合(法的レッドライン・散文の校正)は、専用の単語レベルツール(コマンドラインのgit diff --word-diffなど)の方が適している。
ユニファイドdiffとサイドバイサイドの違いは何ですか?
ユニファイドdiffは全ての.patchファイルが使うコンパクトで機械可読な形式で、3行のコンテキストとhunksを@@でマークする。サイドバイサイドは2つのバージョンを並列列で表示し、視覚的なレビューには目に優しいが横方向のスペースを多く取る。ユニファイドはメールやコミットに使い、サイドバイサイドは広いモニターで読む。
ソースコードにはMyersより優れたアルゴリズムがありますか?
関数の移動や大規模な再配置があるソースコードには、はい、パティエンスdiff(Bram Cohen、2002年)とヒストグラムdiff(git、2010年頃)は一般的な行ではなく意味のあるユニーク行で整列するように設計されており、より読みやすい出力を生成する。どちらもgit diff --patienceとgit diff --histogramで利用できる。difftasticのようなAST対応ツールはさらに進んで実際の言語構造を解析する。
機密テキストをここに貼り付けても安全ですか?
はい、diffはJavaScriptのLCS実装を使いブラウザ内で完全に動作する。何もアップロードされず、入力に関するアナリティクスもなく、テキストのサーバー側ログもない。これはNDA・内部ソースコード・未公開の契約条項に安全なdiffツールの唯一のカテゴリーだ。クラウドベースのdiffサービスは貼り付けた内容を閲覧できる。