Пошук уроків, статей та іншого контенту
Перенесете вибрані коміти між гілками та розберетеся з наслідками cherry-pick для історії.
git cherry-pickgit cherry-pick застосовує зміни з одного або кількох комітів до поточної гілки.
На відміну від git merge, команда не об’єднує всю гілку. Вона бере зміни конкретного коміту, створює для них новий коміт і додає його до поточної гілки.
Наприклад:
main: A---B
\
feature: C---DЯкщо перейти на main і виконати:
git cherry-pick Cісторія стане приблизно такою:
main: A---B---C'
\
feature: C---DC' містить ті самі зміни, що й C, але це інший коміт з іншим ідентифікатором.
cherry-pickпереносить зміни коміту, а не сам коміт буквально.
Спочатку перейдіть у гілку, до якої потрібно додати зміни:
git switch mainПотім передайте ідентифікатор потрібного коміту:
git cherry-pick <commit-hash>Наприклад:
git cherry-pick a1b2c3dПовний хеш вказувати не обов’язково. Git зазвичай приймає достатню унікальну скорочену версію.
Щоб переглянути коміти та їхні скорочені хеші:
git log --onelineПриклад результату:
a1b2c3d Fix validation for email
e4f5g6h Add dashboard page
i7j8k9l Update dependenciesПісля виконання cherry-pick Git створить новий коміт у поточній гілці.
Уявімо, що у вас є гілки main і feature. У feature було виправлено помилку, але повністю зливати цю гілку в main поки не потрібно.
git switch feature
git log --onelineРезультат:
7f3a1c2 Fix incorrect total calculation
2b8d4e6 Add report page
91c0aa1 Create report structureПотрібно перенести лише виправлення 7f3a1c2:
git switch main
git cherry-pick 7f3a1c2Git застосує зміни та створить коміт у main. Перевірити результат можна так:
git log --oneline --decorateУ main з’явиться новий коміт, наприклад:
c9d4e11 Fix incorrect total calculation
5a6b7c8 Previous commit on mainНазва коміту зазвичай зберігається, але хеш буде іншим.
Можна вказати кілька хешів у потрібному порядку:
git cherry-pick 7f3a1c2 4e5d6f7Git застосує коміти послідовно.
Порядок може мати значення: якщо другий коміт залежить від змін першого, спочатку потрібно застосувати перший.
Для перенесення послідовності комітів використовуйте діапазон:
git cherry-pick A^..DЦя команда застосує коміти від A до D включно.
Символ ^ перед A потрібен тому, що запис A..D зазвичай не включає сам коміт A.
Наприклад, історія має вигляд:
A---B---C---DЩоб перенести B, C і D, виконайте:
git cherry-pick A^..DПеред застосуванням діапазону переконайтеся, що всі ці коміти справді потрібні в цільовій гілці.
Наведена послідовність команд створює невеликий репозиторій, додає коміти в окрему гілку, а потім переносить один із них у main.
#!/usr/bin/env bash
set -e
rm -rf cherry-pick-demo
mkdir cherry-pick-demo
cd cherry-pick-demo
git init
git config user.name "Demo User"
git config user.email "demo@example.com"
printf "version 1\n" > app.txt
git add app.txt
git commit -m "Initial version"
git switch -c feature
printf "version 1\nfeature enabled\n" > app.txt
git add app.txt
git commit -m "Enable feature"
printf "version 1\nfeature enabled\nbug fixed\n" > app.txt
git add app.txt
git commit -m "Fix bug"
git log --oneline --decorate --all
# Повертаємося до main і переносимо лише виправлення
git switch main
git cherry-pick feature
git log --oneline --decorate --all
cat app.txtУ цьому прикладі git cherry-pick feature використовує кінцевий коміт гілки feature. У результаті до main буде перенесено зміни останнього коміту цієї гілки.
У реальному проєкті краще явно вказувати хеш потрібного коміту, щоб не залежати від того, який коміт зараз є останнім у гілці:
git cherry-pick <hash-commit>Коміт Git містить не лише зміни файлів. Він також має:
батьківський коміт;
автора;
дату;
повідомлення;
посилання на стан дерева файлів.
Під час cherry-pick Git бере patch — різницю між вибраним комітом і його батьківським комітом — та застосовує її до поточного стану гілки.
Після цього створюється новий коміт із новим батьківським комітом. Через це:
хеш нового коміту відрізняється від оригінального;
у двох гілках можуть бути коміти з однаковим повідомленням і змінами, але різними хешами;
історія більше не є спільною в точці перенесеного коміту;
повторне перенесення того самого коміту може спричинити конфлікт або дублювання змін.
Наприклад, якщо коміт C перенесено з feature у main, Git не вважає C і C' одним і тим самим комітом:
feature: C
main: C'Зміст може бути однаковим, але ідентичність комітів різна.
-xЯкщо коміт переноситься між публічними гілками, іноді корисно зберегти посилання на оригінальний коміт у повідомленні:
git cherry-pick -x <commit-hash>Git додасть до повідомлення щось на зразок:
Fix incorrect total calculation
(cherry picked from commit 7f3a1c2...)Це допомагає зрозуміти, звідки походять зміни, коли команда переглядає історію.
Опцію -x зазвичай використовують, коли важливо явно відстежувати перенесення між гілками. Для локальних або тимчасових операцій вона може бути непотрібною.
cherry-pickКонфлікт виникає, коли зміни вибраного коміту не можна автоматично застосувати до поточного стану файлів.
Наприклад:
у цільовій гілці ті самі рядки вже були змінені;
потрібний файл видалено;
кілька гілок змінили один фрагмент коду по-різному.
Під час конфлікту Git зупиняє операцію:
error: could not apply 7f3a1c2... Fix incorrect total calculationПеревірити стан можна командою:
git statusУ конфліктному файлі Git залишить спеціальні маркери:
<<<<<<< HEAD
Зміст поточної гілки
=======
Зміст із cherry-pick-коміту
>>>>>>> 7f3a1c2Відредагуйте файл вручну: залиште правильний варіант і видаліть маркери конфлікту. Потім додайте виправлений файл до індексу:
git add path/to/fileПісля цього продовжте операцію:
git cherry-pick --continueЯкщо конфліктів кілька, повторюйте ці кроки для кожного з них.
Якщо ви зрозуміли, що вибраний коміт не потрібно переносити, скасуйте поточну операцію:
git cherry-pick --abortGit спробує повернути робоче дерево та поточну гілку до стану перед початком cherry-pick.
Під час перенесення діапазону іноді один коміт уже фактично присутній у цільовій гілці. Якщо його зміни не потрібно застосовувати, можна пропустити поточний коміт:
git cherry-pick --skipЦе завершить поточний крок і перейде до наступного коміту в операції.
cherry-pickcherry-pick доречний, коли потрібно перенести невелику кількість конкретних змін:
виправлення помилки з однієї гілки в стабільну;
окремий коміт із терміновим виправленням;
вибрану частину роботи, коли вся гілка ще не готова;
зміни між гілками, які мають різне призначення.
Наприклад, гілка feature може містити кілька незавершених комітів, але один коміт із виправленням уже можна безпечно перенести в main.
cherry-pick зручний, але ним не варто без потреби замінювати об’єднання гілок.
Пам’ятайте про такі наслідки:
Створюється новий коміт.
Його хеш не збігається з хешем джерельного коміту.
Історія може містити дублікати.
Одна й та сама зміна може бути представлена різними комітами в різних гілках.
Можуть виникнути конфлікти.
Особливо якщо цільова гілка вже сильно відрізняється від гілки-джерела.
Потрібно враховувати залежності.
Окремий коміт може залежати від попередніх комітів. Якщо перенести лише його, код може не працювати або конфліктувати.
Повторне перенесення не завжди безпечне.
Якщо зміни вже присутні в цільовій гілці, Git може повідомити про порожній коміт або зупинитися через конфлікт.
Перед перенесенням корисно переглянути коміт:
git show <commit-hash>Так ви побачите файли та рядки, які він змінює.
Також перевірте контекст історії:
git log --oneline --graph --decorate --allІноді після спроби cherry-pick Git повідомляє, що зміни вже застосовані:
The previous cherry-pick is now emptyЦе означає, що в поточній гілці вже немає нових змін для створення коміту.
Якщо цей коміт не потрібен як окремий запис, пропустіть його:
git cherry-pick --skipЯкщо потрібно все одно створити порожній коміт для фіксації факту перенесення, можна використати:
git commit --allow-empty -m "Apply previously integrated fix"Такий варіант варто використовувати лише тоді, коли порожній коміт має зрозумілу користь для історії.
Після успішного перенесення перевірте:
git status
git log --oneline --decorate -n 5За потреби перегляньте зміни останнього коміту:
git show HEADЯкщо проєкт має автоматичні перевірки, запустіть їх після cherry-pick. Навіть коли коміт успішно застосувався без конфліктів, його зміни могли взаємодіяти з іншим кодом у цільовій гілці.
# Переглянути історію та знайти потрібний коміт
git log --oneline --all --decorate
# Перевірити вміст коміту
git show <commit-hash>
# Перейти до цільової гілки
git switch main
# Оновити локальний стан перед роботою, якщо це потрібно у вашому процесі
git pull
# Перенести коміт
git cherry-pick <commit-hash>
# Перевірити результат
git status
git log --oneline -n 5Якщо виник конфлікт:
git status
# Виправити конфліктні файли
git add <виправлені-файли>
git cherry-pick --continueЯкщо операцію потрібно скасувати:
git cherry-pick --abortcherry-pick працює з поточною гілкою як із цільовою. Перед командою перевірте, де ви перебуваєте:
git branch --show-currentКоманда:
git cherry-pick featureпереносить коміт, на який зараз вказує feature. Якщо в гілці з’явиться новий коміт, результат буде іншим.
Для конкретної зміни краще використовувати її хеш:
git cherry-pick 7f3a1c2Коміт може спиратися на попередню зміну. Перед перенесенням перевірте сусідні коміти:
git log --oneline --stat <commit-hash>^..<commit-hash>Якщо потрібні кілька пов’язаних комітів, переносьте їх разом у правильному порядку.
Після ручного виправлення конфлікту недостатньо просто виконати git add. Перегляньте файл і запустіть перевірки проєкту перед публікацією результату.
Після конфлікту не запускайте git cherry-pick <hash> повторно без перевірки стану. Спочатку використайте один із варіантів:
git cherry-pick --continueабо:
git cherry-pick --abortІнакше можна заплутати стан незавершеної операції.
git cherry-pick переносить зміни вибраного коміту в поточну гілку.
Для перенесення використовують хеш коміту або діапазон комітів.
Результатом є новий коміт із новим хешем.
Оригінальний коміт і його копія можуть мати однакові зміни, але це різні об’єкти історії.
Під час конфлікту використовують git cherry-pick --continue, git cherry-pick --skip або git cherry-pick --abort.
Перед перенесенням перевіряйте вміст коміту та його залежності.
Опція -x додає до повідомлення нового коміту посилання на оригінальний коміт.