- Андрей Куманяев/
- Git: руководства и команды/
- git cherry-pick: перенос коммита из одной ветки в другую/
git cherry-pick: перенос коммита из одной ветки в другую
Иногда нужно перенести не всю ветку, а только один конкретный коммит. Например, разработчик исправил критическую ошибку в feature-ветке, но её срочно нужно доставить в production, не дожидаясь завершения feature. Именно для таких случаев существует git cherry-pick.
Что такое cherry-pick #
git cherry-pick берёт конкретный коммит из любой ветки и применяет его изменения к текущей ветке. Это похоже на копирование: исходный коммит остаётся на месте, а в текущей ветке создаётся новый коммит с теми же изменениями, но новым SHA-хешем.
feature: A - B - C - D
↑
cherry-pick
main: X - Y - C'
↑
новый коммит (копия C)
Важно понимать: cherry-pick не «переносит», а «копирует» коммит. Оригинальный коммит C остаётся в ветке feature, а C' в main — его копия.
Когда использовать cherry-pick #
Срочное исправление в production. Разработчик нашёл и исправил баг в feature-ветке. Исправление нужно срочно в main, но вся feature ещё не готова.
Перенос фикса между версиями. Проект поддерживает несколько версий (v1.x и v2.x). Исправление из v2 нужно применить и к v1.
Случайно закоммитили в неправильную ветку. Коммит сделан в main вместо feature. Можно cherry-pick в нужную ветку, затем отменить в main.
Выборочный перенос функций. Нужно взять только часть работы из большой ветки.
Простой cherry-pick одного коммита #
Сначала нужно узнать SHA-хеш нужного коммита:
# Посмотреть историю ветки feature
git log --oneline feature
# Вывод:
# d5e6f7g Исправить SQL-инъекцию в форме входа
# c3d4e5f Добавить тесты для авторизации
# b2c3d4e Рефакторинг модуля авторизации
Теперь применить нужный коммит к текущей ветке:
# Переключиться на целевую ветку
git checkout main
# или
git switch main
# Cherry-pick конкретного коммита
git cherry-pick d5e6f7g
# Вывод при успехе:
# [main a1b2c3d] Исправить SQL-инъекцию в форме входа
# 1 file changed, 3 insertions(+), 2 deletions(-)
Git создал новый коммит a1b2c3d в ветке main с теми же изменениями, что и d5e6f7g в ветке feature.
Cherry-pick нескольких коммитов #
Можно перенести несколько коммитов за один раз — перечислив их или указав диапазон.
# Cherry-pick нескольких отдельных коммитов (по очереди)
git cherry-pick a1b2c3d b2c3d4e c3d4e5f
# Cherry-pick диапазона коммитов (исключающий — a1b2c3d не включается)
git cherry-pick a1b2c3d..e4f5g6h
# Cherry-pick диапазона коммитов (включающий — оба конца включаются)
git cherry-pick a1b2c3d^..e4f5g6h
Разница между a..b и a^..b: первый вариант не включает коммит a, второй — включает.
Cherry-pick без автоматического коммита #
По умолчанию cherry-pick сразу создаёт коммит. Если нужно проверить изменения или объединить с другими — используйте флаг -n (--no-commit):
# Применить изменения, но не коммитить
git cherry-pick -n a1b2c3d
# Теперь изменения в индексе, можно проверить
git status
git diff --staged
# Можно дополнить изменениями
# Затем закоммитить вручную
git commit -m "Применить исправление безопасности и улучшить логирование"
Опция -n особенно полезна при переносе нескольких коммитов с объединением в один:
git cherry-pick -n a1b2c3d b2c3d4e c3d4e5f
git commit -m "Применить все исправления безопасности"
Редактирование сообщения коммита #
При cherry-pick можно изменить сообщение коммита:
# Открыть редактор для изменения сообщения
git cherry-pick --edit a1b2c3d
# или
git cherry-pick -e a1b2c3d
Добавление подписи (signoff) #
Опция --signoff добавляет строку Signed-off-by: в сообщение коммита — полезно для проектов, требующих явного подтверждения авторства:
git cherry-pick --signoff a1b2c3d
# Коммит получит в сообщении:
# Signed-off-by: Ваше Имя <[email protected]>
Разрешение конфликтов при cherry-pick #
Если изменения в коммите конфликтуют с текущим состоянием ветки, Git остановится и сообщит о конфликте:
git cherry-pick a1b2c3d
# error: could not apply a1b2c3d... Исправить баг авторизации
# hint: after resolving the conflicts, mark the corrected paths
# hint: with 'git add <paths>' or 'git rm <paths>'
# hint: and commit the result with 'git commit'
# Проверить конфликтные файлы
git status
# Открыть файлы, разрешить конфликты (убрать маркеры <<<, ===, >>>)
# Затем добавить исправленные файлы
git add src/auth.js
# Продолжить cherry-pick
git cherry-pick --continue
# Или полностью отменить cherry-pick
git cherry-pick --abort
Просмотр непенесённых коммитов #
Команда git cherry (без -pick) показывает, какие коммиты из одной ветки ещё не применены в другой:
# Показать коммиты из feature, которых нет в master
git cherry -v master feature
# Вывод:
# + a1b2c3d Добавить функцию X (+ = не перенесён)
# - b2c3d4e Исправить баг Y (- = уже есть в master)
# + c3d4e5f Добавить тесты (+ = не перенесён)
Знак + означает коммит, которого нет в целевой ветке. Знак - — коммит уже присутствует (возможно, через предыдущий cherry-pick).
Практические примеры #
# Перенести исправление безопасности из feature в main
git log --oneline feature | grep security
# a1b2c3d Исправить SQL-инъекцию
git switch main
git cherry-pick a1b2c3d
# Перенести несколько коммитов
git cherry-pick a1b2c3d b4c5d6e c5d6e7f
# Перенести диапазон включительно
git cherry-pick a1b2c3d^..e4f5g6h
# Проверить изменения перед коммитом
git cherry-pick -n a1b2c3d
git diff --staged
git commit -m "Применить исправление"
# Cherry-pick из удалённой ветки
git fetch origin
git cherry-pick origin/hotfix/critical-bug a1b2c3d
# Отменить неудачный cherry-pick
git cherry-pick --abort
# Если уже закоммитили неверный cherry-pick
git revert HEAD
# Посмотреть, что изменил коммит перед cherry-pick
git show a1b2c3d
Отмена cherry-pick #
Если cherry-pick ещё не завершён (прервался из-за конфликта):
git cherry-pick --abort
Если cherry-pick уже завершён и нужно отменить коммит:
# Отменить через revert (создаст новый отменяющий коммит)
git revert a1b2c3d # хеш нового коммита cherry-pick, не исходного
# Или через reset (если коммит последний и не опубликован)
git reset --hard HEAD~1
Часто задаваемые вопросы #
Какая разница между cherry-pick и merge? Merge объединяет всю ветку. Cherry-pick переносит конкретные коммиты. Merge сохраняет полную историю ветки, cherry-pick создаёт копии выбранных коммитов.
Что произойдёт, если сделать cherry-pick коммита, который уже есть в текущей ветке? Git создаст дублирующий коммит с теми же изменениями. Это обычно нежелательно. Команда git cherry -v помогает проверить, каких коммитов ещё нет в ветке.
Как разрешить конфликты при cherry-pick? Так же, как при merge: открыть конфликтный файл, найти маркеры <<<<<<<, =======, >>>>>>>, выбрать нужную версию, удалить маркеры, git add <file>, затем git cherry-pick --continue.
Можно ли cherry-pick в обратном направлении (отменить изменения)? Да, с опцией --reverse или git revert. Это применит обратные изменения из коммита.
Когда НЕ следует использовать cherry-pick? Когда нужно объединить целые ветки — лучше merge или rebase. Если коммитов много и они связаны — cherry-pick каждого неудобен. Cherry-pick также затрудняет отслеживание происхождения изменений в истории.
Заключение #
git cherry-pick — инструмент для точечного переноса изменений. Он незаменим для срочных исправлений, синхронизации между ветками поддержки и исправления ошибок при коммите в неправильную ветку. Для повседневного объединения веток предпочтительнее git merge или git rebase.