Патчи в ревью кода, отчётах об ошибках и рассылках почти всегда имеют формат unified diff. Его используют git diff, diff -u и «raw diff» на GitHub. Поначалу символов кажется много, но правила простые.
Общий вид
@@ -1,5 +1,6 @@ def greet(name):- print("Hello " + name)+ message = f"Hello, {name}!"+ print(message) def main(): greet("world")
Сверху вниз идут расширенные заголовки git, заголовки файла (---, +++) и блоки (hunk — фрагменты, начинающиеся с @@). В одном файле может быть несколько блоков, а в одном патче — несколько файлов.
Заголовки файла: --- и +++
--- 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 @@ означает: «показаны 5 строк файла до изменения начиная со строки 1 и 6 строк файла после изменения начиная со строки 1».
- Первое число — номер начальной строки, число после запятой — сколько строк занимает блок (включая контекстные).
- Если строк 1, число вместе с запятой опускают:
@@ -7 +7 @@— блок из одной строки 7. - Если строк 0, на этой стороне строк нет.
@@ -3,0 +4,2 @@означает, что после строки 3 оригинала вставлены две новые строки. При сравнении с пустым файлом появляется-0,0. - После второго
@@иногда идёт текст вроде имени функции (@@ -40,7 +40,8 @@ def main():). Это справка о том, внутри какой функции находится блок; при применении патча она игнорируется.
Посчитаем сами: контекстных строк (начинающихся с пробела) в примере выше четыре — def greet(name):, пустая строка, def main(): и greet("world"). До изменения — 4 контекстные строки плюс 1 строка -, итого 5; после — 4 контекстные плюс 2 строки +, итого 6. Отсюда заголовок -1,5 +1,6.
Три вида строк в теле
| Первый символ | Значение |
|---|---|
| Пробел | Контекстная строка, одинаковая в обоих файлах |
- | Строка есть только до изменения (удаление) |
+ | Строка есть только после изменения (добавление) |
По умолчанию контекст — 3 строки до и после изменения (можно изменить, например git diff -U5). Контекстные строки нужны, чтобы при применении патча найти правильное место. Если номера строк немного сдвинулись, но контекст совпадает, patch применит изменение со смещением (offset).
Исправление одной строки выглядит как пара: строка -, за ней строка +. В формате 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/path b/path — дополнительная информация, которую выводит только 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 old-path,rename to new-path— переименование (содержимое совпадает на 90%)Binary files a/logo.png and b/logo.png differ— для бинарных файлов вместо содержимого выводится только эта строка
Полезные приёмы для ревью
- Посмотрите сначала на разницу в числе строк в заголовке блока — она даёт представление о размере изменения.
-10,7 +10,30означает, что добавилось 23 строки. - Чтобы отсеять блоки, где изменились только пробелы, используйте
git diff -w. Особенно полезно, когда в коммите смешаны выравнивание отступов и реальные изменения (Опции игнорирования пробелов). - Чтобы увидеть, что изменилось внутри строки, используйте
git diff --word-diffили вставьте diff в инструмент с подсветкой внутри строк, как этот сайт. - Перед применением патча проверьте командой
git apply --check file.patch, что он применяется без ошибок. Вне git-репозитория используйтеpatch -p1 --dry-run < file.patch.
Попробуйте в этом инструменте
Объединённый вид в сравнении текстов показывает группу -, затем группу + в том же порядке, что и этот формат, и дополнительно подсвечивает изменённые слова внутри строк. Кнопка patch на панели результата копирует стандартный текст unified diff, а соседняя кнопка сохранения скачивает файл diff.patch. Patch, созданный при выключенных опциях игнорирования, можно применить к оригиналу через patch -p1 или git apply.