Патчи в Git - git format-patch для обмена изменениями

17 декабря 2025
Автор

Олег Марков

Введение

Патчи в Git часто воспринимаются как что‑то старомодное по сравнению с pull request и code review на платформах вроде GitHub или GitLab. Но на практике git format-patch остается одним из самых мощных и точных инструментов для обмена изменениями, особенно когда вы:

  • отправляете изменения по почте,
  • работаете с проектами с жесткими требованиями к истории коммитов,
  • хотите заранее увидеть, как ваши коммиты будут выглядеть при интеграции,
  • переносите изменения между разными репозиториями без добавления удаленных (remote).

Смотрите, в этой статье я покажу вам, как шаг за шагом использовать git format-patch, чтобы:

  • создавать патчи из одного или нескольких коммитов,
  • настраивать их вид (автор, тема письма, префиксы),
  • отправлять патчи по почте (в связке с git send-email),
  • применять патчи (git am) и разбираться с конфликтами,
  • использовать патчи для code review и переноса изменений.

Мы будем двигаться от простого к более продвинутому, с примерами команд и пояснениями, что именно делает каждая опция.

Что такое патч в контексте Git

Формат патча

Патч — это текстовое представление изменения. Оно включает:

  • метаданные коммита (автор, дата, тема),
  • краткое и подробное описание,
  • список измененных файлов,
  • строки, которые были добавлены и удалены.

Git использует расширенный формат патча, который:

  • близок к обычному diff -u, но
  • содержит дополнительную информацию (имя автора, дату, SHA коммита, тему, подпись Signed-off-by и т.д.).

Это позволяет:

  • восстановить коммит почти в точности таким, как он был,
  • переносить патчи между разными репозиториями и ветками,
  • делать code review, просто читая письмо/файл.

Ниже я помещу пример фрагмента патча, чтобы вы увидели структуру:

From 3a7bcb3f3fa7b42a28e2f403b9ac4bc7e5a5b2d2 Mon Sep 17 00:00:00 2001
From: Ivan Petrov <ivan@example.com>  // Автор коммита
Date:   Tue Dec 3 10:15:42 2024 +0300  // Дата коммита

    Add user service and simple validation  // Тема и краткое описание

    Здесь можно разместить более подробное описание коммита,
    разбить его на абзацы и объяснить мотивацию изменений.

    Signed-off-by: Ivan Petrov <ivan@example.com>  // Опциональная подпись

---
 src/user/service.go | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/src/user/service.go b/src/user/service.go
new file mode 100644
index 000000000..f5b6e422d
--- /dev/null
+++ b/src/user/service.go
@@ -0,0 +1,25 @@
+package user
+
+type Service struct {
+    // Здесь может быть бизнес-логика работы с пользователями
+}
+
+func (s *Service) ValidateName(name string) bool {
+    if len(name) == 0 {
+        return false  // Пустое имя считаем невалидным
+    }
+    return len(name) <= 50  // Ограничиваем длину имени
+}

Обратите внимание, что в патче присутствует:

  • шапка с данными коммита,
  • статистика изменений (1 file changed, 25 insertions(+)),
  • сам diff по файлам.

git format-patch как раз и создает такие файлы автоматически из ваших коммитов.

Базовое использование git format-patch

Простейший случай — один патч из последнего коммита

Представьте, что вы сделали коммит и хотите создать патч, чтобы отправить его коллеге.

git format-patch -1
# -1 означает - взять один последний коммит

Git создаст файл вида:

  • 0001-Add-user-service-and-simple-validation.patch

Имя формируется так:

  • порядковый номер в серии (0001),
  • тема коммита, где пробелы заменены на дефисы.

Вы можете открыть этот файл в любом текстовом редакторе и посмотреть содержимое. Его можно:

  • прикрепить к письму,
  • передать через мессенджер,
  • сохранить как артефакт CI.

Несколько коммитов — серия патчей

Часто изменения разбиты на несколько логичных коммитов. В этом случае удобно отдать серию патчей вместо одного большого.

Команда:

git format-patch HEAD~3
// Создать патчи для трех последних коммитов
// Диапазон: (HEAD~3, HEAD] — не включая HEAD~3, включая HEAD

Git создаст три файла:

  • 0001-...patch
  • 0002-...patch
  • 0003-...patch

Порядок соответствует порядку применения: получатель сможет применять их по очереди.

Можно указать диапазон явно:

git format-patch <base-commit>..HEAD
// Например, все коммиты после конкретного коммита base-commit

Где base-commit — SHA или имя тега/ветки, от которого вы хотите взять изменения.

Диапазоны и ссылки: как правильно выбирать коммиты

Давайте разберемся на примере:

  • Есть ветка main,
  • Вы создали ветку feature/login,
  • Сделали в ней 5 коммитов.

Теперь вам нужно сгенерировать патчи только для этих 5 коммитов, без того, что уже есть в main.

Сделайте так:

git checkout feature/login

git format-patch main
// Создаст патчи для всех коммитов, которых нет в main,
// но есть в текущей ветке feature/login

Здесь Git сам вычисляет разницу между feature/login и main и создает патчи для недостающих коммитов.

Альтернативный вариант с явным диапазоном:

git format-patch main..feature/login
// То же самое, но более явно

Куда сохраняются патчи

По умолчанию патчи создаются в текущем каталоге. Если вы хотите положить их в отдельную папку, используйте опцию -o:

mkdir patches

git format-patch main -o patches
// Все файлы патчей будут в каталоге patches

Это удобно, если вы хотите держать рабочую директорию в чистоте или передать папку целиком.

Структура и содержание патча

Что включается в патч и почему это важно

Патч, созданный git format-patch, содержит:

  • строку From <SHA> ... — оригинальный хеш коммита,
  • автора и дату,
  • тему (subject) и тело сообщения,
  • список файлов и статистику изменений,
  • diff с контекстом.

Смотрите, как команды управления формируют это содержимое:

git log -1 --pretty=full
// Показывает примерно то же, что будет в шапке патча

Git использует именно информацию из истории, а не “состояние файлов на диске” — это важный момент. Поэтому, если вы хотите изменить тему или описание коммита, сначала используйте git commit --amend или интерактивный rebase, а уже потом создавайте патчи.

Добавление подписи Signed-off-by

Многие проекты (например, ядро Linux) требуют обязательного поля Signed-off-by. git format-patch сам по себе это поле не добавляет, он только переносит уже существующее из сообщения коммита.

Чтобы добавить подпись, при коммите используйте опцию -s:

git commit -s -m "Add validation to user service"
// -s добавляет строку Signed-off-by в сообщение коммита

В патче она появится автоматически:

Signed-off-by- Ivan Petrov <ivan@example.com>

Если подписи не было, а добавить ее нужно уже после, можно:

git commit --amend -s
// Изменяет последний коммит, добавляя подпись

И только после этого заново сгенерировать патчи.

Отделение шапки письма от тела патча

Когда патчи отправляются по почте, важно разделять:

  • “письмо” (email), то есть шапка с темой и описанием серии,
  • и сами патчи.

Внутри одного файла патча структура примерно такая:

  • строка From,
  • блок метаданных,
  • тема коммита,
  • текст описания,
  • пустая строка,
  • разделитель ---,
  • дальше статистика и diff.

Все, что выше ---, относится к “описанию” коммита. Ниже — техническая часть.

Если вы редактируете патч вручную, будьте аккуратны и не меняйте структуру без необходимости — это может затруднить автоматическое применение.

Полезные опции git format-patch

Опция -n / --numbered и нумерация патчей

По умолчанию git format-patch уже нумерует файлы (0001, 0002, ...). Опция -n влияет на нумерацию в теме писем (Subject), если вы дальше используете git send-email.

git format-patch -n main
// Явно включить нумерацию патчей

Но чаще вам понадобится не это, а опция --subject-prefix.

Настройка Subject: префиксы для патчей

Большинство проектов используют стандартные префиксы для писем с патчами, например:

  • [PATCH],
  • [PATCH v2],
  • [PATCH RFC] и т.д.

git format-patch умеет добавлять такой префикс в тему автоматически.

git format-patch --subject-prefix="PATCH" main
// Тема будет вида [PATCH 1/3] Add user service

Если вы отправляете вторую версию патч-серии:

git format-patch --subject-prefix="PATCH v2" main

Эта опция полезна, когда вы используете git send-email, потому что она формирует тему письма, понятную мейнтейнерам.

Добавление обложки (cover letter) для серии патчей

Когда вы отправляете несколько патчей, удобно добавить “обложку” — единое письмо, в котором вы:

  • коротко описываете цель всей серии,
  • перечисляете, что меняет каждый патч (“Patch 1- добавляет… Patch 2- исправляет…”),
  • даете общий контекст.

git format-patch позволяет создать черновик такого cover letter:

git format-patch -n --cover-letter main
// -n — нумерация, --cover-letter — создать обложку

В результате, кроме файлов 0001-...patch, 0002-...patch, вы получите и файл вида:

  • 0000-cover-letter.patch

Его можно открыть и отредактировать:

Cover Letter

[город и дата опционально]

Здесь вы можете описать мотивацию всей серии патчей,
изменения по пунктам, потенциальные вопросы к ревьюерам.

---

v2:
 - Уточнил валидацию имени
 - Добавил тесты для граничных случаев

Дальше вы отправите его через git send-email как первое письмо в серии.

Формирование патчей только по конкретным файлам или директориям

Иногда нужно сделать патчи только по части дерева проекта, например, только по каталогу src/api.

Сделайте так:

git format-patch main -- src/api
// Патчи будут содержать изменения только в поддиректории src/api

Здесь ключевой момент — двойное тире --. Оно отделяет список ревизий от списка путей. Все, что после --, трактуется как путь в репозитории.

Указание автора и переопределение email (редко, но бывает нужно)

git format-patch использует автора из коммита. Но есть ситуации, когда вы хотите изменить отображение имени, не трогая сам коммит (например, для тестов или локальной переупаковки).

Можно поправить автора через фильтр, но обычно проще заранее корректно настраивать Git:

git config user.name "Ivan Petrov"
git config user.email "ivan@example.com"

Тогда и коммиты, и патчи будут с ожидаемыми полями.

Применение патчей: git am

Разница между git apply и git am

Есть два основных способа “применить” патч:

  • git apply — просто применяет diff к рабочим файлам, но не создает коммит автоматически;
  • git am — применяет патч как коммит, сохраняя автора, дату, сообщение.

Патчи, созданные git format-patch, как раз рассчитаны на git am.

Вот пример:

git am 0001-Add-user-service-and-simple-validation.patch
// Создаст новый коммит с автором и сообщением из патча

Если у вас серия патчей:

git am patches/*.patch
// Применить все патчи по порядку

Или:

git am 0001-*.patch 0002-*.patch
// Явно указать файлы

Что делает git am “под капотом”

При применении патча git am:

  1. Читает шапку патча (автор, дата, тема, тело).
  2. Применяет diff к текущему состоянию ветки.
  3. Создает новый коммит:
    • автор — из патча,
    • коммитер — тот, кто запускает команду,
    • сообщение коммита — из патча.

Важно: хеш нового коммита будет отличаться от оригинального, потому что меняется его родитель (parent) и часто коммитер.

Разбор конфликтов при git am

Если патч не может быть применен напрямую (например, код в целевой ветке сильно изменился), git am остановится с конфликтом.

Вы увидите примерно такое:

Applying: Add user service and simple validation
error- patch failed- ...
hint- Resolve all conflicts manually then run "git am --continue"

Алгоритм действий:

  1. Посмотреть конфликтные файлы:

    git status
    // Git покажет файлы в состоянии both modified
    
  2. Открыть эти файлы и вручную исправить конфликтные блоки:

    // Пример конфликтного блока
    
    func (s *Service) ValidateName(name string) bool {
    <<<<<<< HEAD
        // Новое поведение в текущей ветке
        return len(name) >= 3 && len(name) <= 50
    =======
        if len(name) == 0 {
            return false
        }
        return len(name) <= 50
    >>>>>>> Add user service and simple validation
    }
    

    // Здесь вам нужно вручную выбрать и скорректировать итоговую логику.

  3. Отметить файл решенным:

    git add src/user/service.go
    
  4. Продолжить применение серии:

    git am --continue
    

Если вы хотите прервать применение всех оставшихся патчей:

git am --abort
// Откатывает состояние к моменту перед началом git am

Применение патчей “вслепую” и тестирование

Перед тем как применять сложную серию патчей к важной ветке, разумно:

  • создать отдельную временную ветку,
  • применить патчи там и посмотреть на результат.

Пример:

git checkout -b tmp/review-patches main
// Создаем временную ветку от main

git am ../incoming-patches/*.patch
// Применяем серию патчей

// Дальше можно просмотреть историю, запустить тесты и т.д.

Так вы не рискуете поломать основную ветку.

Использование git format-patch в реальных сценариях

Отправка изменений по почте

Типичный сценарий:

  1. Вы сделали серию коммитов в ветке feature/login.
  2. Вам нужно отправить их мейнтейнерам по почте.

Сначала формируем патчи:

git checkout feature/login

git format-patch -n --cover-letter --subject-prefix="PATCH v2" main -o outbox
// -n — нумерация
// --cover-letter — создать обложку
// --subject-prefix — добавить префикс в тему писем
// -o outbox — сложить файлы в каталог outbox

Далее:

  • редактируем outbox/0000-cover-letter.patch,
  • используем git send-email (если настроен) или прикладываем файлы вручную.

Команда с git send-email может выглядеть так:

git send-email outbox/*.patch \
  --to "maintainer@example.com" \
  --cc "dev-list@example.com"

git send-email сам:

  • возьмет тему из патчей,
  • подставит нужные заголовки,
  • сформирует цепочку ответов (thread) для серии.

Патчи как альтернатива pull request во внутренних проектах

Если у вас нет общего централизованного Git-сервера или есть ограничения на доступ, можно устроить процесс ревью на патчах:

  1. Разработчик делает ветку и коммиты.
  2. Создает патчи:

    git format-patch main -o reviews/feature-login
    
  3. Отправляет папку reviews/feature-login коллеге.
  4. Коллега:
    • применяет патчи в отдельной ветке,
    • просматривает изменения,
    • пишет замечания или модифицирует патчи.

Это удобно, когда репозитории находятся в закрытых контурах или используются разные системы контроля версий.

Перенос изменений между “несвязанными” репозиториями

Бывает, что два Git-репозитория логически связаны, но не имеют общего remote (или вы не можете его добавить).

Решение:

  • генерируем патчи в одном репозитории,
  • применяем в другом.

Пример:

# В первом репозитории
git format-patch main -o /tmp/exported-patches

Копируем /tmp/exported-patches во второй репозиторий (через архив, сетевую папку и т.п.), затем:

# Во втором репозитории
git checkout -b sync-from-repo1

git am /path/to/exported-patches/*.patch

Так вы переносите не только код, но и историю коммитов (авторы, сообщения).

Патчи для code review без изменения ветки

Иногда мейнтейнеры не хотят мержить вашу ветку напрямую, а предпочитают просмотреть “чистую” серию патчей, чтобы потом вручную выбрать нужные.

В этом случае у вас есть два варианта:

  1. Отправить патчи как есть (git format-patch + git send-email).
  2. Сгенерировать патчи после “перепаковки” истории.

Например, вы хотите сделать историю линейной и понятной:

git rebase -i main
// В интерактивном режиме вы можете:
// - объединить мелкие коммиты (squash),
// - переименовать сообщения,
// - удалить лишнее

После этого формируете патчи:

git format-patch main -o clean-patches

Теперь серия патчей отражает уже “очищенную” историю, а не хаотические промежуточные коммиты.

Комбинация с другими инструментами Git

Связка git format-patch и git rebase -i

Интерактивный rebase часто идет рука об руку с git format-patch. Логика такая:

  1. Сначала вы приводите историю коммитов к внятному виду:

    git rebase -i main
    // В открывшемся редакторе:
    // - pick для важных коммитов
    // - squash/fixup для мелких правок
    // - reword для улучшения сообщений
    
  2. Затем создаете серию патчей:

    git format-patch main -o final-patches
    

Таким образом вы отправляете на ревью уже аккуратную серию коммитов.

Связка git format-patch и git cherry-pick

Если у вас есть патчи, которые вы применили через git am, и нужно выбрать только часть этих изменений в другую ветку, вы можете:

  • либо заново сгенерировать патчи из конкретных коммитов,
  • либо использовать git cherry-pick по их SHA.

Пример:

git log --oneline
// Допустим, вы видите коммит a1b2c3d "Add user service"

git checkout other-branch

git cherry-pick a1b2c3d
// Переносит только этот коммит

Часто рабочий процесс выглядит так:

  • сначала вы применяете патчи в отдельной ветке,
  • смотрите на результат,
  • а потом точечно переносите нужные коммиты в основную ветку через cherry-pick.

Автоматизация в CI

git format-patch можно использовать и в CI:

  • для генерации артефактов с изменениями,
  • для автоматической отправки патчей на внутренний email-бот,
  • для проверки того, что в серии патчей нет “грязных” коммитов (например, через скрипт, анализирующий файлы патчей).

Простейший пример скрипта, который в CI формирует патчи для всех изменений с момента последнего тега:

#!/usr/bin/env bash

# Находим последний тег
LAST_TAG=$(git describe --tags --abbrev=0)

# Генерируем патчи с этого тега до HEAD
git format-patch "$LAST_TAG"..HEAD -o ci-patches

# Здесь вы можете:
# - сохранить ci-patches как артефакт
# - отправить их куда-то, где они будут проанализированы

Комментарии в таком скрипте помогают другим разработчикам понять, зачем это делается и как это использовать.

Типичные ошибки и как их избегать

Создание патчей из “грязного” состояния

git format-patch всегда работает только с коммитами. Если у вас есть несохраненные изменения в рабочей директории, они в патчи не попадут.

Проверьте состояние:

git status
// Убедитесь, что у вас нет uncommitted changes,
// иначе часть работы просто не окажется в патчах

Решение:

  • либо закоммитьте изменения,
  • либо создайте отдельный коммит для “черновика” и потом очистите историю через rebase -i.

Патчи из неправильного диапазона

Очень частая проблема — вы случайно включили лишние коммиты (например, чужие изменения или старые фиксы). Это происходит, когда неправильно указан диапазон ревизий.

Рекомендация:

  • Всегда явно проверяйте, какие коммиты попадут в патчи.

Например:

git log main..feature/login --oneline
// Смотрите список коммитов, которые будут в серии

git format-patch main..feature/login
// И только после проверки формируйте патчи

Изменение патча вручную без понимания

Патч — это текстовый файл, и его можно редактировать. Но:

  • изменение diff-блоков может сделать патч неприменимым,
  • изменение шапки (From, Date и т.п.) может ввести в заблуждение.

Если вам нужно поправить сообщение коммита, лучше:

  1. поправить коммит (через git commit --amend или git rebase -i),
  2. заново сгенерировать патчи.

Ручное редактирование самого патча стоит делать только, если вы уверены, что понимаете структуру и последствия.

Игнорирование версии серии патчей

Когда вы отправляете одну и ту же серию несколько раз (с учетом замечаний), очень помогает указывать версию:

  • [PATCH v1],
  • [PATCH v2] и т.д.

Иначе ревьюерам сложно понять, где новая версия, а где старая.

Используйте --subject-prefix:

git format-patch --subject-prefix="PATCH v3" main

И описывайте изменения между версиями в cover letter (раздел v3: и т.п.).

Заключение

git format-patch — это инструмент, который раскрывает полную мощь патч‑ориентированного рабочего процесса в Git. Он позволяет:

  • аккуратно упаковывать ваши коммиты в переносимый текстовый формат,
  • отправлять изменения по почте или через любые сторонние каналы,
  • переносить историю между репозиториями и ветками,
  • интегрировать ревью и обмен патчами в CI и внутреннюю инфраструктуру.

Чтобы эффективно использовать git format-patch, важно:

  • понимать, что он работает именно с коммитами, а не с “текущими изменениями”;
  • уметь задавать правильные диапазоны (main..feature, HEAD~3 и т.д.);
  • знать, как применять патчи через git am, и быть готовым разруливать конфликты;
  • комбинировать его с git rebase -i, git send-email, git cherry-pick.

Если вы постепенно внедрите патчи в свой процесс — хотя бы как дополнительный инструмент к pull request, — у вас появится более гибкий способ обмениваться изменениями, особенно в распределенных командах и в проектах с высокой культурой истории коммитов.

Частозадаваемые технические вопросы и ответы

Как сгенерировать патч только для одного коммита, который не является последним?

Используйте SHA или сокращенный хеш:

git format-patch -1 <commit-sha>
// -1 — один коммит
// <commit-sha> — нужный коммит

Например:

git format-patch -1 a1b2c3d4

Git создаст патч именно для этого коммита, даже если после него есть другие.

Как исключить из серии патчей один конкретный коммит?

Проще всего — временно “убрать” его из истории с помощью rebase -i, а потом вернуть обратно.

  1. Запустите:

    git rebase -i main
    
  2. В открывшемся списке поставьте перед нежелательным коммитом drop или просто удалите строку.
  3. Сохраните и завершите rebase.
  4. Сгенерируйте патчи:

    git format-patch main -o outbox
    

Нежелательный коммит в патчи не попадет.

Как изменить тему (Subject) коммита, который уже попал в патч?

Менять тему прямо в патче не рекомендуется. Лучше:

  1. Обновить коммит:

    git rebase -i main
    // Перед нужным коммитом поставьте reword
    // В следующем шаге введите новую тему
    
  2. Заново сформировать патчи:

    git format-patch main -o outbox
    

Патчи будут уже с новой темой.

Можно ли объединить несколько патчей в один перед отправкой?

Да, но делается это на уровне коммитов, а не патчей. Используйте интерактивный rebase:

git rebase -i main
// Для нескольких коммитов подряд поставьте squash или fixup

Git объединит их в один коммит. После этого:

git format-patch main -1
// Один патч для объединенного коммита

Как проверить, что патч можно применить без конфликтов, не изменяя текущую ветку?

Создайте временную ветку и попробуйте применить патч там:

git checkout -b tmp/check-patch main
git am /path/to/patches/*.patch

Если git am прошел без ошибок, значит патч применим. После проверки можно удалить временную ветку:

git checkout main
git branch -D tmp/check-patch
Хуки Git git hooksСтрелочка вправо

Постройте личный план изучения Git до уровня Middle — бесплатно!

Git — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Git

Открыть базу знаний

Лучшие курсы по теме

изображение курса

Основы Git

Антон Ларичев
AI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий