コードレビューで何百行もの git diff を前にして「どこを見ればいいんだ……」と固まったこと、ない? あるいは「設定ファイルを書き換えたのに反映されない」と言われて本番とステージングを見比べたら、行末の改行コードだけが違っていた、みたいな徒労。diff(ディフ) はテキストの違いを可視化する道具で、Unix の diff コマンドから Git、GitHub の PR 画面、VS Code のエディタまで、開発者の日常に深く食い込んでいます。でも「記号の意味はなんとなく知ってる」止まりの人は意外と多い。本記事では unified 形式の読み方、side-by-side との使い分け、Myers アルゴリズムの内側、ホワイトスペースの罠、レビューで差分を読むコツ、そしてマージコンフリクトの解き方まで、実務で詰まるポイントを一気に整理します。読み終わるころには「この @@ -10,4 +10,5 @@ ってなんだっけ?」と検索し直さずに済むようになっているはずです。
diff コマンドが世に出たのは 1974 年、生まれた場所は AT&T ベル研究所です。実装したのは Unix の設計者のひとり Douglas McIlroy と、当時大学院生だった James W. Hunt。ふたりは 1976 年に論文「An Algorithm for Differential File Comparison」を発表し、現在「Hunt–McIlroy アルゴリズム」として知られる最長共通部分列(LCS)ベースの差分検出手法を世に出しました。このコマンドは Unix の基本ツール群に組み込まれ、やがて 1984 年に Larry Wall がそれを入力として受け取る patch コマンドを書き上げたことで「差分を送って当てる」文化が定着します。メールで diff -u の出力を流し合ってソフトを育てる時代を経て、その DNA は現在の Git にまで受け継がれました。半世紀前のコマンドが今もまったく同じ形式で読まれている、というのは地味にすごい話です。
diff とは何か
diff は「2 つのテキストの違いを、どこがどう変わったかという形で可視化する仕組み」です。入力は 2 つのファイル(あるいは 2 つの文字列)で、出力は「片方を相手に書き換えるための手順書」。この手順書は人間が読むためのものでもあり、同時にコンピュータに「当てて」元ファイルを書き換えさせるための命令としても機能します。この二面性が diff を特別な存在にしています。
記号の意味はおおむね 3 種類だけ覚えれば十分です。行頭の - は「古い側にあったが新しい側では消えた行」、+ は「新しい側で追加された行」、スペース(あるいは何もなし)は「両方に存在する文脈行」。変更は「同じ位置の - と + のペア」として表現されます。diff 自身は「削除」と「追加」しか区別せず、「変更」という概念はありません。これは愚直ですが、フォーマットの単純さと普遍性を生んでいる設計判断です。
人間が diff を読む場面は、たとえば PR のレビュー、本番障害時の「最後にデプロイした差分はなんだ?」、あるいは設定ファイルの「昨日までは動いていたのに」調査などです。一方でマシンが diff を読む場面もあって、その代表は patch コマンドや Git の git apply。パッチファイル(.patch や .diff)をやり取りしてソフトを更新する文化は、メールベースの Linux カーネル開発で今も現役です。同じ 1 本のテキストが「人間向けのサマリ」と「機械向けの命令」を兼ねている、というのが diff 形式の魅力です。
ちなみに diff の出力には歴史的にいくつか形式があり、normal(古典形式)・context(文脈付き)・unified(統合形式)の 3 つが主要です。現在の Git やほとんどのレビューツールは unified 形式を採用しているので、本記事でも基本は unified に絞って解説します。
unified 形式の読み方
unified 形式は 1990 年ごろに Wayne Davison が GNU diff に実装し、その後 patch コマンドにも取り込まれた形式です。文脈行を - 側と + 側で共有することで、context 形式より縦方向のサイズが約半分になります。読みやすさと機械可読性のバランスが良く、今では diff といえばこれを指すと言っていい。
最小限のサンプルを見てみましょう。diff -u old.txt new.txt の出力はだいたいこんな形になります。
--- old.txt 2026-04-15 10:00:00
+++ new.txt 2026-04-15 10:05:00
@@ -10,4 +10,5 @@ def greet(name):
if not name:
return "hello, stranger"
- return "hello, " + name
+ if name == "admin":
+ return "hello, boss"
+ return f"hello, {name}"1 行目の --- と 2 行目の +++ はファイルヘッダで、それぞれ「旧側」「新側」を表します。3 行目の @@ -10,4 +10,5 @@ は「ハンクヘッダ」と呼ばれる情報で、「旧ファイルの 10 行目から 4 行を、新ファイルの 10 行目から 5 行に置き換える」という意味です。一般形は @@ -旧開始,旧行数 +新開始,新行数 @@。後ろに続く def greet(name): は「この変更が含まれている関数の頭」で、これは Git の xfuncname ヒントが自動で拾ってくれる親切情報。これがあると長いファイルでも「どこの変更なのか」が一目でわかります。
本文の各行は行頭記号で種類を区別します。
| 記号 | 意味 | 補足 |
|---|---|---|
--- | 旧ファイルのヘッダ | 1 行目、必ず新ファイルの +++ と対 |
+++ | 新ファイルのヘッダ | 2 行目、続けてハンクが並ぶ |
@@ | ハンクヘッダ | 変更箇所の開始位置と行数を示す |
- | 旧側にのみ存在した行 | 「削除された行」として読む |
+ | 新側にのみ存在する行 | 「追加された行」として読む |
| (スペース) | 文脈行 | 前後の位置関係を示すため両側に残している |
「ハンク」(hunk)は「変更のかたまり」の単位で、1 ファイルの中に複数のハンクが並ぶことがあります。デフォルトで各ハンクの前後には 3 行の文脈行が付きますが、これは -U オプションで増減できます。たとえば diff -U 0 なら変更行のみ、diff -U 10 なら前後 10 行ずつ出力。レビューで「もう少し周りが見たい」ときは git diff -U10 がおすすめです。
-u オプションが当たり前になったのは diff の歴史の中では比較的新しい話で、それ以前は context 形式を使うのが一般的でした。unified が普及したおかげで「メールに貼っても読みやすいし、patch にそのまま食わせれば当たる」形式が定着し、今の Git の diff 表示もこの系譜です。半世紀近くほぼそのまま使われているフォーマットはそう多くないよね。
side-by-side 表示のメリット
unified 形式は縦に詰まっていて読みやすい一方、1 行が長いコード(たとえば CSS のルール 1 つや SQL の長いクエリ)では弱点があります。- 行と + 行を上下で比べても、変わったのが行頭なのか末尾なのか中央なのか、目で追いにくい。そんなときの味方が side-by-side(左右並べ)表示です。
GNU diff にも diff -y というオプションがあり、2 つのファイルを左右に並べて中央に差分マーカーを出します。たとえば次のような見た目になります。
def greet(name): def greet(name):
if not name: if not name:
return "hello, stranger" return "hello, stranger"
return "hello, " + name | if name == "admin":
> return "hello, boss"
> return f"hello, {name}"中央の | は「両側で内容が違う行」、< は「左側にだけある行」、> は「右側にだけある行」。横幅が足りるならこちらのほうが直感的に把握できます。ターミナルの横幅が狭いと折り返しで地獄になるので、実用するならワイド画面か、エディタの機能に寄せたいところ。
実際のところ、side-by-side の真価を発揮するのはターミナルよりもグラフィカルな差分ビューアです。VS Code の「Compare With」は 2 ファイルを並べて差分を表示しますし、GitHub の PR 画面も「Split」ビューを切り替えると split(左右)と unified(上下)を切り替えられます。JetBrains 系 IDE の「Compare Files」も同様で、しかもキャレットを動かすと左右が同期スクロールしてくれる気の利き方。リファクタリングで大きくブロックが動いた PR を読むときは、unified よりこちらのほうが頭に入りやすい。
選び方の目安としては、行単位で細かい修正が散らばっているときは unified、ブロック単位で大きく動いた変更やリファクタのレビューは side-by-side、と覚えておくと迷いません。片方だけで戦わず、レビューツールの切り替えボタンを積極的に使いましょう。
Myers アルゴリズム
「2 つのファイルの差分を計算する」というのは簡単に聞こえますが、真面目にやると立派なアルゴリズムの問題です。diff の世界で現代のデファクトになっているのが、1986 年に Eugene W. Myers が論文「An O(ND) Difference Algorithm and Its Variations」で示した Myers アルゴリズムです。GNU diff も Git も、基本的にはこの系統を使っています。
Myers アルゴリズムのゴールは「2 つの系列を互いに変換するのに必要な編集操作(挿入・削除)の最小数」を求めること。これは最長共通部分列(LCS)問題と双対で、共通部分を最大化すれば編集距離は最小化されます。計算量は O((N+M)D) で、ここで N と M は 2 つの入力行数、D は最終的な編集距離(差分の大きさ)です。差分が小さいほど高速、という性質があるため、現実のコード差分(大半はファイル全体のうちほんの一部の変更)と相性が抜群。
ポイントは「行単位の LCS」である点です。diff は単語や文字ではなく行を最小単位として扱うため、2 つの異なる行を「似ている」と判定することはできません。1 文字だけ違う 2 行は「旧 1 行を削除、新 1 行を追加」として表示されます。これは speed と単純さのトレードオフで、より細かい変化を見たいときは --word-diff などのバリアントを後段で組み合わせて対応します。
Git には Myers のほかに Patience diff と Histogram diff という選択肢もあります。Patience は Bram Cohen(BitTorrent の人)考案で、「ユニークに出現する行」を最初に対応付けてから再帰的に間を埋めるアプローチ。大きな関数の挿入や並び替えで、Myers が変な位置に - / + を置いてしまうケースを改善します。Histogram diff は Patience の変種で、Git のデフォルトとして Myers を置き換える動きも進んでいます。設定は次のように切り替えられます。
# デフォルトの diff アルゴリズムを切り替え
git config --global diff.algorithm histogram
# 一時的に patience を使う
git diff --diff-algorithm=patience HEAD~1普段は気にしなくていい設定ですが、「リファクタの diff が読みにくい」と感じたらアルゴリズム切り替えを試してみる価値があります。アルゴリズム自体の出力は少し変わるだけですが、レビュー負荷がグッと下がることがある。
ホワイトスペース・改行コード
diff で一番地味にハマるのがホワイトスペースです。タブとスペースの混在、行末のトレイリングスペース、CRLF と LF の違い、ファイル末尾の改行有無。どれも「見た目は同じ」に見える変更なのに、diff は律儀に違いとして出してきます。
GNU diff や git diff には、これらを無視するためのオプションが用意されています。よく使うのは次のあたり。
| オプション | 意味 | ユースケース |
|---|---|---|
-w / --ignore-all-space | すべての空白を無視 | インデント方式を丸ごと変えた PR |
-b / --ignore-space-change | 連続空白の数違いを無視 | 空白の数だけ直った変更を除外 |
--ignore-blank-lines | 空行の増減を無視 | 整形ツールで空行が入れ替わった |
--stat | ファイルごとの +/- 数をサマリ表示 | PR 全体のボリューム感を把握 |
--word-diff | 行ではなく単語単位で色づけ | 文章の細かい編集 |
-U <n> | 文脈行の数を n にする | 周辺コードを多めに見たい |
-M | リネームを検出して「rename」表示 | ファイル移動を追いたい |
実例として、Python コードでインデントをタブからスペース 4 に変えた PR を想像してください。見た目は同じまま全行が「変わった」ことになり、レビュー負荷が爆発します。こういうときは次のコマンドで空白を無視した diff を見ます。
diff -w old.py new.py
git diff -w HEAD~1空白以外の変更だけが残るので、本質的な修正がないかどうかを短時間で確認できます。PR 上でも GitHub なら URL に ?w=1 を付けると「Hide whitespace changes」モードに切り替わります。レビューでありがたい小ワザ。
Windows で編集したファイルと macOS / Linux で編集したファイルでは改行コードが違います(CRLF と LF)。気づかずにコミットすると、diff がファイル全体を「変更された」と判定してしまう地獄が生まれがち。Git なら core.autocrlf の設定、リポジトリ共通なら .gitattributes に * text=auto eol=lf を書いておくと、チーム全体で改行コードを統一できます。チーム開発を始めた初日に仕込んでおくと将来の自分が泣かずに済みます。
もうひとつ注意しておきたいのが「ファイル末尾の改行」問題です。POSIX の規約ではテキストファイルの末尾は必ず改行で終わるべきとされており、改行がないと diff は \ No newline at end of file という警告行を出します。これが PR の最後に出てきて「えっなにこれ?」と驚かれがちですが、意味は「ファイル末尾の改行が抜けている」だけで、怖い現象ではありません。エディタの設定で「ファイル保存時に末尾改行を付ける」(VS Code なら files.insertFinalNewline)を ON にしておけば自動で消えます。
用途別の使いどころ
diff は「コードレビューで使うもの」というイメージが強いですが、実際はもっと広い場面で役立ちます。ここでは代表的な 5 つの用途を見ていきます。
1. コードレビュー。言うまでもなく最頻出。PR に対して「何が」「なぜ」変わったかを把握するために使います。GitHub・GitLab・Gitea などはどれも unified と split の両方を表示できるので、状況に応じて切り替えましょう。大きな PR ほど unified のほうが圧倒的で、split は巨大な CSS や SQL の変更に向きます。
2. 設定ファイルの差分確認。本番とステージングの nginx.conf が食い違っていて動作が違う、という調査で活躍します。diff -u prod.conf staging.conf でどこが違うかを即座に把握できます。インフラ屋さんなら Ansible の dry run(--check --diff)でも同じ unified 形式が出力されるので、読めると強い。
3. 翻訳原稿の差分。翻訳では原文と訳文を並べるだけでなく、訳文の改訂履歴を diff で追うことがあります。段落単位だと変更が大きすぎて読みにくいので、--word-diff と合わせ技が便利。後述します。
4. ドキュメント校正。Markdown の文章を書き換えたあと、「何が本質的に変わったか」を可視化するのに使います。Pull Request の承認プロセスを取り入れているドキュメントリポジトリでは diff がそのまま編集履歴になります。
5. ログの比較。同じコマンドを 2 回実行したログを取って、どこが違うかを見る使い方。たとえばベンチマーク結果や、失敗するテストだけログを分けて diff にかけると「タイミング依存で出てくるエラーだけ」が浮き上がります。ノイズになりがちなタイムスタンプは事前に sed で削るなど、下処理を入れるとさらに効きます。
コードレビューで差分を読むコツ
レビューは「差分を読む」行為の頂点みたいな仕事です。実のある指摘を返すには、差分そのものの読み方にもコツがあります。以下に 6 つの実践をまとめました。どれも小さな工夫ですが、レビュー体験をぐっと軽くしてくれます。
1. 小さい PR を好む。200 行を超える PR は合成エラーみたいな指摘しか返せなくなります。レビュアーとしては「いくつかにばらしてもらえますか?」の一言を恐れないこと。レビュイーとしては機能単位・リファクタ単位でコミットを分けておくと、レビュアーが commit ごとに追えます。
2. 先に「意図」を読む。PR の description とイシュー本文を読んでから diff を開きます。「何を達成したいか」を知ったうえで差分を見ると、「なぜここを触ったのか」に納得がいき、逆に「触らなかった箇所」に対しても疑問を持てます。
3. 追加と削除を先に俯瞰する。--stat オプションや GitHub の「Files changed」タブの上部で、ファイルごとの + / - 数をまず見渡します。どのファイルが重心かを把握してから、具体的な行の読み込みに入ると、重要度の順番を外しません。
4. リファクタと機能追加は分けて読む。コミットが分かれていれば commit ごとに読み、混ざっていれば「リファクタ部分」と「機能追加部分」を脳内で分離しながら読みます。混ぜて読むと脳がオーバーフローして、両方に雑なコメントを付けがちです。
5. コメントより先に理解。すぐに「ここの変数名が〜」みたいな指摘に飛びつかず、まず全体を通して読む。そのほうが大きな設計ミスを見逃さないし、指摘の粒度も整います。レビューコメントは「読んだ後」で書くのが原則です。
6. 大きい diff はコミット単位で読む。どうしても巨大な PR を渡されたときは、git log origin/main..HEAD --oneline でコミット一覧を出し、git show <hash> で 1 件ずつ読みます。ひとつのコミットを読みきるまで次に行かない、というルールで進めると、途中で迷子になりません。
差分を読む前に次の 5 項目を自問すると、指摘の質が安定します。(1) この PR のゴールは何か/(2) テストは変更と対応しているか/(3) ユーザー影響(挙動・性能・互換性)があるか/(4) ロールバックは簡単か/(5) レビューをここで止めるべきほどの大きな懸念はあるか。5 番目が「ある」なら、指摘コメントを書く前に PR 全体の方針について対面・チャットで会話したほうが早いことが多いです。
マージコンフリクト解消
マージコンフリクトはチーム開発の必要悪です。2 人が同じ行を別の方向に書き換えたとき、Git はどちらを採用すべきか判断できずに、ファイルの中に次のような目印を残します。
def greet(name):
if not name:
return "hello, stranger"
<<<<<<< HEAD
return f"Hello, {name}!"
=======
return "hi, " + name
>>>>>>> feature/casual-greet<<<<<<< HEAD から ======= までが現在のブランチ(HEAD)側、======= から >>>>>>> feature/casual-greet までがマージしようとしているブランチ側です。解決するにはマーカーごと消して、どちらを残すか・どう混ぜるかを人間が決めて書き換えます。消し忘れるとコードが構文エラーになるので、エディタの検索で <<<<<< が残っていないかを必ず確認しましょう。
デフォルトの 2-way マーカーよりも情報量が多いのが diff3 スタイルです。次の設定を入れると、共通の祖先(base)も一緒に表示されます。
git config --global merge.conflictStyle diff3これを入れると先ほどのマーカーが次のようになります。
<<<<<<< HEAD
return f"Hello, {name}!"
||||||| merged common ancestors
return "hello, " + name
=======
return "hi, " + name
>>>>>>> feature/casual-greet真ん中の ||||||| merged common ancestors が「両者が分岐する前の共通ベース」。これがあると「どちらが何を変えたか」を正確に再現できるので、意図を取り違えずに解決できます。コンフリクトを頻繁に扱うチームほど、diff3 を ON にしておくメリットが大きい。
どうしても片方を丸ごと採用したいときは、git checkout --ours <file> と git checkout --theirs <file> が使えます。ours は自分側、theirs は相手側。ただしこれは最終兵器で、安易に使うと相手の変更を無言で捨ててしまうので慎重に。複数ファイルを自動解決したいなら git mergetool で好みの GUI ツール(VS Code、Beyond Compare、Meld、KDiff3 など)を開き、左(base)・中央(result)・右(incoming)を見ながら解決するのが王道です。
もうひとつ押さえておきたいのは、そもそもコンフリクトを起こしにくくするコーディングスタイルです。1 ファイルを巨大にしない、関数を細かく切る、レビューを早く回す、ブランチを短命に保つ、という習慣はすべてコンフリクト削減に寄与します。「直すより起こさないほうが簡単」なので、ここに投資する価値は高い。
単語単位 / 文字単位 diff
Myers アルゴリズムは行単位で動くため、1 行内の小さな変更はすべて「行ごと入れ替え」として表示されます。コードなら気にならないことも多いですが、散文や翻訳ではもっと細かい粒度で見たいケースがあります。そんなときのために Git は --word-diff というオプションを持っています。
git diff --word-diff=color HEAD~1これを付けると、変更された単語だけが色付きで強調され、それ以外はそのまま表示されます。--word-diff=plain なら色なしで {-old-}{+new+} の形で出力され、パッチファイルに貼っても読めます。--word-diff-regex で単語の切り出しルールを正規表現で指定できるので、プログラミング言語の識別子単位や、空白を含む区切りにも対応できます。
問題は日本語です。英語と違って日本語には単語境界の空白がないため、デフォルトの単語区切りだと「行まるごと」扱いになってしまいます。対策はいくつかあって、(1) 正規表現で文字単位にする(--word-diff-regex=.)、(2) MeCab や kuromoji のような形態素解析器で事前に分かち書きしてから diff を取る、(3) 専用の文字単位 diff ツール(たとえば diff-highlight や wdiff -w)を使う、といったアプローチがあります。
翻訳原稿のように「ここの助詞だけ直した」「語尾を丁寧にした」みたいな細かい変更を追いたいときは、文字単位 diff が圧倒的に便利です。校正履歴をすべて保存しておき、任意の 2 つのリビジョンに対して文字単位 diff をかけると、改訂の傾向が見えてきます。「この訳者は語尾を柔らかくする癖があるな」とか。
なお文字単位 diff は計算量が大きくなりがちで、長大なファイルで使うとレスポンスが落ちることがあります。実務的にはセクション単位に区切ってから適用するのがコツ。
JSON / YAML diff の落とし穴
JSON や YAML を diff で比べると、意味的には同じなのに「見た目の違い」が大量に出てきて悲しくなるケースがあります。代表的なのがキーの並び順。
// A.json
{"name": "Alice", "age": 30}
// B.json
{"age": 30, "name": "Alice"}テキストとしての diff は「全行違う」扱いですが、JSON オブジェクトとしては完全に同一。この落とし穴はよくあって、特に API レスポンスを保存したスナップショットテストで「日によって順序が違う」現象を引き起こします。
対処は 2 方向あります。ひとつは「フォーマッター&ソート」で正規化してから diff を取る方法。次のような短いパイプラインで十分実用になります。
jq -S . A.json > a.sorted.json
jq -S . B.json > b.sorted.json
diff -u a.sorted.json b.sorted.jsonjq -S はキーをアルファベット順にソートしつつインテグリティを保ったまま整形出力してくれるオプションで、「順序の違い」を一発で消せます。YAML なら yq -S、dasel、あるいは yamlfmt でも似たことができます。CI のスナップショットテストでは「書き出しの直前に必ず jq -S で整形する」ルールを入れておくと、以降のテスト安定度が段違いです。
もうひとつの方向は「意味的 diff」を取るツールを導入する方法です。jd(JSON diff)や dyff(YAML 向け)、json-diff などは、キーの順序違いを同一扱いしたうえで、意味的な変更だけを抽出してくれます。dyff は YAML のコメントやアンカーも考慮してくれるので、Kubernetes マニフェストのレビューに便利。
配列の扱いにはもうひとつ罠があります。JSON 配列は順序付きコレクションなので、要素の順序が変わると「全要素が置き換わった」扱いになります。「順不同リスト」として扱いたいときは、事前に jq 'sort_by(.id)' のようにキーを指定してソートしてから diff を取ると安定します。リスト要素にユニークな ID がないと同一性判定が難しいので、API 設計の段階で ID を用意しておくのが理想です。
ベンリーの diff ツール使い方
コマンドラインをいちいち開かずに、ブラウザ上でサクッとテキスト差分を取りたいときに使えるのが、ベンリーの テキスト差分ツールです。処理はすべてブラウザ内で完結し、入力内容がサーバーに送信されることはありません。業務のログや設定ファイルを貼っても安心です。
主な機能は次のとおり。
- 2 ペイン入力: 左右のテキストエリアに比較したい内容を貼り付けるだけで、その場で差分が計算されて表示されます。コピペ中心のワークフローにフィットします。
- unified / side-by-side 切り替え: 一覧性重視の unified と、左右で見比べる split を、好みに応じて切り替えられます。PR のレビュー感覚で読めます。
- 空白・改行コードの無視: トグルひとつでホワイトスペースや CRLF/LF の違いを無視できます。「見た目は同じなのに違う扱いされてる」ときの救済装置です。
- 単語単位ハイライト: 行単位だけでなく、変更された部分を単語レベルで強調表示するモードも用意しています。文章比較でも読みやすい。
- 差分サマリ: 追加行数・削除行数・変更行数を上部に表示するので、まずはボリューム感を把握してから中身を読めます。
ちょっとしたコツとして、JSON や YAML を貼り付ける前にベンリーの JSON フォーマッターでキー順ソート込みの整形を挟んでおくと、意味的に同じなのに並び順だけ違うケースで余計な差分が消えて見通しが良くなります。ログを比べるときは、事前にタイムスタンプや行番号をエディタで取り除いてから貼ると、本質的な違いだけがくっきり浮かび上がる。ちょっとした下処理が diff の読みやすさを大きく変えます。
よくある質問
diff がやたら長くなる理由は?
よくある原因は (1) インデントの統一・自動フォーマッターの適用で全行が触られた、(2) CRLF と LF が混ざって改行コードごと変更扱いになった、(3) 末尾の改行がない/ある、(4) エディタが勝手に BOM を付けた、あたりです。まずは git diff -w で空白を無視した diff を見てみて、それでも大量に残るなら .gitattributes と改行コードの設定を疑いましょう。チームで一度だけ「自動整形済みの大規模コミット」を作り、以降は .git-blame-ignore-revs にそのコミットハッシュを登録しておくと、blame が荒れずに済みます。
空行だけの変更をレビューから除外したい
GNU diff / Git diff には --ignore-blank-lines(短縮 -B)というオプションがあり、空行の増減だけの変更を無視してくれます。git diff -B HEAD~1 のように使えば、改段落の調整だけの PR は見事に消えます。GitHub の PR 画面で同等のことをするなら、URL に ?w=1 を付けて「Hide whitespace changes」モードを使うと空行差分もかなり消えます(完全ではないですが実用十分)。コードレビューで「なんで空行差分が大量に?」と感じたらこのオプションを思い出してください。
日本語テキストを単語単位で diff したい
Git の --word-diff はデフォルトで英語の単語境界(空白区切り)を使うため、日本語だと 1 行まるごとの差分になってしまいます。対策は (1) --word-diff-regex=. を指定して文字単位にする、(2) MeCab や kuromoji で形態素解析して分かち書きしたテキストに対して diff を取る、(3) 文字単位の専用ツール(wdiff、diff-highlight、VS Code の比較モードなど)を使う、のどれかです。翻訳・校正の現場では (2) が圧倒的に読みやすく、(1) はサクッと試すとき向き。ベンリーの diff ツールは単語単位ハイライトで日本語にもそれなりに対応しているので、下書き比較くらいなら手軽です。
JSON / YAML のフィールド順序差分を無視したい
テキスト diff をかける前に正規化するのが一番確実です。JSON なら jq -S . input.json、YAML なら yq -S . input.yaml でキー順をソートしつつ整形できます。両方のファイルを同じフォーマッターに通してから diff -u を取れば、順序だけの違いは消えます。意味的に比較したいなら jd(JSON 差分)や dyff(YAML 差分)のような専用ツールもおすすめ。CI では「スナップショットを書き出すときに必ずソート済み整形を通す」ルールを決めておくと、そもそも順序ゆらぎが発生しません。予防が治療に勝る、という話です。
大文字・小文字の違いを無視したい
GNU diff には -i(--ignore-case)という直球のオプションがあります。diff -i A.txt B.txt とすれば Hello と hello を同一視してくれます。Git diff には直接同等のオプションがなく、こちらは git diff の出力を grep -i にかけるか、事前にファイルを tr '[:upper:]' '[:lower:]' で小文字化してから diff を取るのが現実的。なお SQL や定数名のように「大文字小文字の違いが意味を持つ」文脈ではこの無視は危険なので、使う前にドメインの事情を必ず確認しましょう。
テキスト差分をブラウザでサクッと
ベンリーのテキスト差分ツールは、2 つの入力を貼り付けるだけで unified / split 両対応、空白無視や単語単位ハイライトまでそろったブラウザ完結型。ログ・設定ファイル・文章の比較にどうぞ。
テキスト差分ツールを開く →