Git cherry-pick: копия, а не перенос
Один коммит, диапазон со включённым стартом, конфликт посреди последовательности — и черта pick-vs-merge-vs-rebase, закрывающая спор.
■ пространство · прозовая полоса
git cherry-pick <sha> копирует один коммит — тот же патч, новый SHA, ничего не двигается.
Диапазон исключает свой старт: A^..B включает A, A..B молча его пропускает.
Остановило на конфликте? Разрешите, git add, git cherry-pick --continue — до тех пор вы посреди последовательности.
КОПИЯ, НАРИСОВАННАЯ
Один фикс живёт в feature-ветке; main нужен сейчас. Pick переигрывает патч, чеканит новый SHA и оставляет оригинал точно где он был:
рис. — копия, не перенос: feature-ветку удаляйте когда угодно; скопированная версия имеет собственный SHA и не зависит от старой
ТРИ ШАГА
Найти SHA, переключиться на целевую ветку, pick — приземлится там, куда указывает HEAD:
# 1. locate the commit on the source branch
git log feature/payment-fix --oneline -5
# 2. switch to the branch that should receive it
git switch main
# 3. copy it over
git cherry-pick 1a2b3c4
verify: git log -1 --stat → скопированное изменение, новый SHA, на целевой ветке
Две удобности, о которых стоит знать: git cherry-pick <branch> берёт tip этой ветки — удобно, но легко сделать случайно, держа устаревшую картину в голове о том, что такое «tip». А после любого pick, git log -1 --stat подтверждает, что приземлилось: секунда чтения, экономит revert.
НЕСКОЛЬКО КОММИТОВ
Три формы, по убыванию того, как часто они нужны — все работают через ветки, ведь SHA резолвятся против всего репозитория:
# 1. explicit list — picked in the order you list them
git cherry-pick 1a2b3c4 5d6e7f8
# 2. range — everything after A up to and including B
git cherry-pick A..B
# 3. range including A
git cherry-pick A^..B
verify: git log --oneline -4 после pick → оба коммита переиграны по порядку, старейший первым
Различие между A..B и A^..B — классический сюрприз: A..B исключает A. Представьте диапазон от git log, возьмите oldest..newest, и вы молча пропустите самый старый коммит — oldest^..newest убирает off-by-one. Чтобы сложить несколько pick в один коммит вместо трёх, ставьте в stage без коммита через -n / --no-commit, а затем один коммит:
git cherry-pick -n 1a2b3c4 5d6e7f8
git commit -m "Backport: payment retry fixes"
«ПОЧЕМУ НЕ РАБОТАЕТ» — ЧЕТЫРЕ ПРИЧИНЫ
- Конфликт остановил пик. Git применяет патч, упирается в строку, изменённую в обеих ветках, и замирает посреди последовательности. Разрешите файлы, затем
git add <resolved-files>иgit cherry-pick --continue— или--abort, чтобы вернуться в состояние до pick.--continueне опционален и не подразумевается: пока вы его не запустили,git statusпродолжает говорить You are currently cherry-picking. - Пик пуст («The previous cherry-pick is now empty») — изменение уже есть в этой ветке, часто от более раннего pick или сквошнутого merge. Пропустите с
git cherry-pick --skip, или форсируйте пустой маркер с--allow-empty. - Коммит — это merge-коммит. У merge два родителя, поэтому «примени этот diff» неоднозначно — git отказывается, а не гадает. Скажите, против какого родителя вы диффите:
git cherry-pick -m 1 <merge-sha>. Родители нумеруются в порядке записи: parent 1 — ветка, на которой вы были при merge, parent 2 — пришедшая ветка —-m 1— почти всегда то, что хочет backport. - Неверное рабочее дерево или detached HEAD. Пик приземляется там, куда указывает HEAD.
git branch --show-currentперед pick; если ничего не печатается — вы в detached HEAD, и коммит станет сиротой при переключении ветки.
PICK VS MERGE VS REBASE
| Команда | Что приземляется на цель | История | Когда применять |
|---|---|---|---|
git cherry-pick <sha> | Только названные коммиты | Копия, новые SHA | Конкретный фикс нужно перенести сейчас |
git merge <branch> | Всё на ветке | Merge-коммит или fast-forward | Нужна вся ветка, расхождение видно |
git rebase <base> | Все коммиты ветки, переигранные | Линейно, SHA написаны заново | Нужна ветка как чистая линейная полоса |
git revert <sha> | Обратное коммита | Добавляет undo-коммит | Приземлившийся коммит нужно отменить на общей истории |
Правило в одну строку: cherry-pick переносит выборку; merge и rebase переносят всё. Тянуться к cherry-pick ради «синхронизации» с веткой — признак того, что вам на самом деле нужен merge. Привычка, которую стоит пропустить: долгосрочно пикать один и тот же коммит в несколько веток — каждый будущий фикс исходной ветки потребует нового pick, а ветки в итоге разъедутся. Backports в release-ветки — нормальный паттерн; постоянная параллельная вселенная — нет.
WORKFLOW, СЖАТО
git log <source-branch> --oneline -5 # find the SHA(s)
git switch <target-branch> # land in the right place
git cherry-pick A^..B # range, list, or single SHA
# on conflict: resolve → git add → git cherry-pick --continue
git log -1 --stat # confirm what landed
Найти, переключиться, пик, проверить. Команда прослыла опасной зря — diff либо накатится, либо остановится и скажет почему. Единственная по-настоящему разрушительная ошибка — пикать не в ту ветку, и git log -1 перед push — и ошибка уже не проскочит.
A..B исключает A), поведение пустого пика и -m нумерация родителей — документированное поведение git, набрано с пиков, которые реально приземлились.конец страницы пика · ничего не скачано · всё локально