git cherry-pick: как перенести отдельный коммит в другую ветку
git cherry-pick берёт изменения указанного коммита и создаёт на текущей ветке новый коммит с тем же диффом и сообщением, но другим хешем. Перейдите в целевую ветку и выполните git cherry-pick <хеш>; при конфликте исправьте файлы, сделайте git add и git cherry-pick --continue.
git cherry-pick переносит изменения одного конкретного коммита на текущую ветку, не сливая всю ветку целиком. Git вычисляет дифф выбранного коммита относительно его родителя, применяет его к вашему HEAD и создаёт новый коммит — с тем же сообщением и автором, но с другим хешем, потому что у него другой родитель.
Типичный сценарий: в ветке фичи вы исправили баг, а исправление срочно нужно в релизной ветке, где фича ещё не готова. Сливать всю ветку нельзя, а один коммит — можно.
Как перенести один коммит в другую ветку
Сначала найдите хеш нужного коммита, затем переключитесь на ветку, куда его нужно перенести, и выполните cherry-pick.
# 1. Находим коммит в ветке feature
git log --oneline feature
a1b2c3d Исправить деление на ноль в расчёте скидки
9f8e7d6 Добавить новый экран оплаты
# 2. Переходим в целевую ветку
git switch release-1.4
# 3. Переносим коммит
git cherry-pick a1b2c3d
[release-1.4 4e5f6a7] Исправить деление на ноль в расчёте скидки
Date: Mon Oct 5 14:12:03 2026 +0300
1 file changed, 3 insertions(+), 1 deletion(-)
Обратите внимание: новый коммит получил хеш 4e5f6a7, а не a1b2c3d. Это два разных объекта с одинаковыми изменениями. Исходная ветка feature не меняется.
Вместо хеша можно передать любую ссылку на коммит: git cherry-pick feature возьмёт последний коммит ветки feature, git cherry-pick feature~2 — третий с конца.
Как перенести несколько коммитов
Перечислением
Хеши можно передать списком — Git применит их в указанном порядке, создав столько же новых коммитов.
git cherry-pick a1b2c3d 7c8d9e0 3f4a5b6
Порядок важен: если второй коммит зависит от первого, а вы перечислили их наоборот, получите конфликт или сломанный код. Безопаснее указывать от старых к новым.
Диапазоном
Для подряд идущих коммитов используется синтаксис диапазона. Здесь есть ловушка, на которой спотыкаются почти все: A..B означает «коммиты, достижимые из B, но не из A», то есть сам A в диапазон не входит.
# Перенесёт коммиты ПОСЛЕ A вплоть до B включительно
git cherry-pick A..B
# Перенесёт A и всё до B включительно
git cherry-pick A^..B
Если нужно перенести всё, что есть в ветке feature и чего нет в main, удобна запись git cherry-pick main..feature. Но если коммитов много, задумайтесь: возможно, вам нужен rebase или merge, а не cherry-pick.
Полезные флаги git cherry-pick
| Флаг | Что делает | Когда нужен |
|---|---|---|
-x | Добавляет в сообщение строку «(cherry picked from commit …)» | При переносе исправлений между публичными ветками — видно происхождение |
-n, --no-commit | Применяет изменения в индекс и рабочую директорию без коммита | Хотите объединить несколько коммитов в один или поправить перед коммитом |
-e, --edit | Открывает редактор для правки сообщения | Нужно уточнить сообщение под целевую ветку |
-s, --signoff | Добавляет строку Signed-off-by | Проекты с требованием DCO |
-m 1 | Указывает «основного» родителя для merge-коммита | Перенос merge-коммита |
--ff | Перематывает ветку, если HEAD — родитель выбранного коммита | Редко; позволяет не плодить дубликат |
--allow-empty | Разрешает переносить изначально пустые коммиты | Служебные пустые коммиты |
Зачем нужен -x
Через полгода кто-то увидит в релизной ветке исправление и захочет понять, откуда оно. Строка, добавленная -x, даёт прямой ответ:
git cherry-pick -x a1b2c3d
git log -1
Исправить деление на ноль в расчёте скидки
(cherry picked from commit a1b2c3d4e5f60718293a4b5c6d7e8f9012345678)
Используйте -x, когда исходный коммит уже опубликован. Для переноса из своей локальной ветки, которую вы потом удалите, ссылка будет вести в никуда — там флаг не нужен.
Собрать несколько коммитов в один: -n
git cherry-pick -n a1b2c3d 7c8d9e0
git status # изменения обоих коммитов в индексе
git commit -m "Исправления расчёта скидки"
Конфликты при cherry-pick и как их решать
Конфликт возникает, когда изменения коммита не ложатся на текущий код: в целевой ветке те же строки уже выглядят иначе. Git останавливается и сообщает об этом.
git cherry-pick a1b2c3d
Auto-merging src/discount.js
CONFLICT (content): Merge conflict in src/discount.js
error: could not apply a1b2c3d... Исправить деление на ноль в расчёте скидки
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".
Дальше у вас три пути:
- Разрешить и продолжить. Откройте файл, уберите маркеры
<<<<<<<,=======,>>>>>>>, оставив правильный вариант, затемgit add src/discount.jsиgit cherry-pick --continue. - Пропустить коммит. При переносе списка
git cherry-pick --skipпропустит проблемный коммит и перейдёт к следующему. - Отменить всё.
git cherry-pick --abortвернёт ветку в состояние до начала операции.
Есть ещё git cherry-pick --quit: он прекращает операцию, но оставляет уже применённые коммиты и текущее состояние файлов. Полезно, когда половину списка вы перенесли успешно и дальше хотите действовать вручную.
Если конфликты повторяются из раза в раз (например, вы регулярно переносите исправления в несколько релизных веток), включите git config rerere.enabled true — Git запомнит, как вы разрешили конфликт, и применит решение автоматически в следующий раз.
Как перенести merge-коммит
У merge-коммита два родителя, и Git не знает, относительно какого считать дифф. Без подсказки вы получите ошибку:
git cherry-pick 5d6e7f8
error: commit 5d6e7f8 is a merge but no -m option was given.
Флаг -m 1 говорит: считай изменения относительно первого родителя — обычно это ветка, в которую сливали. Так вы перенесёте всё, что принесла слитая ветка, одним коммитом.
git cherry-pick -m 1 5d6e7f8
Учтите, что после этого Git не будет считать ветку слитой: при будущем merge он может попытаться применить те же изменения повторно.
git cherry-pick и git apply: в чём разница
git apply часто вспоминают рядом с cherry-pick, потому что обе команды «накладывают изменения». Но работают они с разными входными данными.
| git cherry-pick | git apply | |
|---|---|---|
| Источник | Коммит, который есть в репозитории | Файл патча (дифф) |
| Создаёт коммит | Да (без -n) | Нет, только меняет файлы (с --index — ещё и индекс) |
| Сохраняет автора и сообщение | Да | Нет, в патче их нет |
| Конфликты | Трёхстороннее слияние с маркерами | По умолчанию просто отказ; с --3way — маркеры |
| Нужен общий репозиторий | Да, коммит должен быть доступен локально | Нет, патч можно передать как угодно |
Пример использования git apply, когда коммита нет в вашем репозитории — коллега прислал дифф файлом:
# Коллега делает
git diff main..fix-discount > fix.patch
# Вы проверяете и применяете
git apply --check fix.patch # проверить, ляжет ли патч
git apply fix.patch # применить к рабочей директории
git apply --3way fix.patch # при расхождениях — с маркерами конфликтов
Если нужно передать коммит целиком, с автором и сообщением, используйте пару git format-patch и git am: первая сохраняет коммиты в файлы, вторая создаёт из них коммиты. По сути это cherry-pick «по почте».
Кстати, cherry-pick из другого репозитория тоже возможен: добавьте его как remote, выполните git fetch, и коммиты станут доступны по хешу.
Когда cherry-pick — плохая идея
Cherry-pick создаёт дубликаты: одно и то же изменение живёт в истории под двумя хешами. В небольших дозах это нормально, но есть сценарии, где он вредит.
- Перенос десятков коммитов. Если вы переносите почти всю ветку — это rebase или merge. Cherry-pick здесь даст длинную историю дублей и больше конфликтов.
- Двусторонние переносы. Коммит перенесли из feature в main, а потом feature сливают в main. Git обычно справляется (изменения совпадают), но если после переноса строки правили, получите конфликт на ровном месте.
- Зависимые коммиты. Коммит использует функцию, добавленную двумя коммитами ранее. Cherry-pick перенесёт только сам коммит, и код в целевой ветке не соберётся. Всегда проверяйте сборку и тесты после переноса.
Хорошая практика для хотфиксов — делать исправление сразу в самой старой поддерживаемой ветке и сливать его «вверх» в новые. Тогда cherry-pick нужен только как исключение.
Как отменить cherry-pick
Если операция ещё идёт (конфликт) — git cherry-pick --abort. Если коммит уже создан и не отправлен на сервер, уберите его:
git reset --hard HEAD~1
Если коммит уже запушен в общую ветку, переписывать историю нельзя — создайте обратный коммит:
git revert 4e5f6a7
А если вы сделали reset и передумали, потерянный коммит находится через git reflog.
Как проверить, перенесён ли уже коммит
Раз хеши разные, обычный git branch --contains не поможет. Git умеет сравнивать коммиты по содержимому изменений (patch-id):
git cherry -v release-1.4 feature
- a1b2c3d Исправить деление на ноль в расчёте скидки
+ 9f8e7d6 Добавить новый экран оплаты
Минус означает, что эквивалентное изменение в release-1.4 уже есть, плюс — что ещё нет. Альтернатива — git log --cherry-pick --oneline release-1.4...feature, которая скрывает коммиты-близнецы. Если вы использовали -x, можно просто искать по сообщению: git log --grep="cherry picked from commit a1b2c3d".
Итог
git cherry-pick — точечный инструмент: перенести хотфикс в релиз, вытащить полезный коммит из заброшенной ветки, восстановить случайно сделанный не туда коммит. Запомните три вещи: новый коммит получает новый хеш, диапазон A..B не включает A, а для публичных веток стоит добавлять -x. Если нужно перенести не коммит, а дифф из файла, берите git apply. Rebase, stash, cherry-pick и теги подробно разобраны на практике в курсе по Git.
Частые вопросы
Меняет ли cherry-pick исходную ветку?
Нет. Исходный коммит остаётся на месте, а в текущей ветке появляется его копия с новым хешем.
Можно ли сделать cherry-pick коммита из удалённой ветки?
Да. Выполните git fetch, после чего коммит доступен по хешу или как origin/имя-ветки, и его можно передать в git cherry-pick.
Почему после cherry-pick у коммита другой хеш?
Хеш зависит от родителя, времени коммита и содержимого дерева. У копии другой родитель и время коммиттера, поэтому хеш тоже другой.
Сохраняется ли автор коммита при cherry-pick?
Да, автор и дата авторства берутся из исходного коммита, а коммиттером становитесь вы с текущей датой.
Что делать, если cherry-pick говорит, что коммит пустой?
Значит, такие изменения в ветке уже есть. Пропустите его через git cherry-pick --skip или зафиксируйте пустым через git commit --allow-empty.
Эта тема подробно разобрана с практикой в курсе «Git с нуля до уверенного разработчика».
Перейти к курсу