コードレビューや不具合報告、メールでやり取りされるパッチの多くは unified diff 形式です。git diff、diff -u、GitHub の「raw diff」はどれもこの形式を使います。最初は記号が多く見えますが、ルールは単純です。
全体の形
@@ -1,5 +1,6 @@ def greet(name):- print("Hello " + name)+ message = f"Hello, {name}!"+ print(message) def main(): greet("world")
上から順に git 拡張ヘッダー、ファイルヘッダー(---、+++)、ハンク(@@ で始まるかたまり)が続きます。1つのファイルに複数のハンクがあることも、1つのパッチに複数のファイルが含まれることもあります。
ファイルヘッダー:--- と +++
--- a/greet.pyは変更前のファイル、+++ b/greet.pyは変更後のファイルです。- git はパスの前に
a/、b/を付けます。そのためpatchコマンドで適用するときは-p1で最初のパス要素を取り除きます。 - 新しく作成したファイルは
--- /dev/null、削除したファイルは+++ /dev/nullと表示されます。 - GNU diff はファイル名の後ろに更新日時を付け加えることもあります。
ハンクヘッダー @@ -a,b +c,d @@
@@ -1,5 +1,6 @@ は、「変更前のファイルの1行目から5行、変更後のファイルの1行目から6行」を表示しているという意味です。
- 最初の数字は開始行番号、カンマの後ろはそのハンクが占める行数(文脈行を含む)です。
- 行数が1の場合はカンマごと省略されます。
@@ -7 +7 @@は7行目の1行だけのハンクです。 - 行数が0の場合は、その側に行がないという意味です。
@@ -3,0 +4,2 @@は、元のファイルの3行目の後に2行が新しく入ったことを表します。空のファイルと比較すると-0,0になります。 - 2つ目の
@@の後ろに関数名のような文字列が付くこともあります(@@ -40,7 +40,8 @@ def main():)。ハンクがどの関数の中にあるかを示す参考情報で、適用時には無視されます。
実際に数えてみると、上の例の文脈行(空白で始まる行)は def greet(name):、空行、def main():、greet("world") の4つです。変更前の行数は文脈4行に - の1行を足して5、変更後の行数は文脈4行に + の2行を足して6なので、ヘッダーは -1,5 +1,6 になります。
本文の3種類の行
| 先頭の文字 | 意味 |
|---|---|
| 空白 | 両側に同じようにある文脈行 |
- | 変更前にだけある行(削除) |
+ | 変更後にだけある行(追加) |
既定の文脈は変更の前後3行です(git diff -U5 のように変更できます)。文脈行は、パッチを適用するときに正しい位置を見つけるために使われます。行番号が少しずれていても、文脈が合っていれば patch は位置をずらして(offset)適用します。
1行を書き換えた場合は、- 行のすぐ後に + 行が続く組として表れます。unified 形式には「変更」を表す専用の記号がないため、連続した - のかたまりと + のかたまりを上下で突き合わせて読むのがコツです。
\ No newline at end of file
@@ -1,2 +1,2 @@ host=example.com-port=8080\ No newline at end of file+port=8080
すぐ上の行がファイルの最終行で、末尾に改行文字がないことを示す表示です。上の例では内容は同じで、変更後のファイルにだけ最後の改行が追加されました。詳しくは 改行コードと見えない差分を参照してください。
git 拡張ヘッダー
diff --git a/パス b/パス に続く行は、git 独自の追加情報です。
index 3b18e51..a6f4c2d 100644— 変更前後のファイル内容のハッシュ(先頭部分)とファイルモード(100644 は通常ファイル、100755 は実行ファイル)new file mode 100644/deleted file mode 100644— ファイルの作成・削除old mode 100644/new mode 100755— 実行権限だけが変わった(内容のハンクなしで現れることがある)similarity index 90%、rename from 旧パス、rename to 新パス— 名前の変更(内容が90%同じという意味)Binary files a/logo.png and b/logo.png differ— バイナリファイルは内容の代わりにこの1行だけ
レビューで役立つコツ
- まずハンクヘッダーの行数の差を見ると、変更の規模の見当がつきます。
-10,7 +10,30なら23行増えています。 - 空白だけが変わったハンクを除外したいときは
git diff -wを使います。インデントの整理と実際の変更が混ざったコミットを見るときに特に便利です(空白無視オプション)。 - 行の中で何が変わったかを見たいときは
git diff --word-diffを使うか、このサイトのように行内ハイライトに対応したツールに貼り付けます。 - パッチを適用する前に、
git apply --check ファイル.patchできれいに適用できるかを確認します。git リポジトリでない場合はpatch -p1 --dry-run < ファイル.patchを使います。
このツールで試す
テキスト比較ツールの統合表示は、この形式と同じ順序で - のかたまりの後に + のかたまりを表示し、行内の変わった単語をもう一度強調します。結果バーの patch ボタンを押すと標準的な unified diff テキストがコピーされ、隣の保存ボタンで diff.patch ファイルとしてダウンロードできます。無視オプションをすべてオフにした状態で作った patch は、patch -p1 や git apply で元のファイルに適用できます。