Перенос одного коммита (cherry-pick)
git cherry-pick переигрывает изменение одного коммита на текущую ветку как совершенно новый коммит с новым SHA. Это инструмент бэкпорта — взять одну правку, не сливая целую ветку. Конфликты решаются через --continue/--abort; берегись дубликатов, когда позже сделаешь merge.
Критическая правка только что легла в main, но релизной ветке release/2.3, которая ушла в прод неделю назад, тоже нужна именно эта правка — без сорока других несвязанных коммитов, что накопились в main с тех пор. Сливать main ты не хочешь; тебе нужен ровно один коммит, перенесённый через. Это и делает git cherry-pick.
К концу этого урока ты сможешь скопировать изменение любого коммита на текущую ветку, обработать конфликты посреди переноса и понять, почему у копии появляется новая идентичность — и когда это укусит тебя позже.
После этого урока ты сможешь с помощью git cherry-pick применить изменение одного или нескольких конкретных коммитов на текущую ветку, разрешать конфликты через --continue/--abort и объяснить, почему у перенесённого коммита появляется новый SHA и как это порождает дублирующиеся коммиты.
git cherry-pick <commit> применяет изменение, которое внёс этот коммит — его diff относительно родителя — на твою текущую ветку как новый коммит. Ты не перемещаешь коммит и не сливаешь его ветку; ты переигрываешь его патч прямо здесь:
git switch release/2.3
git cherry-pick 9f3c1a2 # применить diff коммита 9f3c1a2 здесьРезультат — свежий коммит на release/2.3, чьё содержимое совпадает с изменением в 9f3c1a2, с тем же автором и сообщением, но другим SHA коммита, потому что идентичность коммита включает его родителя и отметку времени — и то и другое теперь иное.
▸Почему это работает
Почему важен новый SHA: хеш коммита вычисляется по его дереву, родителю, автору/коммиттеру и сообщению. Cherry-pick даёт патчу нового родителя (вершину твоей ветки) и новую дату коммита, поэтому хеш неизбежно меняется. Два коммита эквивалентны по содержимому, но различны по идентичности. У git нет встроенной связи «это копия того» — и это корень проблемы дублирующихся коммитов, с которой ты встретишься в шаге 4.
Cherry-pick может конфликтовать, и тогда он встаёт на паузу, как мини-merge. Если целевая ветка разошлась достаточно, чтобы патч не лёг чисто, git останавливается и помечает конфликты. Ты их разрешаешь, индексируешь и продолжаешь — или полностью отступаешь:
git cherry-pick 9f3c1a2
# ... CONFLICT в src/auth.js ...
# правишь файл, затем:
git add src/auth.js
git cherry-pick --continue # завершить создание коммита
# или, чтобы выбросить всё и восстановить состояние до переноса:
git cherry-pick --abortЕсть ещё --skip, чтобы пропустить текущий коммит и идти дальше, когда переносишь диапазон.
-x записывает, откуда взялся коммит, и можно перенести диапазон. Для бэкпортов хорошая гигиена — оставить след. Флаг -x дописывает в сообщение строку вроде (cherry picked from commit 9f3c1a2), чтобы любой, читающий историю release/2.3, нашёл оригинал:
git cherry-pick -x 9f3c1a2
git cherry-pick A..B # перенести каждый коммит ПОСЛЕ A вплоть до B включительно
git cherry-pick A^..B # включить в диапазон сам AЗаметь, что диапазон A..B не включает A — частая ошибка на единицу. Используй A^..B, когда хочешь захватить и изменение из A.
Cherry-pick, а позже merge той же работы может породить дублирующиеся коммиты. Поскольку у копии новый SHA, git не распознаёт её как «уже применённую». Если ты cherry-pick’нул правку из feature на main, а потом слил feature в main, история может показать то же изменение дважды. Часто слияние git по содержимому замечает, что строки уже совпадают, и не даёт чистого diff, но записи коммитов-дубликаты остаются, засоряя историю и иногда вызывая конфликты. Правило большого пальца:
Cherry-pick = "Мне нужно ЭТО ОДНО изменение здесь, сейчас."
Merge = "Я хочу присоединить историю всей этой ветки."Если ты всё равно ожидаешь слить ветку позже, предпочти дождаться merge, а не делать cherry-pick — кроме случая, когда правка срочно нужна другой ветке прямо сейчас.
Бэкпорт фикса безопасности в релизную ветку.
Правка бага с утечкой токена — это коммит a1b2c3d на main. Живой релиз — release/4.1, ответвившийся недели назад. Ты переносишь только этот коммит:
git switch release/4.1
git cherry-pick -x a1b2c3d
# CONFLICT: main отрефакторил auth.js, на release ещё старая форма
# открываешь auth.js, применяешь правку к СТАРОЙ структуре, затем:
git add auth.js
git cherry-pick --continue
git push origin release/4.1release/4.1 теперь несёт правку как новый коммит, чьё сообщение заканчивается на (cherry picked from commit a1b2c3d), так что происхождение поддаётся аудиту. Сорок несвязанных коммитов на main остались на main. Когда release/4.1 в итоге сольют вперёд в main, содержимое уже совпадёт, поэтому слияние для этих строк будет пустым — хотя дублирующая запись коммита всё ещё может появиться в логе.
▸Частая ошибка
Cherry-pick не замена merge, когда тебе реально нужна полная история ветки, и он не беспоследственный лишь потому, что «копирует только один коммит». Команды, привычно перебрасывающие одни и те же правки туда-сюда между долгоживущими ветками, в итоге получают запутанные, набитые дубликатами истории, в которых трудно разобраться. Береги cherry-pick для настоящих разовых пересадок — срочный бэкпорт, вытащить один хороший коммит из заброшенной ветки — а не как рутинный способ таскать работу.
Ты делаешь cherry-pick коммита 9f3c1a2 из feature на main. Что верно про коммит, появившийся на main?
git cherry-pick <commit> переигрывает изменение, внесённое коммитом, на текущую ветку как новый коммит с новым SHA — оригинал не перемещается и не разделяется. Это инструмент бэкпорта: взять одну срочную правку, не сливая целую ветку. Конфликты ставят перенос на паузу, разрешаются через git add, затем --continue (или отменяются через --abort); -x записывает источник, а A..B переносит диапазон (не включая A). Поскольку у копии новая идентичность, cherry-pick, а потом merge той же работы может оставить дублирующиеся коммиты, так что береги его для настоящих разовых случаев. Дальше ты будешь помечать релизные точки в истории тегами.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.