Skip to content
ブログに戻る
チュートリアル

テキスト差分オンライン:LCS/Myers アルゴリズム解説

テキストをブラウザで比較:side-by-side と unified diff の両ビュー、LCS/Myers アルゴリズム解説、コードレビューやログ比較で即使えるオンライン text diff。

14 分で読める

テキスト差分オンライン:LCS/Myers アルゴリズムと 6 つのユースケース

テキスト差分オンラインツールは、ひとつの問いに即答する。バージョン A と B のあいだで、実際に何が変わったのか。二つのテキストブロックを貼り付けると、ツールが最長共通部分列(Longest Common Subsequence)アルゴリズムを走らせ、すべての挿入・削除・編集を side-by-side または unified ビューで表示する。たいていは 1 ミリ秒以内で完了する。

このガイドは、コードレビューを行う開発者、ログスライスを比較する SRE、契約書をレッドライン処理する弁護士、編集を確認するライター向けだ。アルゴリズム(LCS、Myers、Patience)、二つの標準ビュー、「すべてが変わって見える」という苦情の 95% を解消する無視オプション、JSON diff を選ぶべきタイミング、コピペできる 6 つのユースケース、アルゴリズム挙動から説明できる落とし穴を扱う。

先にツールを使いたい場合は、テキスト比較・差分ツールを開けばよい。すべてブラウザ内で動作し、アップロードは発生しない。

1. テキスト差分とは何か

テキスト差分とは、一方のテキストをもう一方に変換するための、挿入と削除の最小集合のことだ。各行に「追加」「削除」「未変更」の印を付けて出力する。最近の diff は単語または文字レベルの二段目スキャンを加え、一文字の編集が行全体ではなく該当トークンだけをハイライトする。

1.1 文字レベルの一致(===)では不十分な理由

200 行の設定ファイルの先頭に 1 行挿入すると、素朴な文字比較では挿入点以降のすべてのバイトが「異なる」と報告されてしまう。テキストそのものは変わっていない。位置がずれただけだ。diff アルゴリズムは「次の 199 行は同じ行のままで、1 行ずれただけ」と認識し、挿入を 1 件として報告しなければならない。この認識を与えるのが LCS で、git、GitHub、各種コードレビューツールが LCS を実装している理由でもある。

1.2 Side-by-side と unified diff

Side-by-side は二つのバージョンを並列のカラムに配置し、セルを色分けする。追加は緑、削除は赤、変更は黄。Unified diff は GNU diff 由来の古いテキスト形式で、1 カラム、-+ のマーカー、各ハンクの周囲に 3 行のコンテキストを置く。同じ比較を二通りに見せている。どちらを選ぶかはセクション 4 で扱う。

1.3 テキスト差分が使われる場面

GitHub と GitLab のコードレビュー。ローカルの git diff 出力。Slack に貼り付けたパッチ。契約書のレッドライン処理。翻訳レビュー。+/- 出力で失敗する CI のスナップショットテスト。インシデント時のログタイムライン調査。二つの .env ファイルの比較。二つのテキスト塊を行単位で対応付ける必要のある場面なら、どこでも当てはまる。

テキスト比較・差分ツールを開いて二つのテキストを貼り付ければ、ここまでの話を実際に体験できる。比較はすべてブラウザ内のローカル処理だ。

2. テキスト差分の背後にあるアルゴリズム(LCS + Myers + Patience)

2.1 最長共通部分列

行の系列 A と B が与えられたとき、最長共通部分列とは、両方に同じ順序で現れる行の最長リストだ。連続している必要はない。LCS が得られれば diff は素直に組み立てられる。A にあって LCS にない行は削除、B にあって LCS にない行は追加、LCS にある行は未変更となる。

古典的な LCS は N × M の動的計画法テーブルで動作する。セル (i, j) には A の最初の i 行と B の最初の j 行の LCS の長さが入る。テーブルを左から右、上から下に埋め、右下のセルから逆方向にたどって編集スクリプトを再構築する。時間も空間も O(N×M)。2000 行のファイル二つなら問題ないが、10 万行のログでは遅い。

2.2 Myers(1986)

Eugene Myers の 1986 年の論文「An O(ND) Difference Algorithm and Its Variations」は、問題を編集グラフ上の最短経路として再定式化する。ノードは二つの入力の位置 (i, j)、水平移動は削除、垂直移動は挿入、対角移動はマッチだ。最短経路が最小の編集スクリプトに対応する。

Myers の計算量は O((N+M)D)。D は編集スクリプトのサイズだ。二つのテキストが似ている場合(diff の通常ケース)、D は小さく、アルゴリズムは事実上線形になる。git diff、GNU diff、GitHub の PR レンダラのデフォルトであり、Web 入力の 99% にとって妥当な選択だ。

2.3 Patience diff(Bram Cohen、2005)

Patience diff は別のアプローチを取る。それぞれの入力に厳密に 1 回だけ現れる行(「ユニークアンカー行」と呼ぶ)を見つけて対応させ、アンカー間の空白部分に再帰的に適用する。数学的にはやや扱いにくく、最悪計算量も悪いままだが、コードに対する出力は格段に読みやすい。

なぜか。Myers は編集距離を最小化する。これは数学的には最適だが、最適なアラインメントが無関係な閉じ括弧や空行を横切ると、視覚的には荒れる。Patience は共通の定型句(どのファイルにも } 行はあり、どのファイルにも空行がある)でアラインメントを取ろうとしないので、関数の境界が崩れない。Bram Cohen は Bazaar のために考案した。Git は git diff --patience として同梱している。近縁の Histogram アルゴリズム(git diff --histogram)は出力品質が同等でわずかに高速だ。

同じファイルの二つのバージョンで関数が移動した場合を考えよう。Myers は関数 A の閉じ括弧と関数 B の閉じ括弧をアラインし、本体を完全に異なると報告するかもしれない。Patience はユニークな関数名にアンカーを置き、きれいな移動として報告する。同じ入力でも、レビュー体験はまるで違う。

2.4 アルゴリズム比較

特性Myers(デフォルト)PatienceHistogram
計算量O((N+M)D)一般ケースで ~O(N log N)Patience と同等
最適編集距離はい、最短スクリプトいいえ、長くなることもあるいいえ、長くなることもある
コードでの読みやすさ括弧や空行の誤アラインが起きることがある優秀。ユニーク行をアンカーにする優秀
採用例git デフォルト、GNU diff、GitHub UIgit diff --patience、Bazaargit diff --histogram
適する場面大半の入力での速度と正確さコードレビュー、リファクタ diffPatience と同じ、わずかに高速

2.5 このツールが採用している方式

テキスト比較・差分ツールは古典的な動的計画法ベースの LCS に、二つの最適化を載せている。共通の prefix と suffix のトリミング、行内ワードレベル差分のためのトークンレベル LCS の二段目スキャンだ。1 行だけ変更された 2000 行の設定ファイル二つの diff は、トリミング後に 1×1 の DP テーブルに収まり、1 ミリ秒未満で描画される。典型的な Web 入力では Myers と DP の選択は見えない。どちらもブラウザが結果を描画するより速く完了する。

3. 行内ワードレベル差分:一文字の変更で行全体が光る理由

行内の識別子を一つ変えただけなのに、行全体が赤と緑で光る。バグだろうか。違う、設計だ。

diff はまず行レベルで LCS を走らせる。「14 行目が置き換えられた」というように。次に、置き換えられたペアごとにトークンレベルで二度目の LCS を走らせる。トークンは Unicode の単語境界で分割して生成する。文字と数字の連続はひとまとまりに保たれ、空白と句読点はそれぞれ独立したトークンになる。二度目の LCS が、その行の中で最小のトークンレベル編集スクリプトを返す。

レンダラは行全体をハイライト色で描画し、行を見つけやすくする。そのうえで、変更されたトークンだけを明るい背景で塗る。周囲の未変更トークンは同色の暗いバージョンを帯びる。存在はするが、視覚的には静かだ。目は正確な編集箇所に着地する。

例 1:識別子のリネーム。 function getUser(id)function getUser(userId) になる。行全体が「変更」と印付けされる。行の中では id(赤の打ち消し線)と userId(明るい緑)だけが行内ハイライトを帯びる。それ以外は暗いままだ。

例 2:ログのレイテンシ変化。 POST /api/orders 201 88msPOST /api/orders 201 4200ms になる。行は「変更」になる。行内では 884200 だけが明るい。パス、メソッド、ステータスコードは暗いままだ。インシデントタイムラインを読む側が必要とする見え方だ。

トークンの変化が多すぎる場合、ワードレベルハイライトはノイズになる。ツールはペア化された remove + add 表示にフォールバックする。元の行を削除として表示し、新しい行を追加として表示し、行内の色付けはしない。閾値はおおむね「トークンの半分以上が異なる場合」だ。

整理すると、行レベル diff は「どの行が変わったか」を、ワードレベル diff は「その行のどの文字が変化を担っているか」を教える。テキスト比較・差分ツールの中で Sample をクリックすれば、同じ入力に対して両方のビューを確認できる。

4. Side-by-side と unified diff:二つのビュー、一つの diff

4.1 Side-by-side ビュー

二カラム。左に元、右に変更後。マッチする行は水平方向にアラインされる。追加された行は右カラムにだけ緑の背景で現れる。削除された行は左カラムにだけ赤の背景で現れる。変更ペアは隣り合って配置され、黄色のガターと行内ワードハイライトを伴う。

人間が diff を読む場合は side-by-side を使う。PR レビュー、指導、デモ、技術的でないステークホルダーと一緒に契約変更を確認する場面だ。目で読むためのビューだ。

欠点もある。可搬性がない。side-by-side のレンダリングを Slack に貼り付けても、誰もそれを適用できない。patch にパイプもできない。共有や適用には unified が必要だ。

4.2 Unified diff 形式

Unified diff は GNU diff が定義し POSIX で標準化された、50 年来のプレーンテキスト形式だ。具体例を示す。

--- original
+++ modified
@@ -1,3 +1,4 @@
 1. The service is provided as-is.
 2. Either party may terminate with 30 days notice.
+2a. Termination notice must be in writing.
 3. Disputes are resolved in California courts.

最初の 2 行はソースファイル名を示す。@@ -L,C +L,C @@ の行はハンクヘッダだ。-L,C は元ファイルの L 行目から C 行が関与することを意味し、+L,C は変更後について同じことを表す。ハンク内で空白で始まる行はコンテキスト(未変更)、- は削除、+ は追加だ。

各変更の上下 3 行のコンテキストが GNU のデフォルトだ。多くのツールでは -U n で変更できる。diff -U0 でコンテキストなし、diff -U10 で 10 行のコンテキスト。ハンクヘッダは選択した値を追跡する。

テキスト比較・差分ツールでは、Unified タブをクリックしてビューを切り替えるか、Copy unified diff でパッチをクリップボードにコピーできる。

4.3 Unified diff の可搬性

Unified diff はどこへでも持ち運べる。テキスト変更のやり取りで広く通用する形式だ。

行き先unified diff を受け付けるか方法
GNU patchはいpatch -p1 < diff.patch
git applyはいgit apply diff.patch
GitHub PR レビューコメントはい(```diff ブロック内)色付きで描画
GitLab MR コメントはい同じフェンスブロック
Bitbucket / Azure DevOps PRはい同じフェンスブロック
Slack / Discord 貼り付け部分対応コードブロック内のテキストとして描画、色なし
VS Code「Open Patch」はいSource Control からパッチを適用
Jira / Linear の issue 本文部分対応コードブロック内で動作、Apply ボタンなし

同じ ---/+++/@@ の 9 行のテキストが、patchgit apply に適用でき、3 つの PR プラットフォームで描画され、Slack の貼り付けも生き残る。実務で使う diff 形式の中では、この互換性の広さは抜けている。

4.4 どちらを選ぶか

レビューには side-by-side、共有と適用には unified だ。自分で diff を読むなら、カラムのほうが速い。下流の誰か、あるいは何か(レビュアー、ツール、パッチコマンド)が消費する必要があるなら、unified 形式をコピーする。

5. 無視オプション:空白、ケース、空行、行末文字

「すべてが変わって見える」という苦情のほとんどはノイズだ。4 つのトグルがその 95% を片付ける。

  1. ケースを無視Aa にマップする。git diff -i に相当する。環境変数の比較、SQL キーワードのスタイル監査など、慣習が大文字と小文字に揺れるが意味は同じ、という場面で使う。
  2. すべての空白を無視は、比較前に空白・タブ・改行をすべて畳む。git diff -w に相当する。タブ↔スペースの再フォーマット、インデントの書き直し、「Prettier に移行した」系の行数を破壊する diff への対処になる。これらの変更に対して無視空白の diff をかけると、87 件の変更が 4 件に減るような結果になりやすい。
  3. 行末スペースとタブを無視は行末の空白だけを取り除く。git diff -b に相当する。Windows と Unix のマシン間でコピーした後の CRLF ノイズに有効だ。行末の \r 文字がフィルタされ、実際の内容がそろう。
  4. 空行を無視は diff の前に空行を落とす。「段落の改行を一つ追加しただけなのに、12 段落目がまったく違って見える」という散文 diff に効く。

「87 件の変更」を報告していた 200 行の設定ファイルは、すべての空白を無視を有効にすると、たいてい「4 件の変更」になる。あらゆる行をフラグしていた Windows-to-Unix のコピーは、行末スペースを無視するとゼロになる。各トグルは独立しており、セッションをまたいで保持される。

CRLF と LF。 Windows の行末文字は \r\n、Unix は \n、クラシック Mac は \r だ。正規化しない Unix エディタで Windows のファイルを開くと、行末の \r が残る。すべての行が「内容は一致するが行末に \r がある」として diff される。行末スペースを無視すれば、実体のある変更を失わずに、このノイズを抑えられる。

ひとつの注意点。 無視オプションは裏返るリスクがある。ケース無視を有効にすると、LOG.errorlog.Error に変えるリファクタは同一に見える。すべての空白を無視を有効にすると、Python のインデントバグが見えなくなる。問うている質問に合わせてトグルを選び、終わったらオフに戻す。

6. テキスト diff、JSON diff、Git diff:決定マトリックス

テキスト diff は構造を理解しない、行とワードのマッチングだ。これは散文には望ましく、JSON には望ましくない。

6.1 決定マトリックス

入力タイプテキスト diffJSON diffGit diff
散文 / Markdown / 契約書最適不適部分対応(追跡済みファイルのみ)
コードスニペット(単一ファイル貼り付け)最適不適部分対応(リポジトリが必要)
リポジトリ内のコード(複数ファイル)部分対応不適最適
API の JSON レスポンス不適(キー順序で誤検出)最適不適
YAML / TOML 設定部分対応(キー順序で誤検出)最適(変換後)部分対応
CSV の行単位比較部分対応不適不適
ログ / heredoc最適不適不適
バイナリファイル不適不適git diff --binary

6.2 テキスト diff が不適切なとき

古典的な間違いが三つある。

キーが並び替わった JSON。 {"a":1,"b":2}{"b":2,"a":1} は同じ JSON ドキュメントだ。テキスト diff はすべての行を変更として報告する。実際に異なる行なのだから当然だ。JSON 差分ツールを使う。JSON のキーは順序を持たないことを前提に動く。

再フォーマットされた YAML 設定。 値を一つ変えて、ファイルをフォーマッタにかけると、インデント、キー順序、引用符の付け方がすべてずれる。テキスト diff は完全な書き直しと報告する。両方のファイルを JSON に変換してから、JSON Diff で比較するとよい。

リネームを伴う複数ファイルのリファクタ。 Git はリネームを追跡するが、テキスト diff は追跡しない。ファイルを連結して一つの塊にし、二つのツリーを比較すると、ファイルをまたぐすべての移動が「削除 + 追加」として現れる。代わりに git diff(あるいは git diff --find-renames=80%)を使う。

6.3 テキスト diff がちょうど適切なとき

散文。どこからでも貼り付けたコードスニペット。契約書のレッドライン。ログスライス。自然言語の文を対応付ける翻訳レビュー。シェルが上から下に読むため順序が意味を持つ .env ファイル。行そのものが意味を運ぶ場面なら、テキスト diff の出番だ。

JSON diff からノイズ(タイムスタンプ、リクエスト ID、自動生成 UUID)を除外する深掘りは、JSON差分でタイムスタンプとIDを無視する方法を読んでほしい。

7. 6 つの実例(コピペできる入力付き)

7.1 コードレビュースニペット — 関数のリネーム

PR をレビューしている。著者は iduserId にリネームし、ガード節を追加した。両バージョンを貼り付ける。

// Original
function getUser(id) {
  const u = db.users.find(x => x.id === id);
  return u;
}
// Modified
function getUser(userId) {
  if (!userId) return null;
  const u = db.users.find(x => x.id === userId);
  return u;
}

diff は変更された 3 行と追加された 1 行を表示する。行内ワードハイライトは iduserId のすべてのトークンを印付ける。新しいガード節は緑の背景で現れる。無視オプションはオフ。テキスト比較・差分ツールで試して、unified 出力をコピーすればレビューコメントとして残せる。

7.2 契約書またはポリシーのレッドライン — 1 条項の挿入

契約書 50 段落のうち、1 条項を挿入した。昨日のバージョンを左に、今日のバージョンを右に貼り付ける。

1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
3. Disputes are resolved in California courts.
1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
2a. Termination notice must be in writing.
3. Disputes are resolved in California courts.

diff は未変更の 49 行と追加された 1 行(+2a. Termination notice must be in writing.)を描画する。法務レビューの証跡として unified diff をエクスポートできる。

7.3 ログタイムラインの調査

レイテンシの退行を疑っている。インシデント発生前と発生中のアクセスログのスライスを取り出す。

GET /api/users 200 14ms
POST /api/orders 201 88ms
GET /api/orders/42 200 21ms
GET /api/users 200 14ms
POST /api/orders 201 4200ms
GET /api/orders/42 500 21ms

行内ハイライトが 884200(50 倍のレイテンシ跳ね上がり)と 200500(注文詳細エンドポイントが失敗し始めた)を浮かび上がらせる。フィールド抽出、エンドポイント別グルーピング、パーセンタイル計算など、より高度なログ作業には、ログが JSON ならば diff とjq チートシートを組み合わせる。

7.4 翻訳レビュー — プレースホルダの保持

新しい翻訳エージェンシーを雇い、新しいコピーが構造的に古いものと一致するか検証したい。左に旧訳、右に新訳を貼り付ける。翻訳者は文字列の末尾に余計なスペースを追加しがちなので、行末スペース・タブを無視を有効にする。

diff はすべての {username}{count}%s プレースホルダが所定の位置にあることを確認する。変わるのは自然言語のテキストだけだ。プレースホルダが欠落していれば、行内 diff で削除トークンとして現れる。リリース前に拾える。プレースホルダの形式自体を比較したい場合は、正規表現チートシート\{\w+\} などをカバーしている。テキスト比較・差分ツールで試してみよう。

7.5 設定または .env の監査 — 本番 vs ステージング

二つの .env ファイルを比較する。段落スタイルのグルーピングがセクションを誤アラインしないよう、空行を無視をオンにする。diff は、どのキーの値が異なるか、どのキーが片方の環境にしか存在しないか、コメントがどこで同期から外れたかを示す。「ステージングでは動くが本番では動かない」というデバッグセッションを事前に潰しておく作業だ。

7.6 散文または草稿の改訂

編集者から草稿が戻ってきた。元の文章を左に、編集版を右に貼り付ける。行内ワード diff が、どの文が書き直されたか、どこは触れられていないか、どの段落が挿入されたかを示す。変更を 1 件ずつ受け入れるか拒否するか決められる。Track Changes 機能も、Word ファイルも、独自フォーマットも要らない。

8. よくある落とし穴とその症状の読み解き方

ユーザーの痛点のほとんどはアルゴリズムの挙動で説明できる。よくある 5 つの苦情と、その実体を示す。

落とし穴 1:「Windows から Unix にコピーした後、すべての行が赤になる。」 症状:内容が同じに見えるのに、diff のすべての行が変更として表示される。原因:CRLF 行末からの行末 \r 文字。対処:行末スペース・タブを無視をトグルする。diff は実体のある変更に絞られる。

落とし穴 2:「JSON を貼り付けたら 100% の行が異なる。」 症状:同等のはずの二つの JSON オブジェクトが完全に変更されたように見える。原因:キーの並び替え。テキスト diff は行順を有意とみなすが、JSON はそうではない。対処:JSON 入力にはすべて JSON 差分ツールを使う。

落とし穴 3:「タブ↔スペースの再フォーマットで diff が爆発した。」 症状:87 件の変更がすべてインデント。原因:フォーマッタが各行の先頭の空白を変えた。対処:すべての空白を無視で空白の差を畳むと、意味のある変更が残る。

落とし穴 4:「diff は同一と言うが cmp は不一致と言う。」 症状:diff は差分なしと報告するが、バイトレベル比較ではファイルが異なる。原因:前のセッションで残った無視オプションが実体のある変更を隠している。対処:無視オプションパネルを開いてすべてのトグルをオフにし、再度 diff を取る。

落とし穴 5:「短い編集が remove + add として現れる。」 症状:小さな変更が行内ハイライトではなく、別々の削除行と追加行として現れる。原因:変更トークンの比率が行内閾値を越え、レンダラがペア行表示にフォールバックした。これは設計上の挙動であってバグではない。Unified ビューに切り替えれば、パッチツールが期待する古典的な -/+ ペアが見える。

9. プライバシー、性能、そしてコマンドラインに切り替えるとき

テキスト比較・差分ツールのすべての比較は、ブラウザ内の JavaScript で実行される。アップロードもなければ、一時ファイルもサーバーログも、貼り付けたテキストに対する解析もない。プロプライエタリなコード、社内契約書、プライベートログなど、サードパーティのサーバーに貼り付けたくないものを扱える。

実用上の制限:片側あたりおよそ 5,000 行または 1 MB。合算 200 KB を超えるとライブ diff は無効になり、入力中にページがブロックされないよう手動の Diff ボタンに切り替わる。5,000 行を超えると入力は切り詰められ、警告が表示される。これらの制限は、diff がメインスレッドで動く(web worker を使わない)ためだ。小さな入力では、worker への受け渡しとシリアライズのコストが diff 自体より大きくなる。

入力がブラウザの手に余るときは、コマンドラインに移る。

# Unified diff between two files
diff -u a.txt b.txt

# Same, but using git's diff engine (Patience, Histogram, color)
git diff --no-index a.txt b.txt
git diff --no-index --patience a.txt b.txt

# Streaming diff viewer for huge files (Rust, side-by-side, syntax-aware)
delta a.txt b.txt

数 MB のログ、バイナリファイル、複数ファイルのリポジトリ diff、delta のような構文認識色付けが必要な場面、diff 出力を別のツールにパイプしたい場面では、コマンドラインに切り替える。

10. Unicode、CJK、RTL:国際テキスト diff の注意点

トークナイザは Unicode の単語境界に基づき、三つのカテゴリで分割する。ワードの連続(\p{L} 文字と \p{N} 数字)、非ワードの句読点、空白だ。各カテゴリがそれぞれのトークンを生む。したがって hello, world!hello, world! の 5 トークンになる。

CJK コンテンツ(中国語、日本語、韓国語)では、表意文字や仮名がそれぞれ独立したトークンになる。中国語の文の中の 1 文字を変えると、その文字だけが行内ハイライトを帯び、行の残りは暗いままだ。段落レベルの構造は行ベースのままなので、改行を追加するような文の書き直しは、トークンレベルではなく行レベルの編集として現れる。

RTL 言語(アラビア語、ヘブライ語)では、diff は論理的な CSS 方向(ml-mr- ではなく ms-me-)を使う。RTL ロケールでは、ガターと行カラムが自然に反転する。各 diff セル内では文字方向がコンテンツに従うので、アラビア語の文字列は右から左に描画されつつ、+- のマーカーは開始側のガターにそろう。

行末文字の正規化は \r\n(Windows)、\n(Unix)、裸の \r(バージョン 9 までのクラシック Mac OS)を認識する。三つとも別々の行として分割されるので、あるプラットフォームから別のプラットフォームに変換されたファイルが、一つの巨大な行に潰れることはない。

11. FAQ

オンラインのテキスト diff はどう動くのか

テキスト diff は両方の入力を行に分割し、最長共通部分列アルゴリズム(典型的には Myers の O((N+M)D))を走らせて挿入と削除の最小集合を見つける。そして追加(緑)、削除(赤)、未変更(灰)の行をハイライトする。二度目のトークンレベル LCS が、各変更行内の変更されたワードを印付ける。テキスト比較・差分ツールは比較全体をブラウザ内のローカルで実行する。

text diff と JSON diff の違いは何か

テキスト diff は行ごとに比較する。散文、コード、ログ、契約書に向く。JSON 差分ツールは JSON のデータモデルを前提に動く。キーの順序は無関係、型は厳密(1"1")、配列はキーで対応付けられることもある。JSON をテキスト diff に貼り付けると、キーの並び替えや空白が変更として浮かび上がる。JSON Diff はそれを無視する。構造化されていないコンテンツにはテキスト diff、API レスポンスや設定には JSON Diff を使う。

1 ワードしか編集していないのに、なぜ diff は行全体を変更として表示するのか

そうではない。行がハイライトされているのは行の中で何かが変わったからだ。ハイライトの内側では変更されたトークンだけが明るい背景を帯びる(追加は緑、削除は赤の打ち消し線)。これが行内ワード diff だ。行のコンテキストは読みやすく保ちつつ、目は正確な編集箇所に着地する。ワードレベルのハイライトでは役に立たないほど行が変わっている場合、diff は削除 + 追加のペアに切り替えて構造を保つ。

空白、ケース、空行を diff で無視するには

「無視オプション」パネルをトグルする。ケースを無視は Aa を等しくする。すべての空白を無視はすべての空白・タブ・改行を畳む。git diff -w に相当する。行末スペース・タブを無視は git diff -b に対応し、CRLF ノイズを抑える。空行を無視は空行を落とすので、段落の再スペーシングが diff を誤アラインしなくなる。各オプションは独立しており、セッションをまたいで保持される。

Unified diff 形式とは何か

Unified diff は ---/+++/@@ -L,C +L,C @@ のテキスト形式だ。1980 年代後半に GNU diff が導入し、git、GitHub、GitLab、Unix の patch コマンドが使う。各ハンクは変更の周りに 3 行のコンテキストを示し、削除は -、追加は + で表す。unified 出力を PR コメントにコピーするか、git apply に貼り付けるか、patch -p1 < diff.patch を走らせる。きれいに適用される。

Myers と Patience、コードレビューにはどちらが向くのか

Myers は git diff と GNU diff のデフォルトだ。高速で数学的に最小だが、無関係な空行や閉じ括弧をアラインすることがあり、読みにくい diff になりやすい。Patience(Bram Cohen、2005)は各入力に厳密に 1 回現れる行にアンカーを置き、アンカー間に再帰する。関数の境界が崩れない。リファクタをレビューする際は git diff --patience(または同等の結果でわずかに高速な --histogram)を使う。

貼り付けたテキストはサーバーに送信されるのか

いいえ。テキスト比較・差分ツールのすべての比較はブラウザ内の JavaScript でローカルに実行される。テキストがアップロードされたり、ログに記録されたり、ディスクに保存されたり、第三者に送られたりすることはない。次回訪問時にページが記憶できるよう、UI の設定(ビューモードと無視オプションのトグル)だけが localStorage に保存される。テキストは保存しない。DevTools → Network で確認できる。Diff をクリックしてもリクエストは一切発生しない。

入力の大きさはどこまで許容されるか

実用的な制限は片側あたり約 5,000 行または 1 MB だ。合算 200 KB を超えるとライブ diff は無効になり、手動の Diff ボタンに切り替わる。5,000 行を超えると入力は警告とともに切り詰められる。数 MB のファイルに対しては、diff -u a.txt b.txtgit diff --no-index a.txt b.txtdelta に切り替えるとよい。これらはストリーミングで GB を扱える。