Применение патчей в Git с помощью git am

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

Олег Марков

Введение

Механизм применения патчей в Git часто ассоциируется с командой git apply, но в реальных командных процессах, особенно когда задействованы почтовые рассылки и code review по почте, гораздо важнее git am.

git am берет патчи в формате email-сообщений (mbox), извлекает из них и изменения, и метаданные (автора, дату, тему, сообщение коммита) и превращает каждый такой патч в полноценный коммит в вашей ветке.

Смотрите, в этой статье я покажу вам:

  • чем git am отличается от git apply;
  • как подготовить и применить пачку патчей;
  • как разруливать конфликты при git am;
  • как «перепридумывать» патчи перед применением (опции --committer-date-is-author-date, --signoff, --whitespace и другие);
  • как отменить неудачный git am и вернуть репозиторий в рабочее состояние.

Мы будем двигаться по шагам, с практическими примерами, чтобы вы могли использовать git am в реальной работе, а не только в теории.

Что такое git am и когда его использовать

Задача, которую решает git am

git am расшифровывается как "apply mailbox". Его основное назначение — взять набор email-писем с патчами и превратить каждое письмо в отдельный коммит, сохранив:

  • автора (From);
  • время создания;
  • тему письма как заголовок коммита;
  • тело письма как описание коммита;
  • сам дифф как изменения в файлах.

Это особенно полезно в сценариях:

  • вы получаете патчи по электронной почте (классический workflow opensource-проектов в стиле Linux kernel);
  • у вас есть mbox файл (выгрузка из почты) с набором патчей;
  • коллега отправил вам пачку патчей, созданных через git format-patch;
  • вы хотите сохранить оригинальных авторов коммитов при перенесении изменений между репозиториями.

Отличие git am от git apply и git cherry-pick

Давайте разберемся, чем git am отличается от похожих команд:

  • git apply

    • применяет только дифф (изменения в файлах);
    • не создает коммит автоматически;
    • не использует метаданные email-писем;
    • после git apply вы сами решаете, как и с каким сообщением делать коммит.
  • git cherry-pick

    • берет существующий коммит (по SHA) из другой ветки и переносит его к вам;
    • не работает с файлами-патчами;
    • сохраняет автора и сообщение коммита, но источник — не email, а другой коммит в том же репозитории (или в связанном через remote).
  • git am

    • берет патчи из файлов или stdin;
    • ожидает формат email-писем (обычно, результат git format-patch);
    • создает новые коммиты, используя метаданные письма как данные коммита.

Проще всего запомнить:

  • git format-patch — делает письма из коммитов;
  • git am — делает коммиты из писем.

Подготовка патчей для git am

Связка git format-patch и git am

В типичном процессе один разработчик создает патчи с помощью git format-patch, другой применяет их git am.

Покажу вам простую схему:

  1. Разработчик A делает несколько коммитов в ветке feature.
  2. Он создает пачку патчей:

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

    // Команда создаст серию файлов вида: // 0001-Добавить-новую-функцию.patch // 0002-Поправить-ошибку.patch // и т.д.

  3. Он отправляет эти .patch файлы разработчику B (по почте, в архиве, через issue-трекер).

  4. Разработчик B применяет их у себя:

    git checkout main
    git am 000*.patch
    

    // Каждому patch-файлу соответствует один новый коммит // с сохранением автора, даты и сообщения

Форматы входа для git am

git am умеет читать патчи из:

  • одного файла (один или несколько патчей в формате mbox);
  • списка файлов;
  • стандартного ввода (stdin), например через пайп из curl или cat.

Пример применения одного файла

# Применяем один patch-файл
git am 0001-fix-typo-in-readme.patch

// Git прочитает email-заголовки и дифф // и создаст один новый коммит в текущей ветке

Пример применения нескольких файлов

# Применяем сразу все патчи из текущей директории
git am 000*.patch

// Патчи будут применены по порядку // согласно лексикографическому имени файла (0001, 0002, ...)

Применение из mbox-файла

Иногда патчи собирают в один файл в формате mbox. Тогда:

git am patches.mbox

// Git пройдет по всем письмам в mbox и превратит каждое в отдельный коммит

Применение патчей из stdin

Это удобно, если вы скачиваете патч напрямую по URL. Давайте разберемся на примере:

curl -L https://example.com/patches/series.mbox | git am

// curl скачивает содержимое файла // и передает его на вход git am // git am читает патчи со стандартного ввода и применяет их

Базовое использование git am

Простейший сценарий шаг за шагом

Допустим, у вас есть репозиторий и вы хотите применить патчи, которые прислал коллега.

  1. Убедитесь, что рабочее дерево чистое:

    git status
    

    // Если есть незакоммиченные изменения, лучше их закоммитить или отложить // через git stash, чтобы они не мешали применению патчей

  2. Переключитесь в нужную ветку:

    git checkout main
    
  3. Примените патчи:

    git am /path/to/patches/000*.patch
    

    // Патчи будут применяться по одному // Если все прошло без конфликтов, появится серия новых коммитов

  4. Посмотрите историю:

    git log --oneline
    

    // Вы увидите новые коммиты, автором которых будут те, // кто изначально писал патчи

Как git am формирует коммиты

Когда git am применяет патч, он:

  1. Берет заголовки письма:
    • From → автор коммита;
    • Date → дата автора;
    • Subject → заголовок коммита (первые строки);
  2. Берет тело письма как основное сообщение коммита (после пустой строки);
  3. Берет дифф (часть после --- и +++) как изменения в файлах.

Типичный патч от git format-patch выглядит так (упрощенно):

From 1234567890abcdef Mon Sep 17 00:00:00 2001
From: Иван Иванов <ivan@example.com>
Date:   Mon, 2 Dec 2024 10:00:00 +0300
Subject: [PATCH 1/2] Добавить функцию суммирования

--- a/math.go
+++ b/math.go
@@ -1,3 +1,7 @@
+// Sum возвращает сумму двух чисел
+func Sum(a, b int) int {
+    return a + b
+}

Когда вы запускаете:

git am 0001-Add-sum-function.patch

Git создаст коммит:

  • автор — Иван Иванов;
  • дата — Mon, 2 Dec 2024 10:00:00 +0300;
  • заголовок коммита — [PATCH 1/2] Добавить функцию суммирования (по умолчанию, без обрезки префикса);
  • изменения — добавление функции Sum.

Важные опции git am и их использование

Работа с датами и авторством

--committer-date-is-author-date

По умолчанию у коммита есть две даты:

  • author date — когда автор сделал изменения;
  • committer date — когда изменения попали в вашу ветку (вы применили патч).

Иногда хотят, чтобы обе даты совпадали. Для этого есть:

git am --committer-date-is-author-date 0001-*.patch

// В этом случае время применения патча не учитывается // и история выглядит так, как будто коммиты были сделаны сразу в этой ветке

--ignore-date

Если, наоборот, вы хотите использовать текущее время как дату автора, можно сделать так:

git am --ignore-date 0001-*.patch

// Тогда и author date, и committer date будут установлены в текущее время

Добавление sign-off к коммитам

Во многих проектах (например, в Linux kernel) требуется строка Signed-off-by в сообщении коммита.

Чтобы не добавлять ее вручную, используйте:

git am --signoff 0001-*.patch

// Git возьмет ваше имя и email из настройки user.name и user.email // и добавит строку в конец сообщения коммита: // // Signed-off-by: Ваше Имя you@example.com

Если в патче уже есть Signed-off-by автора, в итоге будет две строки: автора и ваша. Это нормально и ожидаемо в таких workflow.

Управление пробелами: --whitespace и --ignore-space-change

Патчи часто ломаются из-за пробелов и переносов строк. Чтобы уменьшить количество проблем, используйте параметры обработки пробелов.

Самая полезная опция при git am--whitespace. Например:

git am --whitespace=fix 0001-*.patch

Основные значения:

  • --whitespace=nowarn — не предупреждать о проблемах с пробелами;
  • --whitespace=fix — пытаться автоматически исправить проблемы (лишние пробелы в конце строк и т.п.);
  • --whitespace=error — считать проблемы с пробелами ошибкой и прерывать применение патча.

Смотрите, если у вас много патчей с мелкими проблемами форматирования, опция fix помогает их «пригладить», не вводя вас в постоянные конфликты из-за пробелов.

Дополнительно можно использовать опции диффа через -C (context) или -pN, но чаще всего это идет из формата патча, а не из git am.

Пропуск и повторное применение патчей

Если какой-то патч уже был применен (например, изменения уже в вашей ветке), Git может это обнаружить и пропустить его.

--skip

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

git am --skip

// Команда пропустит текущий патч // и попытается применить следующий

Это полезно, если:

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

--continue

Когда вы сталкиваетесь с конфликтом, вы:

  1. Ручками правите файлы;
  2. Отмечаете конфликт как решенный:

    git add путь/к/файлу
    

    // Повторяете для всех конфликтных файлов

  3. Продолжаете применение:

    git am --continue
    

// Git завершит текущий коммит (который конфликтовал) // и перейдет к следующему патчу в очереди

--abort

Если вы понимаете, что применение патчей зашло в тупик, можно полностью откатить процесс:

git am --abort

// Git вернет репозиторий к состоянию, в котором он был до запуска git am // Все частично примененные патчи и промежуточное состояние будут сброшены

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

Как выглядит конфликт в процессе git am

Представьте, что вы применяете пачку:

git am 000*.patch

На каком-то патче git останавливается и пишет:

Applying: Fix typo in README
error: patch failed: README.md:10
error: README.md: patch does not apply
Patch failed at 0003 Fix typo in README
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip".
To restore the original branch and stop patching, run "git am --abort".

Смотрите, здесь важные моменты:

  • Git указывает, что сломался на патче 0003 Fix typo in README;
  • подсказывает, что можно:
    • посмотреть сам патч через git am --show-current-patch;
    • продолжить после ручного решения конфликта (--continue);
    • пропустить (--skip);
    • отменить полностью (--abort).

Как посмотреть текущий патч

Очень полезная команда:

git am --show-current-patch=diff

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

Разрешение конфликта: пошаговый пример

Пусть у вас конфликт в файле README.md.

  1. Git отметит конфликтные места вот так:

    <<<<<<< HEAD
    Текущая версия строки в вашей ветке
    =======
    Версия строки из патча
    >>>>>>> Fix typo in README
    

    // Все, что выше = — ваша текущая версия // Все, что ниже = и до >>>>>>> — версия из патча

  2. Вы открываете файл и приводите его к желаемому виду, удаляя конфликтные маркеры:

    Окончательная версия строки которую вы выбрали
    

    // Здесь вы вручную решили, какой вариант оставить // (или объединили оба варианта по смыслу)

  3. Отмечаете файл как исправленный:

    git add README.md
    
  4. Продолжаете применение патчей:

    git am --continue
    

    // Git завершит коммит для текущего патча // и перейдет к следующему

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

git am --skip

А если хотите отменить все:

git am --abort

Переписывание сообщений и авторства при git am

Иногда бывает нужно изменить:

  • автора коммита;
  • тему/описание коммита;
  • добавить дополнительные строки в сообщение коммита.

Режим интерактивного редактирования сообщений

Один из вариантов — применить патч и потом сделать git commit --amend, но когда патчей много, это неудобно. Для git am есть опция:

git am -i 0001-*.patch

Однако в современных Git обычно используют флаг --interactive или связку с --edit. Более простой рабочий прием — включить редактирование сообщения для каждого применяемого патча через:

git am --edit 0001-*.patch

// Для каждого патча откроется редактор сообщения коммита // Вы сможете поправить заголовок и тело коммита перед его созданием

Переопределение автора

Если вы хотите явно задать автора коммита (например, патч пришел без корректных заголовков), можно сделать так:

GIT_AUTHOR_NAME="Иван Иванов" \
GIT_AUTHOR_EMAIL="ivan@example.com" \
git am 0001-*.patch

// Переменные окружения переопределят автора // для создаваемых коммитов

Аналогично можно управлять GIT_COMMITTER_NAME и GIT_COMMITTER_EMAIL.

Практический пример применения серии патчей

Давайте разберемся на примере типичного рабочего процесса от начала до конца.

Сценарий

  • Ветка main — основная.
  • Разработчик A сделал новую функциональность в ветке feature-x.
  • Он создал четыре коммита и сгенерировал патчи:

    git checkout feature-x
    git format-patch main
    

    // Появились файлы 0001-..., 0002-..., 0003-..., 0004-...

  • Эти файлы вы получили в папке /tmp/patches.

Применяем патчи как интегратор изменений

  1. Проверяем состояние:

    git status
    
  2. Переключаемся в main:

    git checkout main
    
  3. Применяем патчи, добавляя свой sign-off:

    git am --signoff /tmp/patches/000*.patch
    

    // Если все прошло гладко, получим 4 новых коммита // Каждый будет содержать: // - автора как у оригинального разработчика // - дату из оригинального коммита // - ваше Signed-off-by внизу сообщения

  4. Проверяем историю:

    git log --oneline --decorate -4
    

    // Здесь вы увидите последние 4 коммита из серии

Если на каком-то шаге возник конфликт, переходите к алгоритму из раздела про конфликты: правка файлов → git addgit am --continue.

Отличия git am от git apply на практике

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

git apply: ручной режим

Пример:

git apply 0001-fix-bug.patch

// Применяет изменения к рабочему дереву // НЕ создает коммит автоматически

После этого:

git status

// Увидите измененные файлы // Дальше вам нужно:

git add .
git commit -m "Fix bug"

git am: автоматический режим с метаданными

git am 0001-fix-bug.patch

// Сразу создает коммит // Использует автора, дату и сообщение из патча

Когда что использовать

  • git apply, если:

    • патч не в email-формате (нет заголовков From/Subject);
    • вы хотите вручную написать сообщение коммита;
    • патч — это временное изменение или эксперимент.
  • git am, если:

    • у вас патчи от git format-patch;
    • важно сохранить оригинального автора и дату;
    • вы интегрируете чужие изменения как полноценную историю.

Советы по безопасной работе с git am

Всегда работайте в чистом дереве

Перед git am проверяйте:

git status

Если есть локальные незакоммиченные изменения, примените один из вариантов:

  • закоммитьте:

    git add .
    git commit -m "WIP save"
    
  • или отложите:

    git stash push -m "Перед git am"
    

// Это снизит риск того, что ваши текущие изменения будут конфликтовать с патчами

Используйте отдельную ветку для теста патчей

Хорошая практика — не применять патчи сразу в main, а сначала в отдельную ветку.

git checkout -b review-feature-x main
git am /tmp/patches/000*.patch

// Здесь вы можете: // - посмотреть изменения // - прогнать тесты // - при необходимости поправить историю

Если все хорошо, можно потом смержить эту ветку в main.

Проверяйте результат после применения

После удачного git am полезно:

git log --oneline -n 5
git show HEAD

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

При необходимости последний коммит можно поправить:

git commit --amend

// Но помните, что это изменит хэш коммита // Лучше делать такое до того, как ветка уйдет на общий remote

Заключение

git am — это инструмент для автоматического применения патчей в формате email, который:

  • преобразует каждый патч в самостоятельный коммит;
  • сохраняет автора, дату и сообщение из исходного письма;
  • поддерживает удобный workflow с git format-patch;
  • умеет работать с конфликтами, пропуском патчей и отменой операции.

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

Используя опции --signoff, --whitespace, --committer-date-is-author-date, а также команды --continue, --skip, --abort, вы можете гибко управлять процессом применения патчей и избегать типичных проблем.

Ключ к уверенной работе с git am — практиковаться на отдельных ветках, внимательно следить за состоянием репозитория и не бояться использовать git am --abort, когда что-то идет не так.

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

1. Как применить только часть патчей из серии, а не все сразу?

Если у вас есть, например, 10 патчей, но вы хотите применить только первые 3, сделайте так:

  1. Посмотрите список файлов:

    ls 000*.patch
    
  2. Явно укажите нужные файлы:

    git am 0001-*.patch 0002-*.patch 0003-*.patch
    

Если патчи в одном mbox, можно предварительно разделить его на несколько файлов с помощью git mailinfo или простой ручной нарезки, но на практике чаще управляют набором через git format-patch на стороне отправителя.

2. Что делать, если патч не в формате mbox и git am его не воспринимает?

Если патч — обычный unified diff без email-заголовков (From, Subject), используйте:

git apply файл.patch
git commit -m "Описание изменений"

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

3. Как понять, какие патчи уже применены, если у меня их много?

Если патч был создан через git format-patch, внутри него есть строка From <SHA>. Ее можно поискать в истории:

git log --grep='Original-commit-SHA' --all

Но удобнее использовать git patch-id:

git show <commit> | git patch-id
cat 0001-some.patch | git patch-id

Если patch-id совпадают, патч уже присутствует в истории в эквивалентном виде.

4. Как изменить кодировку сообщений коммитов при git am?

Если письма с патчами в другой кодировке (например, Windows-1251), можно предварительно переконвертировать файл:

iconv -f WINDOWS-1251 -t UTF-8 patches.mbox > patches-utf8.mbox
git am patches-utf8.mbox

Git ожидает, что содержимое патчей будет в UTF-8, особенно если вы хотите, чтобы коммиты корректно отображались в большинстве инструментов.

5. Можно ли отменить только часть уже примененных патчей?

Если вы уже применили, например, 5 патчей, а потом поняли, что последние 2 не нужны, можно сделать:

# Откатить два последних коммита
git reset --hard HEAD~2

Либо аккуратнее, если изменения уже ушли в общий репозиторий, использовать git revert для каждого нежелательного коммита:

git revert <hash4> <hash5>

Это создаст новые коммиты, которые логически отменяют изменения, не ломая историю для коллег.

Стрелочка влевоПоиск в истории - git bisectПсевдонимы команд в Git - git aliasСтрелочка вправо

Постройте личный план изучения 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 ₽
Подробнее

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