Пошук уроків, статей та іншого контенту
Освоїте інтерактивний rebase для редагування, об’єднання та впорядкування комітів локальної історії.
Інтерактивний rebase (git rebase -i) дає змогу змінювати локальну історію комітів:
редагувати повідомлення комітів;
об’єднувати кілька комітів в один;
змінювати порядок комітів;
редагувати вміст окремого коміту;
видаляти непотрібні коміти.
На відміну від звичайного rebase, інтерактивний режим показує список комітів і дозволяє вибрати дію для кожного з них.
Інтерактивний rebase зазвичай застосовують до комітів, які ще не були опубліковані у спільному віддаленому репозиторії.
Перед початком перевірте стан робочої директорії:
git statusБажано, щоб у ній не було незбережених змін. Якщо зміни є, їх можна:
закомітити;
тимчасово зберегти через git stash;
завершити або скасувати поточну операцію.
Також корисно переглянути коротку історію:
git log --oneline --graph --decorate -n 8Наприклад:
* 7a1f2c3 Add validation
* 4b8e901 Fix button styles
* 92c6d10 Add login form
* 1f3a8d2 Initial commitЩоб змінити останні три коміти, виконайте:
git rebase -i HEAD~3HEAD~3 означає: взяти для редагування три коміти перед поточним HEAD.
Git відкриє текстовий файл приблизно такого вигляду:
pick 92c6d10 Add login form
pick 4b8e901 Fix button styles
pick 7a1f2c3 Add validation
# Rebase 1f3a8d2..7a1f2c3 onto 1f3a8d2 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# d, drop = remove commitПорядок комітів у цьому списку йде від найстарішого до найновішого.
Після редагування файлу Git виконує вказані дії зверху вниз.
pick — залишити комітpick 92c6d10 Add login formКоміт залишається без змін. pick є стандартною дією, тому його можна не змінювати.
reword — змінити повідомлення комітуЗамініть pick на reword або скорочений варіант r:
reword 92c6d10 Add login formGit збереже вміст коміту, але відкриє редактор для зміни його повідомлення.
Наприклад, повідомлення:
Add login formможна замінити на:
Add user login formedit — змінити вміст комітуКоманда edit зупиняє rebase на вибраному коміті:
edit 4b8e901 Fix button stylesПісля зупинки можна змінити файли, додати їх до індексу й оновити коміт:
# Внесіть зміни у файли
git add styles.css
git commit --amend
git rebase --continueЯкщо потрібно лише змінити вміст коміту, але не його повідомлення:
git commit --amend --no-edit
git rebase --continuesquash — об’єднати коміт із попереднімКоманда squash об’єднує поточний коміт із попереднім і дозволяє відредагувати спільне повідомлення:
pick 92c6d10 Add login form
squash 4b8e901 Fix button stylesУ результаті утвориться один коміт, який міститиме зміни обох комітів.
Під час операції Git відкриє редактор для формування нового повідомлення. У ньому можна залишити одне повідомлення або об’єднати кілька описів.
squash не можна застосувати до першого коміту в обраному діапазоні, оскільки він не має попереднього коміту в цьому списку.
fixup — об’єднати коміт без його повідомленняfixup схожий на squash, але повідомлення поточного коміту буде видалено:
pick 92c6d10 Add login form
fixup 4b8e901 Fix button stylesЦе зручно для технічних комітів на кшталт:
Fix typo;
Review changes;
Small correction.
Основне повідомлення першого коміту збережеться автоматично.
drop — видалити комітЩоб видалити коміт з історії, використайте drop або просто видаліть відповідний рядок:
pick 92c6d10 Add login form
drop 4b8e901 Fix button stylesПісля завершення rebase зміни з цього коміту зникнуть із поточної історії. Якщо вони потрібні, їх можна буде повернути окремо, наприклад через reflog або інший доступний покажчик на стару історію.
Коміти можна переставити, змінивши порядок рядків:
pick 4b8e901 Fix button styles
pick 92c6d10 Add login form
pick 7a1f2c3 Add validationGit спробує застосувати їх у новому порядку.
Важливо, щоб коміти не залежали від попередніх змін. Якщо один коміт використовує файл або код, доданий іншим комітом, зміна порядку може спричинити конфлікт.
Припустімо, історія містить такі коміти:
* c3d4e5f Fix typo in validation
* b2c3d4e Add email validation
* a1b2c3d Add registration formМета:
об’єднати виправлення помилки з комітом валідації;
залишити одне зрозуміле повідомлення;
змінити повідомлення коміту форми.
Запускаємо rebase для трьох останніх комітів:
git rebase -i HEAD~3У редакторі змінюємо список:
reword a1b2c3d Add registration form
pick b2c3d4e Add email validation
fixup c3d4e5f Fix typo in validationПісля збереження:
Git відкриє редактор для зміни повідомлення першого коміту;
застосує коміт із валідацією;
об’єднає виправлення помилки з попереднім комітом;
завершить rebase.
Перевірити результат можна так:
git log --oneline --graph -n 5Очікувана історія:
* 8f7e6d5 Add email validation
* 6a5b4c3 Add user registration formІдентифікатори комітів після rebase зміняться. Це нормально: Git створює нові коміти на основі змінених.
Під час зміни порядку або об’єднання комітів Git може не знати, як застосувати зміни. У такому разі rebase призупиняється, а git status покаже конфліктні файли.
Послідовність дій:
git statusВідкрийте конфліктні файли та виправте маркери конфлікту:
<<<<<<< HEAD
версія з поточної основи
=======
версія з коміту, який застосовується
>>>>>>> commit-idПісля виправлення додайте файли до індексу:
git add path/to/fileПродовжте rebase:
git rebase --continueЯкщо Git відкриє редактор повідомлення коміту, підтвердьте його або змініть.
Якщо конфліктів багато або результат вас не влаштовує, операцію можна скасувати:
git rebase --abortGit спробує повернути гілку до стану, у якому вона була до початку rebase.
Якщо поточний коміт більше не потрібен або його зміни вже присутні в історії:
git rebase --skipВикористовуйте цю команду обережно: вона пропускає коміт повністю.
Інтерактивний rebase можна запускати не лише відносно HEAD, а й від конкретної базової точки:
git rebase -i mainУ цьому випадку Git запропонує коміти поточної гілки, яких немає в main.
Також можна вказати конкретний коміт як основу:
git rebase -i 1f3a8d2Сам коміт 1f3a8d2 не редагуватиметься. До списку потраплять коміти після нього.
Rebase змінює ідентифікатори комітів. Тому після rebase локальна гілка може відрізнятися від її версії на віддаленому репозиторії.
Якщо переписування опублікованої історії справді необхідне, для відправлення змін використовуйте:
git push --force-with-lease--force-with-lease перевіряє, що віддалена гілка не змінилася після останнього отримання даних. Це безпечніше за безумовний:
git push --forceНе переписуйте історію спільної гілки без узгодження з іншими розробниками. Їхні локальні гілки можуть залишитися несумісними з оновленою віддаленою історією.
Типовий сценарій перед створенням pull request:
git status
git log --oneline --graph -n 10
git rebase -i HEAD~5
git log --oneline --graph -n 10Після успішного rebase перевірте проєкт:
# Запустіть тести або інші перевірки проєкту
npm testКоманда для тестів залежить від проєкту. Не запускайте її, якщо в конкретному проєкті немає відповідного скрипту.
Команда:
git rebase -i HEAD~3редагує три останні коміти, але не включає їхню базу — четвертий коміт у минулому.
Якщо потрібного коміту немає у списку, скасуйте операцію й повторіть її з більшим діапазоном:
git rebase --abort
git rebase -i HEAD~5squash для першого рядкаПерший коміт у списку не має попереднього коміту для об’єднання. Для нього потрібно залишити pick, reword або edit.
Git читає команди з файлу rebase після його збереження. Якщо файл закрито неправильно або всі рядки видалено, операція може завершитися помилкою.
Після rebase хеші комітів змінюються. Це очікувана поведінка, оскільки навіть повідомлення, порядок або батьківський коміт є частиною даних коміту.
git push --force без перевіркиБезумовний force push може перезаписати чужі зміни. Для особистої гілки зазвичай краще використовувати:
git push --force-with-leaseПеред git rebase --continue потрібно:
виправити конфлікт у файлах;
виконати git add для виправлених файлів;
лише після цього продовжити rebase.
git rebase -i HEAD~N відкриває останні N комітів для редагування.
pick залишає коміт без змін.
reword змінює повідомлення коміту.
edit зупиняє rebase для зміни вмісту коміту.
squash об’єднує коміти та дозволяє відредагувати повідомлення.
fixup об’єднує коміти й залишає повідомлення попереднього.
drop видаляє коміт.
Переставлення рядків змінює порядок комітів.
Для конфліктів використовують git add і git rebase --continue.
Щоб скасувати операцію, використовують git rebase --abort.
Після переписування опублікованої гілки може знадобитися git push --force-with-lease.