Що таке git cherry-pick і коли його використовувати?
git cherry-pick копіює конкретний коміт з однієї гілки і застосовує його до поточної гілки як новий коміт.
Теорія
TL;DR
- Cherry-pick - це як зірвати одну вишню з дерева, не трясучи весь кущ: береш рівно той один коміт, нічого зайвого.
- Створює новий коміт з тими самими змінами, повідомленням і автором, але з новим хешем і твоєю гілкою як батьківською.
- Головна різниця від merge: merge тягне всю історію гілки, cherry-pick бере diff тільки одного коміту.
- Правило вибору: потрібно 1-3 коміти вибірково? Cherry-pick. Більше 5? Краще merge або rebase.
Швидкий приклад
# В main є bugfix, develop відстає
git log --oneline main
# a1b2c3d Fix login crash <- потрібне це
# f4e5d6c Initial commit
git checkout develop
git cherry-pick a1b2c3d
git log --oneline develop
# b7c8d9e Fix login crash <- новий хеш, ті самі зміни
# f4e5d6c Initial commitНовий коміт на develop має той самий message і diff, але інший хеш. Оригінальний коміт залишається на main без змін.
Як працює cherry-pick
Git витягує diff з цільового коміту (точний патч змін) і застосовує його до робочої директорії через three-way merge. Якщо все підходить чисто, Git робить коміт автоматично, зберігаючи оригінального автора, дату і повідомлення. Якщо є конфлікти, зупиняється і дає їх вирішити. Саме через three-way merge cherry-pick справляється з переміщеними рядками краще ніж звичайний git apply.
Коли використовувати
- Security hotfix на release-гілку: патч є в
main, продакшн наrelease/v4.17, і потрібно доставити один коміт туди прямо зараз. - Бекпорт конкретного bugfix: стабільна гілка отримує одне виправлення з
mainбез незакінчених фіч. - Витягнути один коміт з feature-гілки: решта фічі не готова, але один коміт вже можна шипити.
- Коли не варто: якщо потрібно 5+ комітів. Cherry-pick великого діапазону створює дублікати в історії і ускладнює майбутні merge. Використовуй
git mergeабоgit rebase.
Типові помилки
Помилка 1: Cherry-pick merge-коміту без -m
git cherry-pick M
# fatal: commit M is a merge but no -m option givenMerge-коміт має двох батьків. Git не знає, з якого боку брати diff. Виправлення:
git cherry-pick -m 1 M # -m 1 = перший (mainline) батькоПомилка 2: git commit після конфлікту замість --continue
# Вирішив конфлікт, і потім:
git add .
git commit -m "Fixed" # Неправильно - губляться метадані cherry-pick
# Правильно:
git add .
git cherry-pick --continueПомилка 3: Неправильний синтаксис діапазону
git cherry-pick A..C # Застосовує B і C, пропускає A
git cherry-pick A^..C # Застосовує A, B і C - це зазвичай і потрібноДіапазон A..C виключає A. Додай ^, щоб включити його.
Помилка 4: Короткий хеш у великих репозиторіях
У репозиторіях з довгою історією (як Linux kernel) короткі хеші можуть збігатися. Перевіряй перед cherry-pick:
git log --oneline | grep a1b2c3
git cherry-pick a1b2c3d4e5f6 # використовуй повний хеш якщо є сумнівиДе зустрічається
- Kubernetes: cherry-pick патчі безпеки (CVE) з
mainнаrelease-1.28без зайвих змін. - Node.js: застосовує виправлення вразливостей у crypto до стабільної
v18.xзmain(наприклад, реліз v18.17.1). - React: портує точкові виправлення продуктивності з
canaryнаstable/18. - Linux kernel: бекпортить виправлення драйверів на стабільні гілки діапазонами комітів.
Питання на співбесіді
Q: Яка різниця між git cherry-pick і git revert?
A: Cherry-pick застосовує коміт вперед, додаючи його зміни до гілки. Revert створює новий коміт, що скасовує зміни попереднього. Revert безпечніший на спільних гілках, бо не переписує історію.
Q: Як cherry-pick-нути діапазон комітів включно з першим?
A: git cherry-pick A^..B. Синтаксис A..B виключає A, тому додаєш ^ щоб включити його.
Q: Що відбувається з автором і датою оригінального коміту?
A: Вони зберігаються в новому коміті. Committer і committer date відображають тебе і поточний час.
Q: Коли cherry-pick спрацює там, де git apply впаде?
A: Cherry-pick використовує three-way merge, тому обробляє випадки де навколишній код змістився або переїхав. git apply - це простий патч, який падає якщо рядки контексту не збігаються точно.
Q: Чому в монорепо зі строгим відстеженням залежностей команди можуть надавати перевагу cherry-pick над merge для hotfix?
A: Merge тягне весь граф гілки, додаючи шум в історію залежностей і ускладнюючи роботу автоматизованих сканерів. Cherry-pick копіює тільки diff, тримаючи граф комітів лінійним і зміну прив'язаною до одної точки.
Приклади
Базовий: Hotfix на release-гілку
# Security fix потрапив в main
git checkout main
git log --oneline -3
# a1b2c3d Fix: validate req.body size (prevents DoS)
# ...
# Застосовуємо на production release-гілку
git checkout release/v4.17
git cherry-pick a1b2c3d
# Перевіряємо
git log --oneline -2
# f9e8d7c Fix: validate req.body size (prevents DoS)
# 3a2b1c0 Previous release commitОдна команда, один коміт. Release-гілка отримує виправлення без решти змін з main. Це найпоширеніший сценарій cherry-pick у продакшн-кодових базах.
Проміжний: Вирішення конфлікту під час cherry-pick
git checkout feature-stable
git cherry-pick b2c3d4e
# CONFLICT (content): Merge conflict in src/auth.js
# Відкрий src/auth.js, вирісши маркери конфлікту
# Потім:
git add src/auth.js
git cherry-pick --continue
# [feature-stable e5f6g7h] Add token expiry checkЯкщо передумав - git cherry-pick --abort повертає гілку до стану до початку операції. Я сам кілька разів ішов на abort, коли конфлікт ставало зрозуміло: коміт залежить від змін, яких у цільовій гілці просто ще немає.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.