При работе со сторонними исходниками нередко необходимо вносить изменения в исходные тексты продукта для, например, его локализации, адаптации к системе или исправления зависимостей. В случае, когда разработчики не принимают изменения в основной код (или передача этих изменений должна быть ограничена некоторым кругом лиц и потому не может быть передана для добавления в основной код), можно локально изменить исходники, однако с обновлением основной версии необходимо будет переносить все изменения.
С появлением распределённых систем контроля версий, таких как GIT, роль патч-сетов стали играть отдельные ветки разработки, и коммиты в них; однако diff / patch (или git diff / patch) — всё ещё более гибкий инструмент, а явное хранение различий в .patch-файлах более наглядно.
11.1. Отслеживание изменений
Рассмотрим, как производится отслеживание изменений в файлах и описание патчей для их преобразования. С помощью потокового редактора sed сделаем три последовательных изменения файла. С помощью флага -i зададим постфикс для имени файла, в котором будет сохранена версия файла до изменения:
@user
[user@VM ~]$ mkdir calend-patches
[user@VM ~]$ cd calend-patches/
[user@VM calend-patches]$ cal -y > calend
[user@VM calend-patches]$ cat calend
2025
Январь Февраль Март
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2
6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
13 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16
20 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23
27 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30
31
Апрель Май Июнь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 4 1
7 8 9 10 11 12 13 5 6 7 8 9 10 11 2 3 4 5 6 7 8
14 15 16 17 18 19 20 12 13 14 15 16 17 18 9 10 11 12 13 14 15
21 22 23 24 25 26 27 19 20 21 22 23 24 25 16 17 18 19 20 21 22
28 29 30 26 27 28 29 30 31 23 24 25 26 27 28 29
30
Июль Август Сентябрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 1 2 3 4 5 6 7
7 8 9 10 11 12 13 4 5 6 7 8 9 10 8 9 10 11 12 13 14
14 15 16 17 18 19 20 11 12 13 14 15 16 17 15 16 17 18 19 20 21
21 22 23 24 25 26 27 18 19 20 21 22 23 24 22 23 24 25 26 27 28
28 29 30 31 25 26 27 28 29 30 31 29 30
Октябрь Ноябрь Декабрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2 3 4 5 6 7
6 7 8 9 10 11 12 3 4 5 6 7 8 9 8 9 10 11 12 13 14
13 14 15 16 17 18 19 10 11 12 13 14 15 16 15 16 17 18 19 20 21
20 21 22 23 24 25 26 17 18 19 20 21 22 23 22 23 24 25 26 27 28
27 28 29 30 31 24 25 26 27 28 29 30 29 30 31
[user@VM calend-patches]$ sed -i.old1 's/а/@/g' calend
[user@VM calend-patches]$ cat calend
2025
Янв@рь Февр@ль М@рт
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2
6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
13 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16
20 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23
27 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30
31
Апрель М@й Июнь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 4 1
7 8 9 10 11 12 13 5 6 7 8 9 10 11 2 3 4 5 6 7 8
14 15 16 17 18 19 20 12 13 14 15 16 17 18 9 10 11 12 13 14 15
21 22 23 24 25 26 27 19 20 21 22 23 24 25 16 17 18 19 20 21 22
28 29 30 26 27 28 29 30 31 23 24 25 26 27 28 29
30
Июль Август Сентябрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 1 2 3 4 5 6 7
7 8 9 10 11 12 13 4 5 6 7 8 9 10 8 9 10 11 12 13 14
14 15 16 17 18 19 20 11 12 13 14 15 16 17 15 16 17 18 19 20 21
21 22 23 24 25 26 27 18 19 20 21 22 23 24 22 23 24 25 26 27 28
28 29 30 31 25 26 27 28 29 30 31 29 30
Октябрь Ноябрь Дек@брь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2 3 4 5 6 7
6 7 8 9 10 11 12 3 4 5 6 7 8 9 8 9 10 11 12 13 14
13 14 15 16 17 18 19 10 11 12 13 14 15 16 15 16 17 18 19 20 21
20 21 22 23 24 25 26 17 18 19 20 21 22 23 22 23 24 25 26 27 28
27 28 29 30 31 24 25 26 27 28 29 30 29 30 31
[user@VM calend-patches]$ sed -i.old2 's/0/ /g' calend
[user@VM calend-patches]$ cat calend
2 25
Янв@рь Февр@ль М@рт
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2
6 7 8 9 1 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
13 14 15 16 17 18 19 1 11 12 13 14 15 16 1 11 12 13 14 15 16
2 21 22 23 24 25 26 17 18 19 2 21 22 23 17 18 19 2 21 22 23
27 28 29 3 31 24 25 26 27 28 24 25 26 27 28 29 3
31
Апрель М@й Июнь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 4 1
7 8 9 1 11 12 13 5 6 7 8 9 1 11 2 3 4 5 6 7 8
14 15 16 17 18 19 2 12 13 14 15 16 17 18 9 1 11 12 13 14 15
21 22 23 24 25 26 27 19 2 21 22 23 24 25 16 17 18 19 2 21 22
28 29 3 26 27 28 29 3 31 23 24 25 26 27 28 29
3
Июль Август Сентябрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 1 2 3 4 5 6 7
7 8 9 1 11 12 13 4 5 6 7 8 9 1 8 9 1 11 12 13 14
14 15 16 17 18 19 2 11 12 13 14 15 16 17 15 16 17 18 19 2 21
21 22 23 24 25 26 27 18 19 2 21 22 23 24 22 23 24 25 26 27 28
28 29 3 31 25 26 27 28 29 3 31 29 3
Октябрь Ноябрь Дек@брь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2 3 4 5 6 7
6 7 8 9 1 11 12 3 4 5 6 7 8 9 8 9 1 11 12 13 14
13 14 15 16 17 18 19 1 11 12 13 14 15 16 15 16 17 18 19 2 21
2 21 22 23 24 25 26 17 18 19 2 21 22 23 22 23 24 25 26 27 28
27 28 29 3 31 24 25 26 27 28 29 3 29 3 31
[user@VM calend-patches]$ sed -i.old3 's/@/ю/g' calend
[user@VM calend-patches]$ cat calend
2 25
Янвюрь Феврюль Мюрт
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2
6 7 8 9 1 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
13 14 15 16 17 18 19 1 11 12 13 14 15 16 1 11 12 13 14 15 16
2 21 22 23 24 25 26 17 18 19 2 21 22 23 17 18 19 2 21 22 23
27 28 29 3 31 24 25 26 27 28 24 25 26 27 28 29 3
31
Апрель Мюй Июнь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 4 1
7 8 9 1 11 12 13 5 6 7 8 9 1 11 2 3 4 5 6 7 8
14 15 16 17 18 19 2 12 13 14 15 16 17 18 9 1 11 12 13 14 15
21 22 23 24 25 26 27 19 2 21 22 23 24 25 16 17 18 19 2 21 22
28 29 3 26 27 28 29 3 31 23 24 25 26 27 28 29
3
Июль Август Сентябрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 1 2 3 4 5 6 7
7 8 9 1 11 12 13 4 5 6 7 8 9 1 8 9 1 11 12 13 14
14 15 16 17 18 19 2 11 12 13 14 15 16 17 15 16 17 18 19 2 21
21 22 23 24 25 26 27 18 19 2 21 22 23 24 22 23 24 25 26 27 28
28 29 3 31 25 26 27 28 29 3 31 29 3
Октябрь Ноябрь Декюбрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2 3 4 5 6 7
6 7 8 9 1 11 12 3 4 5 6 7 8 9 8 9 1 11 12 13 14
13 14 15 16 17 18 19 1 11 12 13 14 15 16 15 16 17 18 19 2 21
2 21 22 23 24 25 26 17 18 19 2 21 22 23 22 23 24 25 26 27 28
27 28 29 3 31 24 25 26 27 28 29 3 29 3 31
[user@VM calend-patches]$ ls
calend calend.old1 calend.old2 calend.old3
[user@VM calend-patches]$
Глазами найти изменения довольно трудно, поэтому воспользуемся утилитой
diff. При этом добавим флаг
-u, который оформляет найденные различия в виде единого контекстного блока с указанными изменениями (т. н. «unified diff»). Именно такой формат в подавляющем большинстве случаев применяется для описания патчей:
@user
[user@VM calend-patches]$ diff -u calend.old1 calend.old2 > p0.patch
[user@VM calend-patches]$ diff -u calend.old2 calend.old3 > p1.patch
[user@VM calend-patches]$ diff -u calend.old3 calend > p2.patch
[user@VM calend-patches]$ cat p0.patch
--- calend.old1 2025-07-31 15:22:20.207687635 +0300
+++ calend.old2 2025-07-31 15:33:38.576477837 +0300
@@ -1,6 +1,6 @@
2025
- Январь Февраль Март
+ Янв@рь Февр@ль М@рт
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2
6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
@@ -8,7 +8,7 @@
20 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23
27 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30
31
- Апрель Май Июнь
+ Апрель М@й Июнь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 4 1
7 8 9 10 11 12 13 5 6 7 8 9 10 11 2 3 4 5 6 7 8
@@ -24,7 +24,7 @@
21 22 23 24 25 26 27 18 19 20 21 22 23 24 22 23 24 25 26 27 28
28 29 30 31 25 26 27 28 29 30 31 29 30
- Октябрь Ноябрь Декабрь
+ Октябрь Ноябрь Дек@брь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2 3 4 5 6 7
6 7 8 9 10 11 12 3 4 5 6 7 8 9 8 9 10 11 12 13 14
[user@VM calend-patches]$
С помощью утилиты
patch можно применять изменения к файлам для их преобразования:
@user
[user@VM calend-patches]$ cp calend.old1 work-copy
[user@VM calend-patches]$ patch work-copy < p0.patch
patching file work-copy
[user@VM calend-patches]$ diff -u work-copy calend.old2
[user@VM calend-patches]$
Так как патчи накладываются на обновляющуюся версию исходников, возможна ситуация, когда контекст наложения патча изменяется. В зависимости от того, как сильно обновление исходников изменило контекст, рассчитывается некоторый уровень применимости патча, с какого-то уровня патч не применяется:
@user
[user@VM calend-patches]$ cp calend.old2 work-copy
[user@VM calend-patches]$ patch --verbose work-copy < p2.patch
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- calend.old3 2025-07-31 15:34:51.901347072 +0300
|+++ calend 2025-07-31 15:35:51.631240548 +0300
--------------------------
patching file work-copy
Using Plan A...
Hunk #1 succeeded at 1 with fuzz 2.
Hunk #2 succeeded at 8 with fuzz 2.
Hunk #3 succeeded at 24 with fuzz 2.
done
[user@VM calend-patches]$ cat work-copy
2025
Янвюрь Феврюль Мюрт
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2
6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
13 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16
20 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23
27 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30
31
Апрель Мюй Июнь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 4 1
7 8 9 10 11 12 13 5 6 7 8 9 10 11 2 3 4 5 6 7 8
14 15 16 17 18 19 20 12 13 14 15 16 17 18 9 10 11 12 13 14 15
21 22 23 24 25 26 27 19 20 21 22 23 24 25 16 17 18 19 20 21 22
28 29 30 26 27 28 29 30 31 23 24 25 26 27 28 29
30
Июль Август Сентябрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 1 2 3 4 5 6 7
7 8 9 10 11 12 13 4 5 6 7 8 9 10 8 9 10 11 12 13 14
14 15 16 17 18 19 20 11 12 13 14 15 16 17 15 16 17 18 19 20 21
21 22 23 24 25 26 27 18 19 20 21 22 23 24 22 23 24 25 26 27 28
28 29 30 31 25 26 27 28 29 30 31 29 30
Октябрь Ноябрь Декюбрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2 3 4 5 6 7
6 7 8 9 10 11 12 3 4 5 6 7 8 9 8 9 10 11 12 13 14
13 14 15 16 17 18 19 10 11 12 13 14 15 16 15 16 17 18 19 20 21
20 21 22 23 24 25 26 17 18 19 20 21 22 23 22 23 24 25 26 27 28
27 28 29 30 31 24 25 26 27 28 29 30 29 30 31
[user@VM calend-patches]$
@user
[user@VM calend-patches]$ cp calend.old2 work-copy
[user@VM calend-patches]$ sed -i "s/0/Q/g" work-copy
[user@VM calend-patches]$ patch --verbose work-copy < p1.patch
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- calend.old2 2025-07-31 15:33:38.576477837 +0300
|+++ calend.old3 2025-07-31 15:34:51.901347072 +0300
--------------------------
patching file work-copy
Using Plan A...
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file work-copy.rej
done
[user@VM calend-patches]$
В случае неприменимости каких-то изменений создаётся .rej-файл для описания неприменённых изменений; эти изменения необходимо вносить руками, после чего имеет смысл обновить исходный патч:
@user
[user@VM calend-patches]$ ls
calend calend.old1 calend.old2 calend.old3 p0.patch p1.patch p2.patch work-copy work-copy.orig work-copy.rej
[user@VM calend-patches]$ cat work-copy.rej
--- calend.old2 2025-07-31 15:33:38.576477837 +0300
+++ calend.old3 2025-07-31 15:34:51.901347072 +0300
@@ -1,34 +1,34 @@
- 2025
+ 2 25
Янв@рь Февр@ль М@рт
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2
- 6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
-13 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16
-20 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23
-27 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30
+ 6 7 8 9 1 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
+13 14 15 16 17 18 19 1 11 12 13 14 15 16 1 11 12 13 14 15 16
+2 21 22 23 24 25 26 17 18 19 2 21 22 23 17 18 19 2 21 22 23
+27 28 29 3 31 24 25 26 27 28 24 25 26 27 28 29 3
31
Апрель М@й Июнь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 4 1
- 7 8 9 10 11 12 13 5 6 7 8 9 10 11 2 3 4 5 6 7 8
-14 15 16 17 18 19 20 12 13 14 15 16 17 18 9 10 11 12 13 14 15
-21 22 23 24 25 26 27 19 20 21 22 23 24 25 16 17 18 19 20 21 22
-28 29 30 26 27 28 29 30 31 23 24 25 26 27 28 29
- 30
+ 7 8 9 1 11 12 13 5 6 7 8 9 1 11 2 3 4 5 6 7 8
+14 15 16 17 18 19 2 12 13 14 15 16 17 18 9 1 11 12 13 14 15
+21 22 23 24 25 26 27 19 2 21 22 23 24 25 16 17 18 19 2 21 22
+28 29 3 26 27 28 29 3 31 23 24 25 26 27 28 29
+ 3
Июль Август Сентябрь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 1 2 3 1 2 3 4 5 6 7
- 7 8 9 10 11 12 13 4 5 6 7 8 9 10 8 9 10 11 12 13 14
-14 15 16 17 18 19 20 11 12 13 14 15 16 17 15 16 17 18 19 20 21
-21 22 23 24 25 26 27 18 19 20 21 22 23 24 22 23 24 25 26 27 28
-28 29 30 31 25 26 27 28 29 30 31 29 30
+ 7 8 9 1 11 12 13 4 5 6 7 8 9 1 8 9 1 11 12 13 14
+14 15 16 17 18 19 2 11 12 13 14 15 16 17 15 16 17 18 19 2 21
+21 22 23 24 25 26 27 18 19 2 21 22 23 24 22 23 24 25 26 27 28
+28 29 3 31 25 26 27 28 29 3 31 29 3
Октябрь Ноябрь Дек@брь
Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 1 2 1 2 3 4 5 6 7
- 6 7 8 9 10 11 12 3 4 5 6 7 8 9 8 9 10 11 12 13 14
-13 14 15 16 17 18 19 10 11 12 13 14 15 16 15 16 17 18 19 20 21
-20 21 22 23 24 25 26 17 18 19 20 21 22 23 22 23 24 25 26 27 28
-27 28 29 30 31 24 25 26 27 28 29 30 29 30 31
+ 6 7 8 9 1 11 12 3 4 5 6 7 8 9 8 9 1 11 12 13 14
+13 14 15 16 17 18 19 1 11 12 13 14 15 16 15 16 17 18 19 2 21
+2 21 22 23 24 25 26 17 18 19 2 21 22 23 22 23 24 25 26 27 28
+27 28 29 3 31 24 25 26 27 28 29 3 29 3 31
[user@VM calend-patches]$ diff work-copy.rej p1.patch
[user@VM calend-patches]$