契約書の旧版と新版を付き合わせて「どこが変わったのか」を数時間かけて確認したことはないでしょうか。 あるいは GitHub の PR 画面で赤と緑に塗り分けられた差分を見たことがあるはずです。 どちらも同じ「diff(差分)」という仕組みの応用ですが、差分は開発者だけのものではなく、 仕様書・設定ファイル・契約書など、あらゆる文書比較で役立ちます。 この記事では、diff の仕組み・Myersアルゴリズムの考え方・unified diff の読み方・実務での使い分けを整理します。
diff とは — 2つのテキストを最小操作で表現する
diff(ディフ)とは、2つのテキストの違いを「追加・削除」という最小限の編集操作で表現する仕組みです。 1976年頃に UNIX 系 OS で登場した diff コマンドがルーツで、現代ではバージョン管理・コードレビュー・設定ファイル比較など、 テキストを扱うあらゆる場面で使われています。
単純に「違う・違わない」を判定するだけなら、2つの文字列を1文字ずつ突き合わせるだけで済みます。 しかし diff のゴールはそこではなく、「どう編集すれば A が B になるか」をできるだけ少ない操作で説明することです。 これを「最小編集スクリプト(Shortest Edit Script)」と呼び、差分アルゴリズムの中心的な課題になっています。
最長共通部分列(LCS)問題との関係
diff の計算は、数学的には最長共通部分列(Longest Common Subsequence、LCS)問題の裏返しです。 2つの文字列に共通して現れる最長の部分列が分かれば、それ以外の部分が「追加」か「削除」になる、という考え方です。
たとえば「ABCBDAB」と「BDCAB」の LCS は「BCAB」または「BDAB」(長さ4)です。 LCS が長いほど、2つの文字列は似ていることになります。
LCS は動的計画法(DP)で解くと、2つの文字列の長さを N と M として O(NM) の時間・空間計算量がかかります。 小さなテキストなら問題ありませんが、1万行を超えるファイル同士の比較では億オーダーの計算が必要になり、実用的ではありません。 ここで Myers アルゴリズムが登場します。
Myersアルゴリズム(1986年)— diffの事実上の標準
1986年に Eugene W. Myers が発表した論文「An O(ND) Difference Algorithm and Its Variations」で提案されたアルゴリズムが、 現代の diff の事実上の標準になっています。Git・Subversion・多くの diff ツールの内部で採用されています。
このアルゴリズムの計算量は O((N+M)D) で、D は「必要な編集操作の数(編集距離)」です。 LCS の DP と違い、2つのテキストが似ている(D が小さい)ほど高速に動作する点が大きな利点です。 実際のコード差分では、ファイル全体の数%しか変更されていないことが多く、Myers アルゴリズムはこの性質に非常にフィットします。
豆知識
Myers アルゴリズムはグラフ探索として定式化されます。 「A を行方向、B を列方向に並べた格子」を考え、対角線を進めば「共通行」、右か下に進めば「追加・削除」になります。 このグラフ上で最短経路を求めるのが Myers の考え方です。
差分の表示形式
同じ差分データでも、表示形式によって読みやすさが変わります。代表的な3形式を比較します。
| 形式 | 特徴 | 主な用途 |
|---|---|---|
| unified diff | 前後数行の文脈と変更行を 1 つのハンクにまとめる。コンパクト | Git・パッチファイル・コードレビュー |
| context diff | 変更前・変更後を別ブロックで並べる。unified より冗長 | 古いパッチ形式(現在は少数派) |
| side-by-side | 左右に並べて視覚的に比較。差分量が多いときに見やすい | IDE の diff ビュー・GitHub の Split View |
unified diff の読み方
--- a/config.yml
+++ b/config.yml
@@ -1,5 +1,6 @@
name: sakutto-tools
version: 1.0.0
-timeout: 30
+timeout: 60
+retry: 3
debug: false
--- a/...と+++ b/...が変更前・変更後のファイルパス@@ -1,5 +1,6 @@はハンクヘッダー。「前5行・後6行」の対応範囲を示す- 先頭が空白の行は「文脈行」(共通行)、
-は削除、+は追加
行単位 vs 単語単位 vs 文字単位の比較
diff は比較する「単位」を変えるだけで、読みやすさが大きく変わります。用途に応じて使い分けるのがコツです。
| 粒度 | 向いている用途 | 注意点 |
|---|---|---|
| 行単位 | コード差分・ログ比較・設定ファイル | 行内の小さな変更が全行差分になる |
| 単語単位 | 文章の校正・英文の推敲・GitHub の word-level diff | 日本語は単語区切りが曖昧で精度が落ちやすい |
| 文字単位 | 日本語文章の校正・一文字だけの修正確認 | 差分量が多いと視覚的に読みにくい |
GitHub の PR レビューで「word-level diff」が読みやすく感じるのは、行単位の赤緑に重ねて、 その行の中のどの単語が変わったかを強調表示しているからです。 行単位だけでは「1行全部が赤と緑で塗り分けられて、どこが変わったのか分からない」ことがありますが、 単語単位の強調を加えることで視認性が格段に上がります。
実務で diff を使うシーン
diff は開発現場だけでなく、次のような実務シーンで役立ちます。
- 仕様書・契約書の改定確認 — 法務レビュー後の修正版と原版を突き合わせて、変更箇所だけを短時間で確認する
- 設定ファイルのサーバー間差異 — 本番と検証環境の設定が揃っているか、どの行が違うかを即座に把握する
- APIレスポンスの回帰テスト — リリース前後で同じリクエストのレスポンスがどう変わったかを比較する
- ドキュメントのバージョン管理 — マニュアルの前版と新版を比較し、読者への変更通知を作成する
- 翻訳メモリの更新 — 原文の変更点を抽出して、翻訳すべき箇所だけを特定する
プレーンテキストの比較なら、テキスト差分比較ツールに2つの文章を貼り付けるだけで、行単位・単語単位・文字単位の差分が色分けで表示されます。 ファイルはサーバーに送信されず、ブラウザ内だけで処理されるので、社外秘の契約書や仕様書でも安心して使えます。
JSON差分の特殊性 — テキストdiffだけでは不十分
JSON を含む構造化データを比較する場合、単純なテキスト diff では困ることがあります。
第一に、キーの並び順に意味がないこと。 JSON の仕様では「オブジェクトのキー並び順は未定義」とされており、同じデータでもサーバーやライブラリの都合で順序が入れ替わります。 テキスト diff で見るとすべての行が違って見えてしまいますが、意味的には全く同一なケースが多々あります。
第二に、整形・インデントの揺れ。 1行で出力された JSON と、インデント付きに整形された JSON は、テキストとしては全く違いますが、データとしては同じです。
こうした問題を避けるには、JSON を構造として理解したうえで比較するツールが必要です。 ぱんだツールズのJSON差分比較ツールは、キー・値のレベルで差分を抽出するので、並び順やインデントの違いに惑わされません。 API のバージョン間レスポンス比較や、設定ファイルの変更確認に適しています。
注意
JSON 配列の並び順には意味があります(先頭要素・2番目・3番目…という順序が仕様的に保証される)。 一方オブジェクトのキー並び順は未定義です。 JSON差分ツールは通常、「配列は順序あり・オブジェクトは順序なし」として比較しますが、 用途によっては配列も集合として比較したいことがあります。ツールのオプションを確認しましょう。
まとめ
- diff は2つのテキストを「追加・削除」の最小操作で表現する仕組み。1976年の UNIX diff が原点
- 差分計算は数学的には最長共通部分列(LCS)問題の裏返し。単純な DP は O(NM) で大きなファイルに不向き
- Myersアルゴリズム(1986年)が事実上の標準。Git・多くの diff ツールに採用されている
- unified diff のハンクヘッダー
@@ -1,3 +1,4 @@は「変更前1行目から3行・変更後1行目から4行」の意味 - 行単位・単語単位・文字単位を用途に応じて使い分ける。GitHub の読みやすさは word-level 強調の功績
- 行の入れ替えは diff では大量差分として出る。意味的な移動検出は別ツール(--color-moved・difftastic など)を使う
- JSON はキー並び順やインデントの揺れがあるため、JSON差分比較ツールで構造的に比較するのが確実
- プレーンテキストの比較はテキスト差分比較ツールで。どちらもブラウザ内処理で機密データも安心