ドキュメントを保存したり、コードをコミットしたり、契約書をレビューに出したりするたびに、必ず生まれる疑問があります。「このバージョンと前のバージョン、一体何が変わったの?」テキスト比較(diff)は、その問いに答えるための技術的な核心です。UnixのコマンドラインのdiffからGitのコミット履歴、オンライン共同作業ツールの修正モードまで、背景にあるのはすべて同じロジックです。本記事ではアルゴリズムの仕組みからdiffの全体像を解説します。
1. Diffとは何か?
Diff(differenceの略)とは、2つのテキスト間の最小差分セットを算出する技術です。「旧バージョン」(A)と「新バージョン」(B)が与えられると、diffアルゴリズムは次のことを特定します:
- Bで新たに追加された行(または文字)
- Bで削除された行(または文字)
- 変更されていない行(または文字)
この差分セットは小さければ小さいほど良い——これが「最小編集距離(Minimum Edit Distance)」問題の本質であり、ほとんどのdiffアルゴリズムの最適化目標でもあります。
「Diff」は技術そのものと、その出力結果(差分レポート)の両方を指します。動詞として:「この2つのファイルをdiffしてみよう」、名詞として:「このdiffには3か所の変更がある」。
2. 中核アルゴリズム:LCSとMyers Diff
2.1 最長共通部分列(LCS)
ほとんどのdiffアルゴリズムの理論的基盤は最長共通部分列(Longest Common Subsequence:LCS)です。LCSとは、2つの列に共通して現れ、かつ相対的な順序を保つ最長の部分列です。
例:
- 列A:
猫、犬、魚、鳥 - 列B:
猫、魚、ウサギ、鳥 - LCS:
猫、魚、鳥(長さ3)
LCSが特定されると、LCSに含まれないAの要素が「削除」、LCSに含まれないBの要素が「追加」となります。LCSアルゴリズムの時間計算量はO(mn)で、mとnはそれぞれ2つのテキストの長さです。
2.2 Myers Diffアルゴリズム
1986年、Eugene MyersはLCSよりも効率的なdiffアルゴリズムを発表しました——Myers diffです。その特徴:
- 最良ケースの時間計算量はO(n + d²)(dは差分数)
- 差分が少ない場合(実際の使用で最も一般的)、O(mn)より大幅に高速
- 「元の構造をできるだけ保つ」傾向があり、可読性が高い差分を生成
Myers diffは現在、Git・GNU diffなど主要ツールのデフォルトアルゴリズムです。
2.3 Patience Diff
Patience diffはBram Cohen(BitTorrent作者)が設計した別の手法で、Gitでは--diff-algorithm=patienceで使用できます。核心的な考え方:
- 両バージョンで「ちょうど1回だけ」現れる行をアンカーとして設定
- それらのアンカーをもとにファイルをセクションに分割
- 各セクションに対してLCSを再帰的に適用
実際の効果として、大規模なコードリファクタリング(関数の大量移動)では、Myers diffよりも直感的で読みやすい出力を生成します。
| アルゴリズム | 時間計算量 | 適用場面 | デフォルト採用 |
|---|---|---|---|
| LCS(動的計画法) | O(mn) | 理論的基礎、小規模ファイル | — |
| Myers diff | O(n + d²) | 一般的なコード比較 | Git、GNU diff |
| Patience diff | O(n log n) | コードリファクタリング、関数移動 | Bazaar(オプション) |
| Histogram diff | O(n log n) | Myersの改良版 | Git(オプション) |
3. Unified Diffフォーマット
どのアルゴリズムを使っても、差分結果は通常「Unified diff」フォーマットで出力されます——業界標準であり、git diffのデフォルト形式でもあります。
典型的なUnified diffの出力:
--- a/hello.txt
+++ b/hello.txt
@@ -1,5 +1,5 @@
1行目は変更なし
-旧2行目
+新2行目
3行目は変更なし
-削除された4行目
5行目は変更なし
+追加された6行目
フォーマットの説明:
---:旧バージョン(a)+++:新バージョン(b)@@:ハンクヘッダー。-1,5は旧バージョンの1行目から5行、+1,5は新バージョンの1行目から5行- スペース始まり:変更なしのコンテキスト行(デフォルト3行表示)
-始まり:削除された行+始まり:追加された行
4. 文字単位 vs 行単位の比較
標準diffは「行」単位で比較しますが、より細かい粒度を提供するツールもあります:
| モード | 比較単位 | 適用場面 |
|---|---|---|
| 行単位diff | 1行全体 | コード、設定ファイル、一般文書 |
| 単語単位diff(word diff) | 単語 | 自然言語テキスト、Markdown |
| 文字単位diff(char diff) | 1文字 | 細かいスペルミス修正、法律文書 |
Gitはgit diff --word-diffで単語単位の比較に切り替えられます。オンラインテキスト比較ツールは通常、行レベルと文字レベルの差分を同時に表示し、変更箇所を一目で把握できます。
5. 実際の活用場面
5.1 バージョン管理(Git)
Gitのすべてのコミットには差分が記録されています。git diff、git show、git log -pなどのコマンドで変更内容を確認できます。GitHubやGitLabのプルリクエスト画面は、本質的にはdiffをビジュアル化したものです。
5.2 文書校正(変更追跡)
Microsoft Wordの「変更履歴」やGoogle Docsの「提案モード」は、どちらもdiffの概念をベースにしています——誰がいつ何を変更したかを記録し、レビュアーが各変更を承認または拒否できる仕組みです。
5.3 契約書・法的文書
契約書の修正では、弁護士はすべての文字の変更を確認する必要があります。文字単位の比較モードを使えば、「originally」→「previously」のような微妙な表現の変更も一目で分かり、見落としを防げます。
5.4 設定ファイルとIaC
TerraformやAnsibleなどのIaCツールは適用前に「plan diff」を表示し、どのクラウドリソースが作成・変更・削除されるかをエンジニアが確認できるようにしています。
5.5 データ比較
異なる時点のCSVやJSONエクスポートのデータセットをdiffで比較すると、どのフィールドが変更され、どのレコードが追加・削除されたかを素早く特定できます。データ監査や問題の調査に最適です。
6. オンラインテキスト比較ツールの使い方
オンラインテキスト比較ツール(本サイトのツールなど)はインストール不要で、2つの文字列をすばやく比較するのに最適です:
- テキストを貼り付ける:旧バージョンを左欄に、新バージョンを右欄に貼り付けます
- 比較モードを選択:行単位または文字単位(ツールによって異なる)
- 結果を確認:削除された内容は赤、追加された内容は緑でハイライト表示されます
- 差分を移動:「前へ」「次へ」ボタンで各差分箇所に素早くジャンプ
オンラインツールは、コマンドラインに慣れていない非技術者の文書比較に特に便利で、直感的に操作できます。
7. よくある質問
7.1 diffで画像やPDFを比較できますか?
標準diffは純粋なテキストツールであり、画像やPDFなどのバイナリファイルを直接比較することはできません。PDFはテキスト内容を比較するために先にテキストに変換する必要があります。画像比較にはピクセル差分を比較する専用ツールが必要で、テキストdiffとは根本的に異なる概念です。
7.2 diff結果が期待通りにならないのはなぜですか?
最もよくある原因:
- 空白文字の違い:インデント方法(タブ vs スペース)や行末文字(Windows CRLF vs Unix LF)の違いが多くの偽陽性を生じさせます。
-w(全空白を無視)や-b(末尾空白を無視)オプションでフィルタリングできます。 - エンコーディングの違い:2つのファイルが異なる文字エンコーディング(UTF-8 vs Shift-JISなど)を使用している場合、先に統一してから比較してください。
- アルゴリズムの選択:異なるアルゴリズムは同じ差分を異なる「切り方」で表現します。アルゴリズムを変更するとより直感的な結果が得られることがあります。
7.3 diffとmergeはどう関係していますか?
マージはdiffの延長線上にあります:同一の元ファイルを2人が別々に修正した場合、mergeツールはそれぞれをdiffし、2つの差分セットを結合しようとします。両方の変更が同じ行を触れた場合、「マージコンフリクト」が発生し、手動での解決が必要です。
8. まとめ
テキスト比較技術は一見シンプルに見えて、背後には古典的なアルゴリズム問題が潜んでいます。LCSからMyers diff、行単位から文字単位まで、異なる比較戦略はそれぞれ異なる場面に適しています。これらの原理を理解することで、Gitなどのバージョン管理ツールをより効果的に活用でき、文書共同作業・契約審査・日常的な編集作業での変更追跡もより正確になります。