Пошук уроків, статей та іншого контенту
Визначите ризики rebase для опублікованих комітів і правила безпечної роботи зі спільною історією.
rebasegit rebase переносить коміти на іншу базову гілку. Під час цього Git створює нові коміти, навіть якщо зміни залишаються такими самими.
У коміту змінюється ідентифікатор, якщо змінюється його батьківський коміт. Тому після rebase історія виглядає подібно, але містить інші об’єкти:
До rebase:
A---B---C feature
Після rebase на нову основу:
A---D---E feature
\
B---C стара історіяКоміти D і E можуть містити ті самі зміни, що й B і C, але це вже інші коміти.
Саме зміна ідентифікаторів є головним ризиком rebase.
Опублікованою вважається історія, яку вже відправили до віддаленого репозиторію і яку можуть використовувати інші розробники.
Наприклад, у віддаленому репозиторії є:
origin/feature: A---B---CІнший розробник створив на основі C власний коміт:
A---B---C---D developer-2Якщо перший розробник виконає rebase, історія його гілки може стати такою:
A---B---C---E---F featureА коміти E і F можуть бути перебазованими версіями B і C. Після цього локальна історія першого розробника вже не збігається з історією у віддаленому репозиторії.
Щоб опублікувати нову історію, доведеться переписати посилання на віддалену гілку:
git push --force-with-lease origin featureДля другого розробника це створює проблему: його коміт D базується на старій версії історії. Git більше не сприймає стару та нову послідовність як одну й ту саму історію.
Можливі наслідки:
необхідність повторно синхронізувати локальну гілку;
конфлікти під час наступного pull;
дублювання змін;
складніше відновлення втрачених комітів;
плутанина під час code review;
ризик випадково видалити чужі коміти.
rebaseНе робіть rebase для гілки, яку використовують кілька людей:
main;
master;
develop, якщо вона спільна для команди;
релізної гілки;
будь-якої гілки, на яку інші розробники вже спираються.
Публічні гілки зазвичай повинні зберігати вже опубліковану історію. Для інтеграції змін у них використовують звичайний merge або інший процес, визначений командою.
Навіть якщо гілка має назву feature, її не можна безпечно перебазовувати, якщо:
її вже перевіряє інший розробник;
від неї створили іншу гілку;
на її основі відкрито спільну роботу;
її використовує CI/CD або тестове середовище;
на її коміти посилаються інші задачі чи інструкції.
Назва гілки не визначає, чи можна переписувати її історію. Важливо, чи використовує її хтось інший.
push --forceКоманда:
git push --force origin featureпереписує віддалену гілку без перевірки, чи з’явилися там нові коміти після вашого останнього отримання змін.
Наприклад, інший розробник міг уже додати коміт D. Простий --force може видалити цей коміт із віддаленої гілки.
Безпечніший варіант:
git push --force-with-lease origin feature--force-with-lease дозволяє переписати гілку лише тоді, коли її віддалений стан відповідає відомому вам стану. Якщо хтось уже опублікував нові зміни, команда відмовиться виконувати push.
Однак --force-with-lease не робить переписування спільної історії безпечним. Він лише зменшує ризик випадково перезаписати нові коміти.
rebase зазвичай можна використовувати для локальних комітів, які ще ніхто не отримав.
Наприклад:
A---B---C featureЯкщо гілка feature існує лише у вашому локальному репозиторії, можна змінити її історію:
git switch feature
git rebase mainТакож зазвичай безпечно перебазувати власну гілку, якщо:
ви ще не публікували її;
ви точно знаєте, що ніхто не створював від неї похідних гілок;
команда домовилася дозволити переписування цієї гілки;
після rebase ви попередите всіх, хто може мати її стару версію.
Правило просте:
Не переписуйте історію, якою вже користується хтось інший.
Нижче наведено типовий сценарій для гілки, яку використовує лише один розробник.
# Перейти до власної гілки
git switch feature/login
# Отримати актуальний стан віддаленого репозиторію
git fetch origin
# Перебазувати локальні коміти на актуальний main
git rebase origin/main
# Якщо виник конфлікт:
# 1. Виправити файли
# 2. Позначити їх як розв'язані
git add path/to/file
# Продовжити перебазування
git rebase --continue
# Якщо перебазування потрібно скасувати
# git rebase --abort
# Після rebase опублікувати переписану приватну гілку
git push --force-with-lease origin feature/loginПеред push переконайтеся, що:
гілка не є спільною;
ніхто не додав до неї нові коміти;
команда дозволяє переписувати її історію;
тести проходять після rebase.
Для спільної гілки не потрібно переписувати вже опубліковані коміти. Замість цього отримайте нові зміни та об’єднайте їх зі своєю роботою:
git switch feature/shared-work
git fetch origin
git merge origin/feature/shared-workАбо, якщо це передбачено процесом команди, створіть нову гілку від актуальної основи та перенесіть лише потрібні зміни. Важливо, щоб рішення не видаляло коміти, на які вже посилаються інші.
У гілці main зазвичай не можна виконувати force push. Захищені гілки на сервері часто блокують такі операції, але це не замінює командних правил і уважної перевірки цілі push.
Перед rebase можна переглянути поточну історію:
git log --oneline --graph --decorate --allПорівняйте локальну гілку з віддаленою:
git fetch origin
git log --oneline --left-right origin/feature...featureПозначення:
< — коміти, які є лише у віддаленій гілці;
> — коміти, які є лише у вашій локальній гілці.
Якщо після локального rebase з’явилися нові локальні коміти, а старі коміти зникли з поточної лінії, наступний push, імовірно, вимагатиме --force-with-lease.
Перед force push перевірте саме ту гілку, яку будете оновлювати:
git branch --show-current
git status
git log --oneline --graph origin/feature...HEADНе виконуйте одразу нові команди навмання. Спочатку збережіть поточний стан і з’ясуйте, які коміти були переписані.
Локально Git зберігає попередні позиції гілок у reflog:
git reflogУ виведенні можна знайти стан гілки до rebase, наприклад запис на кшталт:
abc1234 HEAD@{2}: rebase (start): checkout origin/main
def5678 HEAD@{3}: commit: Add login validationЗа потреби можна створити тимчасову гілку на старому коміті:
git branch backup-before-rebase HEAD@{3}Якщо помилковий force push уже виконано у віддалений репозиторій, не намагайтеся виправити ситуацію повторним force push без узгодження з командою. Спочатку потрібно визначити, які коміти були втрачені та хто вже встиг отримати нову історію.
Це не так. Навіть за однакових змін після rebase створюються нові коміти з іншими ідентифікаторами.
Не обов’язково. Feature-гілка може бути спільною або вже використовуватися в іншій роботі.
--force-with-lease повністю усуває ризик»Ця опція захищає від перезапису змін, про які ваш локальний репозиторій ще не знає. Вона не усуває проблеми для людей, які вже мають стару історію гілки.
У rebase і merge різні цілі. Rebase зручно застосовувати до приватної історії, але для спільної історії важливіше не ламати посилання на вже опубліковані коміти.
Захищені гілки можуть блокувати force push, але не всі репозиторії налаштовані однаково. Відповідальність за перевірку гілки та наслідків операції все одно залишається на розробнику.
Перед виконанням rebase перевірте:
Чи є коміти вже опублікованими?
Чи працює хтось ще на основі цієї гілки?
Чи дозволено командою переписування її історії?
Чи потрібно буде виконати force push?
Чи маєте ви актуальний стан віддаленого репозиторію?
Чи можна відновити стару історію за потреби?
Запам’ятайте основні правила:
локальну непубліковану історію можна вільно впорядковувати;
опубліковану спільну історію не слід переписувати;
для власної вже опублікованої гілки використовуйте --force-with-lease, а не --force;
main та інші спільні гілки не перебазовуйте без чіткої командної процедури;
перед force push завжди перевіряйте назву гілки та її стан.
rebase створює нову версію історії, тому змінює ідентифікатори комітів. Для локальної або приватної гілки це зазвичай безпечно й допомагає підтримувати зрозумілу історію.
Для опублікованої спільної гілки rebase небезпечний: інші розробники можуть мати коміти, що базуються на старій історії. Їхня робота стане несумісною з переписаною гілкою.
Головне правило:
Не робіть rebase комітів, які вже опубліковані та використовуються іншими людьми.