Олег Марков
Извлечение изменений - git cherry-pick
Введение
Извлечение изменений в Git через команду git cherry-pick позволяет вам аккуратно перенести один или несколько коммитов из одной ветки в другую, не сливая ветки целиком. Это особенно полезно, когда вы хотите:
- забрать один фикс из feature-ветки в рабочую ветку;
- перенести критический багфикс в релизную ветку;
- аккуратно взять пару полезных коммитов из экспериментальной ветки, не затрагивая остальной «шум».
Смотрите, я покажу вам, как это работает на практике, а также какие подводные камни возникают чаще всего. Мы разберем синтаксис, типичные сценарии использования, работу с конфликтами и дополнительные опции, которые облегчают жизнь.
Что такое git cherry-pick и чем он отличается от merge и rebase
Основная идея cherry-pick
Команда git cherry-pick берет один конкретный коммит (или последовательность коммитов) и «повторяет» его изменения в текущей ветке. При этом:
- создается новый коммит с новыми хешами;
- содержимое изменений, как правило, совпадает (за исключением возможных конфликтов и ручных правок);
- история ветки остаётся линейной, вы не добавляете merge-коммитов.
Говоря проще, вы как бы говорите Git: «Возьми вот этот коммит и сделай то же самое здесь».
Отличия от merge
Давайте разберемся:
git merge:
- объединяет две ветки;
- переносит все недостающие коммиты целиком;
- часто создает merge-коммит;
- сохраняет структурированную историю ветвлений.
git cherry-pick:
- переносит только указанные коммиты;
- не объединяет ветки логически;
- создает новые коммиты, которые повторяют изменения;
- история становится более «линейной» и локально измененной.
Поэтому cherry-pick используют, когда merge «слишком много берет» и вам нужно только несколько конкретных изменений.
Отличия от rebase
С rebase cherry-pick роднит то, что оба инструмента создают новые коммиты, изменяя историю:
- rebase переписывает серию коммитов, как будто вы сделали их от другой базы;
- cherry-pick позволяет избирательно переносить отдельные коммиты куда нужно.
Часто используют сочетание: сначала rebase в своей ветке, затем cherry-pick нужных коммитов в релизную ветку.
Базовый синтаксис git cherry-pick
Подготовка окружения
Перед работой с cherry-pick важно:
- Убедиться, что рабочее дерево чистое.
git status
# Проверяем, что нет незакоммиченных изменений
Если есть незакоммиченные изменения, лучше их либо закоммитить, либо отложить (stash), чтобы не получить конфликт рабочей директории с применяемыми изменениями.
- Переключиться на ветку, куда вы хотите перенести изменения.
git checkout main
# Переходим в целевую ветку, например main
Простейший пример cherry-pick одного коммита
Теперь давайте посмотрим базовую команду:
git cherry-pick <hash-коммита>
# Например
git cherry-pick a1b2c3d4
Что происходит:
- Git находит коммит с указанным хешем;
- применяет его дифф к текущей ветке;
- создает новый коммит с теми же изменениями, но новым хешем;
- если есть конфликты, процесс останавливается до их ручного решения.
Вы увидите новый коммит в истории текущей ветки:
git log --oneline --graph -5
# Смотрим последние 5 коммитов в компактном виде
Извлечение одного коммита: пошаговый пример
Представьте, что у вас есть две ветки:
- main — основная ветка;
- feature-x — ветка, где ведется разработка новой фичи.
В ветке feature-x вы сделали багфикс, который нужен в main, но переносить всю фичу рано.
Шаг 1. Найти нужный коммит
Сначала смотрим историю в feature-x:
git checkout feature-x
# Переходим в ветку с нужным коммитом
git log --oneline
# Отображаем историю этой ветки
Предположим, вы видите:
9f8e7d6 Исправлен баг с валидацией формы
1a2b3c4 Черновик новой фичи
...
Вас интересует коммит 9f8e7d6.
Шаг 2. Переключиться в целевую ветку
git checkout main
# Переходим в ветку, куда будем переносить фикс
Шаг 3. Выполнить cherry-pick
git cherry-pick 9f8e7d6
# Переносим фикс в main
Если конфликта нет — Git сам создаст новый коммит в main. Теперь вы можете отправить его в удаленный репозиторий:
git push origin main
# Публикуем изменения
Так вы забрали только нужный багфикс без остальной незаконченной работы из feature-x.
Извлечение нескольких коммитов
Часто требуется забрать не один, а несколько связанных коммитов: например, фиксы и доработки, которые логически принадлежат одному багу или фиче.
Последовательность коммитов (диапазон)
Если коммиты идут подряд в истории, можно взять их диапазоном. Давайте разберемся на примере:
История ветки feature-x:
d4d4d4d Рефакторинг валидации
c3c3c3c Логирование ошибок валидации
b2b2b2b Исправление ошибки валидации
a1a1a1a Начальная реализация фичи
Вы хотите перенести три верхних коммита: b2b2b2b..d4d4d4d.
Теперь вы увидите, как это выглядит в команде:
git checkout main
# Переходим в основную ветку
git cherry-pick b2b2b2b^..d4d4d4d
# Переносим все коммиты от b2b2b2b включительно до d4d4d4d
Почему b2b2b2b^..d4d4d4d:
- запись
X^..Yозначает «от родителя X до Y включительно»; - так первый коммит в диапазоне тоже попадает в набор.
Альтернатива, если вы точно знаете базу:
git cherry-pick a1a1a1a..d4d4d4d
# Переносит все коммиты после a1a1a1a до d4d4d4d включительно
Обратите внимание, как диапазон влияет на то, какие изменения попадут в целевую ветку. Лучше дважды проверить log перед выполнением.
Неподряд идущие коммиты
Если нужные коммиты не идут подряд, cherry-pick можно запустить сразу с несколькими хешами:
git checkout main
# Переходим в целевую ветку
git cherry-pick d4d4d4d f1f1f1f 9e9e9e9
# Переносим три отдельных коммита
Git применяет их по очереди в указанном порядке. Если на каком-то из них возникнет конфликт, процесс остановится до его решения.
Работа с конфликтами при cherry-pick
Конфликты — обычная часть работы с cherry-pick, особенно если код уже изменился в целевой ветке.
Как выглядит конфликт
После выполнения cherry-pick вы можете увидеть сообщение:
error: could not apply 9f8e7d6... Исправлен баг с валидацией формы
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and then run 'git cherry-pick --continue'
Теперь давайте посмотрим, что происходит в файлах. Например, в конфликтах вы увидите примерно следующее:
<<<<<<< HEAD
// Текущая версия кода в ветке main
validateInput(input string) bool {
// Старая логика валидации
}
=======
// Версия кода из cherry-picked коммита
validateInput(input string) bool {
// Новая логика валидации
}
>>>>>>> 9f8e7d6 (Исправлен баг с валидацией формы)
Маркировки:
- блок между
<<<<<<< HEADи=======— код, который сейчас в вашей ветке; - блок между
=======и>>>>>>> <hash>— код из применяемого коммита.
Шаги решения конфликта
- Открыть файл в редакторе и вручную выбрать или объединить нужные части кода.
// Пример — вы оставили обновленную версию
func validateInput(input string) bool {
// Новая логика валидации
// Здесь мы добавляем дополнительные проверки длины и формата
}
Удалить все конфликтные маркеры (
<<<<<<<,=======,>>>>>>>).Добавить файл в индекс:
git add path/to/file.go
# Помечаем файл как решенный
- Продолжить процесс cherry-pick:
git cherry-pick --continue
# Git создаст коммит с учетом ваших правок
Если вы решили, что хотите отменить этот cherry-pick целиком, можно сделать так:
git cherry-pick --abort
# Откатываемся к состоянию до cherry-pick
Важные опции git cherry-pick
Опция -n или --no-commit
По умолчанию cherry-pick сразу создает новый коммит. Но иногда вам нужно:
- применить изменения;
- вручную доработать код;
- возможно объединить несколько изменений в один коммит.
Тогда удобно использовать --no-commit:
git cherry-pick -n a1b2c3d
# Применяем изменения из коммита, но НЕ создаем новый коммит автоматически
Далее вы можете:
- поменять код;
- добавить/удалить файлы;
- собрать один аккуратный коммит:
git commit -m "Перенос и доработка фикса валидации"
# Создаем свой собственный коммит с перенесенными и доработанными изменениями
Это хороший способ не засорять историю мелкими техническими коммитами.
Опция -x — пометка источника коммита
Если вы переносите изменения из одной ветки в другую, часто полезно явно указать, откуда коммит взят. Для этого есть -x:
git cherry-pick -x 9f8e7d6
# Создаст коммит с доп. строкой в сообщении
Сообщение коммита будет примерно таким:
Исправлен баг с валидацией формы
(cherry picked from commit 9f8e7d6abc123...)
Это помогает отслеживать, какие изменения были перенесены и откуда. Полезно в больших командах и репозиториях.
Опция -e или --edit
Если вы хотите сразу изменить сообщение коммита:
git cherry-pick -e 9f8e7d6
# Откроется редактор для редактирования сообщения коммита
Здесь вы можете:
- адаптировать описание под контекст целевой ветки;
- добавить пометки о релизе;
- уточнить, что это backport или хотфикс.
Опция -s или --signoff
Эта опция добавляет строку «Signed-off-by» в конец сообщения коммита:
git cherry-pick -s 9f8e7d6
# Добавляется подпись автора cherry-pick
Это широко используется в проектах с формальной политикой коммитов (например, в open source с DCO).
Классические сценарии использования cherry-pick
Сценарий 1. Перенос хотфикса в релизную ветку
Допустим, у вас есть:
- ветка
develop— основная разработка; - ветка
release/1.2— текущий релиз; - в
developпочинен критический баг, который нужно срочно забрать в релиз.
Шаги:
git checkout develop
# Ищем нужный коммит
git log --oneline
# Допустим, хотфикс — коммит 7f7f7f7
git checkout release/1.2
# Переходим в релизную ветку
git cherry-pick 7f7f7f7
# Переносим хотфикс
git push origin release/1.2
# Публикуем хотфикс
Здесь cherry-pick спасает от полноценного merge с develop, который может содержать незавершенные фичи.
Сценарий 2. Частичный перенос фичи
Вы разрабатываете фичу в отдельной ветке, и часть работы можно уже использовать, не дожидаясь полного завершения.
Предположим:
feature-searchсодержит 10 коммитов;- первые 3 коммита улучшают инфраструктуру, которую уже можно использовать в других задачах;
- остальное — unfinished UI.
Решение:
- Находите первые 3 коммита в логе.
- Переносите только их:
git checkout main
git cherry-pick c1c1c1c c2c2c2c c3c3c3c
# Забираете только инфраструктурные изменения
Так вы не тянете за собой недоделанный интерфейс.
Сценарий 3. Восстановление случайно удаленных изменений
Иногда коммит попадает в ветку по ошибке, затем его «убирают» через reset или rebase. Если у вас есть его хеш в reflog, вы можете вернуть изменения через cherry-pick.
Пример:
git reflog
# Смотрим историю локальных действий, даже если коммиты пропали из обычного лога
# Находим нужный хеш, например 3a3a3a3
git checkout main
git cherry-pick 3a3a3a3
# Восстанавливаем потерянные изменения
Здесь cherry-pick выступает как инструмент «воскрешения» полезного коммита.
Как не сломать историю репозитория, используя cherry-pick
Внимательно относитесь к публичным веткам
Если коммит:
- уже попал в общую ветку (например, main);
- уже был вытолкнут на удаленный репозиторий;
- другие разработчики уже на него ссылаются,
то бессистемное использование cherry-pick может привести к дублированию изменений и путанице в истории.
Пример проблемы:
- В ветке
developесть коммит A. - Вы сделали
git cherry-pick Aвmain. - Потом кто-то сделал
git merge developвmain. - В результате изменения из A могут прилететь второй раз (хотя Git часто умно их распознаёт, но не всегда однозначно).
Лучше:
- стараться использовать cherry-pick либо для backport’ов в релизные/поддерживающие ветки;
- либо для переноса из временных веток в основные, но с пониманием, как ветки потом будут объединяться.
Избегайте избыточного дробления истории
Если вы переносите серию коммитов, где:
- половина — полезные;
- половина — «фиксы фиксов» и временные правки;
есть смысл:
- В исходной ветке сделать rebase interactive и привести историю в порядок.
- Только после этого cherry-pick’ать аккуратные коммиты.
Это сделает историю в целевой ветке чище и понятнее.
Отличия cherry-pick от revert — что когда использовать
Иногда cherry-pick путают с revert. Давайте разберемся, чтобы вы выбирали подходящий инструмент.
git cherry-pick
- переносит изменения из другого коммита в текущую ветку;
- создает новый коммит с теми же изменениями;
- используется для добавления функциональности или фиксов.
git revert
- создаёт коммит, который отменяет изменения другого коммита;
- не удаляет коммит из истории, а накладывает обратный дифф;
- используется, чтобы «откатить» уже попавший в историю коммит без переписывания истории.
Пример:
git revert a1b2c3d
# Создает новый коммит, возвращающий состояние так, будто a1b2c3d не применялся
Если вы хотите взять изменение из одной ветки в другую — используйте cherry-pick. Если хотите отменить уже совершенный коммит в текущей ветке — используйте revert.
Практический пример: полный цикл с конфликтами и доработкой
Давайте разберемся на примере сценария с конфликтами и доработкой через --no-commit.
Условия:
- В ветке
feature-xесть коммитfix-form, который правит валидацию формы. - В ветке
mainуже изменили форму по-своему, поэтому просто перенести fix не получится без доработок.
Шаг 1. Cherry-pick с опцией --no-commit
git checkout main
# Переходим в основную ветку
git cherry-pick --no-commit fix-form
# Применяем изменения из коммита, но не создаем новый коммит
Если появляются конфликты — Git остановится и пометит конфликтующие файлы.
Шаг 2. Ручное объединение логики
Открываем проблемный файл, видим конфликтные блоки и объединяем логику:
// До конфликта было два варианта кода, теперь вы вручную объединяете:
// Теперь вы увидите, как это выглядит в коде
func validateForm(data FormData) error {
// Сначала проверяем формат полей
if err := validateFormat(data); err != nil {
return err
}
// Затем применяем новую логику из cherry-picked коммита
if err := checkBusinessRules(data); err != nil {
// Здесь мы используем обновленную бизнес-логику
return err
}
return nil
}
Комментарии помогают понять, как вы смешали старую и новую логику.
Шаг 3. Завершение и единый коммит
git add path/to/form_validation.go
# Помечаем файл как решенный
git status
# Проверяем, все ли конфликты решены
git commit -m "Правки валидации формы с учетом обновленной логики main"
# Создаем один аккуратный коммит с перенесенными и доработанными изменениями
Вы не просто перенесли коммит, а адаптировали его к новой реальности ветки main.
Когда cherry-pick использовать не стоит
Хотя инструмент полезный, есть набор ситуаций, где лучше отказаться от него.
Массовый перенос большого набора коммитов
Если нужно перенести десятки или сотни коммитов из одной ветки в другую, вероятнее всего лучше:
- сделать merge;
- либо сделать rebase ветки на нужную базу.
Cherry-pick такого масштаба:
- занимает много времени;
- повышает риск конфликтов;
- затрудняет поддержку (история становится очень запутанной).
Если ветки всё равно скоро будут мержиться
Представьте, что:
- вы используете Git Flow или похожий процесс;
- ветка
developчерез день-два будет слита вmain.
Если вам не критично получить изменения прямо сейчас, проще дождаться merge, чем:
- сейчас cherry-pick’ать;
- а потом еще и merge’ить.
Иначе вы можете получить дубликаты и лишние сложности.
Когда изменения зависят от других коммитов
Если коммит, который вы хотите перенести:
- опирается на предыдущее изменение (например, использует новый модуль или функцию);
- а это предыдущее изменение вы не переносите,
то cherry-pick может привести к:
- некомпилирующемуся проекту;
- скрытым логическим ошибкам.
В таких случаях лучше:
- либо переносить всю цепочку зависимых коммитов;
- либо выделить изменение так, чтобы оно было самодостаточным.
Заключение
git cherry-pick — это инструмент точечного переноса изменений между ветками. Он позволяет:
- забирать конкретные фиксы и доработки, не трогая остальную историю;
- делать аккуратные backport’ы в релизные и поддерживаемые ветки;
- восстанавливать потерянные коммиты.
При этом важно:
- тщательно следить за конфликтами и корректно их решать;
- понимать последствия дублирования изменений при последующем merge;
- использовать дополнительные опции (
-n,-x,-e,-s), когда нужно более тонкое управление.
Давайте еще раз коротко зафиксируем:
- используйте cherry-pick для выборочного переноса отдельных коммитов;
- не применяйте его для массовых переносов, где merge или rebase будут лучше;
- помечайте источник коммита через
-x, если важно отслеживать историю; - не забывайте про
--abort, если хотите безопасно отменить неудачный cherry-pick.
Частозадаваемые технические вопросы по теме и ответы
Как отменить уже выполненный cherry-pick, если я его закоммитил и заметил ошибку?
Если вы еще не отправили изменения в удаленный репозиторий, можно сделать reset:
git reset --hard HEAD~1
# Убираем последний коммит, созданный cherry-pick
Если коммит уже опубликован, безопаснее использовать revert:
git revert <hash-cherry-picked-коммита>
# Создаем коммит, отменяющий изменения, не ломая историю
Как понять, был ли конкретный коммит уже cherry-pick’нут в текущую ветку?
Можно использовать git log с поиском по строке (cherry picked from commit ...), если вы применяли опцию -x:
git log --grep="cherry picked from commit" -i
# Ищем коммиты, помеченные как cherry-picked
Если -x не использовался, можно сравнить патчи:
git log -p <ветка-источник>..HEAD
# Смотрите, есть ли нужные изменения в диффах
Что делать, если я запустил cherry-pick сразу с несколькими коммитами и застрял на конфликте в середине?
После решения конфликта:
git add <файлы>
git cherry-pick --continue
# Продолжаем последовательность cherry-pick
Если хотите прекратить применение оставшихся коммитов и откатиться к началу:
git cherry-pick --abort
# Восстанавливаем состояние до серии cherry-pick
Как перенести cherry-pick в другую ветку, если уже начал и решил часть конфликтов?
Если вы начали cherry-pick в ветке A, но решили, что изменения должны идти в ветку B, безопаснее всего:
- Отменить текущий процесс:
git cherry-pick --abort
- Переключиться в ветку B:
git checkout B
- Снова выполнить cherry-pick нужных коммитов уже в правильной ветке.
Можно ли автоматизировать cherry-pick большого набора коммитов с пропуском конфликтных?
Частично да. Можно использовать опцию --skip, чтобы пропускать проблемные коммиты:
git cherry-pick <коммиты...>
# При конфликте:
git cherry-pick --skip
# Пропускаем проблемный коммит и продолжаем со следующего
Но помните, что пропущенные коммиты останутся неперенесенными, и их судьбу нужно будет решить отдельно, возможно вручную.
Постройте личный план изучения Git до уровня Middle — бесплатно!
Git — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Git
Лучшие курсы по теме

Основы Git
Антон Ларичев
TypeScript с нуля
Антон Ларичев