Пошук уроків, статей та іншого контенту
Опрацюєте конфлікти під час rebase та навчитеся продовжувати, пропускати або скасовувати операцію.
git rebase переносить коміти поточної гілки на нову основу, відтворюючи їх один за одним.
Наприклад, команда:
git switch feature
git rebase mainнамагається застосувати коміти з feature поверх актуального стану main.
Конфлікт виникає, якщо Git не може автоматично застосувати черговий коміт. Найчастіше це трапляється, коли:
у поточній гілці та в основній гілці змінено ті самі рядки;
один коміт видалив файл, а інший його змінив;
структура файлів змінилася несумісним способом.
У разі конфлікту rebase призупиняється. Git не створює помилковий коміт автоматично, а чекає, поки ви вирішите конфлікт.
Нижче наведено невеликий приклад конфлікту. У ньому дві гілки змінюють один і той самий рядок.
Виконайте команди послідовно в окремій тимчасовій директорії:
mkdir rebase-conflict-demo
cd rebase-conflict-demo
git init
git config user.name "Demo User"
git config user.email "demo@example.com"
printf "mode=development\ntimeout=30\n" > config.env
git add config.env
git commit -m "Додати початкову конфігурацію"
git branch -M main
git switch -c feature
printf "mode=testing\ntimeout=30\n" > config.env
git add config.env
git commit -m "Налаштувати режим тестування"
git switch main
printf "mode=production\ntimeout=30\n" > config.env
git add config.env
git commit -m "Налаштувати режим продакшену"
git switch feature
git rebase mainОстання команда зупинить rebase через конфлікт. Вивід може містити повідомлення на кшталт:
CONFLICT (content): Merge conflict in config.env
error: could not apply ...Точний текст залежить від версії Git.
Першою командою після конфлікту має бути:
git statusGit покаже:
що зараз виконується rebase;
який коміт не вдалося застосувати;
які файли мають конфлікти;
які команди можна виконати далі.
Переглянути конфлікт можна безпосередньо у файлі:
cat config.envФайл матиме приблизно такий вигляд:
<<<<<<< HEAD
mode=production
=======
mode=testing
>>>>>>> 1234567 (Налаштувати режим тестування)
timeout=30Маркери мають таке значення:
<<<<<<< HEAD — початок першого варіанта;
======= — роздільник між варіантами;
>>>>>>> ... — кінець другого варіанта та назва коміту, який Git намагався застосувати.
Під час rebase важливо правильно розуміти, який варіант звідки походить:
варіант між <<<<<<< HEAD і ======= походить від нової основи, наприклад main;
варіант між ======= і >>>>>>> ... походить від коміту поточної гілки, який Git зараз відтворює.
У наведеному прикладі можна залишити значення, потрібне для функціональності застосунку. Наприклад:
mode=testing
timeout=30Після редагування у файлі не повинно залишитися маркерів конфлікту.
Після виправлення конфліктного файлу перегляньте його:
cat config.envТакож корисно перевірити різницю:
git diffКоманда git diff показує незастейджені зміни. Для перевірки помилкових пробілів або залишених маркерів можна виконати:
git diff --checkЯкщо результат правильний, позначте файл як вирішений:
git add config.envСтан можна перевірити ще раз:
git statusФайл має перейти до списку змін, підготовлених для наступного коміту.
Коли всі конфлікти поточного кроку вирішені та відповідні файли додані через git add, виконайте:
git rebase --continueGit створить нову версію коміту та спробує застосувати наступний коміт.
Під час виконання команда може відкрити редактор для підтвердження повідомлення коміту. Зазвичай достатньо залишити запропоноване повідомлення та закрити редактор.
Для прикладу без інтерактивного редактора можна використати:
GIT_EDITOR=true git rebase --continueОднак це варто робити лише тоді, коли повідомлення коміту вже правильне.
Rebase може зупинитися знову. Це нормально: кожен коміт застосовується окремо, і конфлікт може виникнути на кількох кроках.
Повторюйте послідовність:
git status
# Виправити конфлікти у файлах
git add <виправлені-файли>
git rebase --continueRebase завершено успішно, якщо Git повідомляє про завершення операції. Після цього перевірте стан:
git status
git log --oneline --graph --decorate --allЯкщо весь файл потрібно замінити одним із варіантів, можна використати git restore.
Під час rebase:
git restore --ours -- config.envзалишає версію файлу з боку нової основи.
Команда:
git restore --theirs -- config.envзалишає версію з коміту, який Git зараз відтворює.
Після цього файл потрібно додати до індексу:
git add config.env
git rebase --continueНазви ours і theirs під час rebase можуть здаватися неочевидними. У звичайному merge вони часто сприймаються інакше, тому не покладайтеся лише на назву. Перед вибором перегляньте вміст файлу та визначте, який варіант відповідає основі, а який — поточному коміту.
Якщо потрібно зберегти частини обох варіантів, відредагуйте файл вручну, а потім додайте його через git add.
Іноді поточний коміт більше не потрібен. Наприклад:
його зміни вже потрапили в нову основу;
зміни були скасовані в іншій гілці;
коміт містить непотрібну або застарілу зміну.
У такому разі можна пропустити коміт:
git rebase --skipGit не перенесе поточний коміт і перейде до наступного.
Пропуск потрібно використовувати обережно. Він видаляє цей коміт із результату rebase. Перед виконанням команди переконайтеся, що потрібні зміни справді вже є в гілці або більше не потрібні.
Якщо після вирішення конфлікту Git повідомляє, що коміт не містить змін, це також може означати, що його зміни вже були застосовані. У такій ситуації зазвичай використовують:
git rebase --skipЯкщо конфлікти виявилися складними або ви почали rebase не тієї гілки, операцію можна повністю скасувати:
git rebase --abortЦя команда:
припиняє поточний rebase;
повертає гілку до стану, у якому вона була до початку rebase;
скасовує проміжні зміни, створені процесом rebase.
Після скасування перевірте стан:
git status
git log --oneline --graph --decorate --allgit rebase --abort працює лише тоді, коли rebase ще триває.
Під час призупиненого rebase не слід без потреби виконувати звичайний:
git commitЗамість цього використовуйте:
git rebase --continueRebase сам створює нову версію поточного коміту та правильно переходить до наступного.
Також не варто запускати новий git pull, git merge або новий git rebase, поки поточна операція не завершена. Спочатку потрібно вибрати один із варіантів:
git rebase --continue
git rebase --skip
git rebase --abortПовний алгоритм розв’язання конфлікту під час rebase:
Перевірити стан:
git statusВідкрити всі конфліктні файли.
Видалити маркери конфлікту та залишити правильний результат.
Перевірити зміни:
git diff
git diff --checkПозначити виправлені файли:
git add <файл>Продовжити rebase:
git rebase --continueЯкщо поточний коміт не потрібен, замість цього виконати:
git rebase --skipЯкщо потрібно повернутися до стану до rebase:
git rebase --abortСамого редагування файлу недостатньо. Git має побачити, що конфлікт вирішено:
git add <файл>
git rebase --continueПереконайтеся, що у файлах немає рядків:
<<<<<<<
=======
>>>>>>>Перевірити це можна командою:
git grep -n -E '^(<<<<<<<|=======|>>>>>>>)'Якщо маркери залишилися, виправте файл і знову виконайте git add.
git rebase --skip замість продовження--skip не означає «продовжити після вирішення конфлікту». Він повністю пропускає поточний коміт. Для збереження змін потрібно використовувати:
git add <виправлені-файли>
git rebase --continueours або theirsПід час rebase ours і theirs потрібно розглядати в контексті поточного кроку. Перевірте:
git status
git diffЛише після цього вибирайте потрібну версію або об’єднуйте обидві вручну.
Після успішного завершення git rebase --abort вже не скасує операцію. Перед завершенням Git переміщує покажчик гілки, тому для повернення до попереднього стану зазвичай потрібен окремий пошук попереднього стану через журнал посилань. Під час активного конфлікту безпечний спосіб скасування — саме:
git rebase --abortПід час конфлікту git rebase призупиняється на конкретному коміті та очікує на рішення.
Основні команди:
git status — показати поточний стан і конфліктні файли;
git add <файл> — позначити конфлікт як вирішений;
git rebase --continue — продовжити rebase;
git rebase --skip — пропустити поточний коміт;
git rebase --abort — скасувати весь rebase і повернути попередній стан.
Найважливіше — після ручного редагування не забути виконати git add, а перед --skip переконатися, що зміни поточного коміту справді не потрібно зберігати.