Пошук уроків, статей та іншого контенту
Розберете, як rebase переносить коміти на нову базу та змінює структуру історії Git.
rebaserebase — це операція Git, яка переносить коміти поточної гілки на нову базу.
Уявімо, що історія має такий вигляд:
A---B---C main
\
D---E featuremain містить коміти A, B, C;
гілка feature була створена від коміту B;
у feature з’явилися коміти D і E.
Якщо виконати rebase гілки feature на main, Git перенесе зміни з комітів D і E поверх коміту
CA---B---C main
\
D'---E' featureКоміти D' і E' містять ті самі зміни, що й D та E, але це вже нові коміти з іншими ідентифікаторами.
rebaseПеребазування складається з кількох логічних кроків:
Git знаходить спільного предка поточної гілки та гілки-основи.
Тимчасово відокремлює коміти поточної гілки після цього предка.
Переміщує поточну гілку на останній коміт гілки-основи.
Застосовує збережені зміни по черзі як нові коміти.
Поточна гілка визначається позицією HEAD. Тому перед виконанням команди потрібно перейти саме на ту гілку, яку ви хочете перебазувати.
Наприклад:
git switch feature
git rebase mainЦя команда означає: «перенеси коміти гілки feature на вершину main».
Створимо невеликий репозиторій і зробимо кілька комітів:
mkdir rebase-demo
cd rebase-demo
git init
git config user.name "Demo User"
git config user.email "demo@example.com"
printf "Проєкт\n" > README.md
git add README.md
git commit -m "Створити README"
git switch -c feature
printf "Форма входу\n" > login.txt
git add login.txt
git commit -m "Додати форму входу"
printf "Перевірка пароля\n" >> login.txt
git add login.txt
git commit -m "Додати перевірку пароля"
git switch main
printf "Інструкція запуску\n" >> README.md
git add README.md
git commit -m "Додати інструкцію запуску"До перебазування історія приблизно така:
A---B main
\
C---D featureТепер перебазуємо feature на main:
git switch feature
git rebase mainПісля цього історія матиме лінійний вигляд:
A---B main
\
C'---D' featureПеревірити історію можна командою:
git log --oneline --graph --allПрапорець --graph показує зв’язки між комітами, а --oneline скорочує їхній вивід.
rebase і mergeТу саму ситуацію можна розв’язати за допомогою merge.
Якщо виконати:
git switch feature
git merge mainGit може створити окремий коміт злиття:
A---B---E main
\ \
C---D---M featureТут M — merge-коміт, який поєднує дві лінії історії.
Після rebase історія зазвичай виглядає простіше:
A---B---E---C'---D' featureОсновна відмінність:
merge зберігає початкове розгалуження та може створити merge-коміт;
rebase переписує коміти поточної гілки так, ніби вони були створені від нової бази.
rebase не змінює коміти гілки-основи. У прикладі команда змінює історію feature, але не змінює main.
Ідентифікатор коміту залежить не лише від змін у файлах. Він також враховує:
батьківський коміт;
автора;
дату;
повідомлення коміту;
інші дані коміту.
Після rebase коміт отримує іншого батька. Тому навіть якщо вміст змін залишився таким самим, ідентифікатор коміту змінюється.
Наприклад:
Старий коміт: C
Новий коміт: C'Це не редагування старого коміту. Git створює нову версію коміту на іншій основі.
Поширений сценарій:
Ви працюєте у гілці feature.
У main з’явилися нові коміти.
Ви оновлюєте локальну копію main.
Перебазовуєте feature на оновлений main.
Перевіряєте програму.
Зливаєте feature з main.
Типова послідовність команд у локальному репозиторії:
git switch main
git pull --ff-only
git switch feature
git rebase mainПісля успішного rebase гілку feature можна перевірити та об’єднати з main.
Якщо feature вже була опублікована у віддаленому репозиторії, після перебазування може знадобитися оновлення з примусовою перевіркою:
git push --force-with-lease--force-with-lease безпечніший за звичайний --force: Git відмовиться перезаписувати віддалену гілку, якщо хтось інший встиг додати до неї нові коміти.
rebaseКонфлікт виникає, коли зміни в коміті поточної гілки несумісні зі змінами в новій базі.
Наприклад, обидві гілки могли змінити один і той самий рядок файлу.
Якщо Git не може застосувати коміт автоматично, перебазування призупиняється. Git повідомить, у яких файлах є конфлікти.
Загальна послідовність розв’язання:
# Переглянути файли з конфліктами
git status
# Після виправлення конфліктів додати виправлені файли
git add path/to/file
# Продовжити перебазування
git rebase --continueПотрібно відкрити файли з конфліктами, видалити службові маркери Git і залишити правильний варіант коду. Типові маркери мають такий вигляд:
<<<<<<< HEAD
Вміст із нової бази
=======
Вміст із коміту поточної гілки
>>>>>>> назва-комітуЯкщо ви зрозуміли, що перебазування потрібно скасувати:
git rebase --abortGit поверне гілку до стану, який був до початку rebase.
Перебазувати поточну гілку на main:
git rebase mainПеребазувати конкретну гілку на конкретну основу:
git rebase main featureУ цьому випадку Git перебазує feature на main, навіть якщо ви не переходили на feature.
Продовжити перебазування після виправлення конфлікту:
git rebase --continueПропустити поточний коміт:
git rebase --skipЦе варто робити лише тоді, коли зміни цього коміту вже присутні в новій базі або коміт більше не потрібен.
Скасувати перебазування:
git rebase --abortrebaseІнтерактивний режим дає змогу переглянути кілька останніх комітів і змінити їхній порядок або об’єднати їх:
git rebase -i HEAD~3Git відкриє список останніх трьох комітів. Перед кожним комітом буде команда, наприклад:
pick a1b2c3d Додати форму входу
pick b2c3d4e Виправити відступ
pick c3d4e5f Додати перевірку пароляНайчастіше використовують такі дії:
pick — залишити коміт без змін;
reword — змінити повідомлення коміту;
edit — зупинитися на коміті для редагування;
squash — об’єднати коміт із попереднім;
drop — видалити коміт.
Наприклад, два останні виправлення можна об’єднати з основним комітом:
pick a1b2c3d Додати форму входу
squash b2c3d4e Виправити відступ
squash c3d4e5f Додати перевірку пароляПісля цього Git запропонує створити одне спільне повідомлення для об’єднаного коміту.
Для початку достатньо запам’ятати звичайну форму:
git rebase mainІнтерактивний режим буде корисним, коли потрібно впорядкувати власну локальну історію перед публікацією.
rebaserebase переписує історію, тому його небезпечно без узгодження застосовувати до спільної гілки.
Не варто перебазовувати коміти, які інші розробники вже завантажили та використовують у своїх локальних гілках. Після цього їхні локальні коміти більше не матимуть тієї самої бази, а синхронізація може стати складнішою.
Зазвичай безпечно перебазовувати:
власну локальну гілку;
гілку, яку ще ніхто не використовує;
робочу гілку перед створенням або оновленням запиту на злиття, якщо це погоджено в команді.
Головне правило:
Не переписуйте історію, спільною з якою вже працюють інші люди.
rebase не з тієї гілкиПеред командою перевірте поточну гілку:
git branch --show-currentЯкщо потрібно перебазувати feature, спочатку виконайте:
git switch feature
git rebase mainrebase і переміщенням файлівrebase не переносить файли в іншу папку й не змінює назви файлів. Він змінює батьківські коміти та створює нову послідовність комітів.
--force без потребиПісля перебазування опублікованої гілки не слід одразу використовувати:
git push --forceЦя команда може перезаписати чужі зміни. Якщо примусове оновлення справді потрібне, використовуйте:
git push --force-with-leaseПісля rebase нові ідентифікатори є очікуваною поведінкою. Це наслідок того, що коміти отримали нового батька.
Після розв’язання конфлікту потрібно перевірити файли, виконати тести та лише потім продовжувати:
git add path/to/file
git rebase --continuerebase переносить коміти поточної гілки на нову базу.
Після rebase коміти створюються заново, тому їхні ідентифікатори змінюються.
Перебазування часто робить історію лінійною та зрозумілішою.
Команда для основного сценарію:
git switch feature
git rebase mainПід час конфлікту використовують git rebase --continue, а для скасування — git rebase --abort.
Не слід переписувати спільну історію без узгодження з командою.