Модуль 3: Історія як вибір — Інтерактивний rebase
Складність: [СЕРЕДНЯ] Час на проходження: 90 хвилин Попередні вимоги: Модуль 2 курсу Git Deep Dive
Що ви зможете зробити
Розділ «Що ви зможете зробити»- Відновити фрагментовану історію комітів у логічну розповідь, використовуючи операції інтерактивного rebase (squash, reword, fixup, drop).
- Діагностувати та вирішити конфлікти злиття, що виникають ітеративно під час багатоетапної послідовності rebase.
- Сформулювати стратегію видалення випадково зафіксованих конфіденційних даних (таких як хмарні облікові дані або сертифікати) з постійної історії гілки.
- Порівняти та оцінити технічні компроміси між merging та rebasing при синхронізації локальних гілок функцій з оновленнями в основній гілці.
- Виконати перенесення гілки за допомогою операції
git rebase --ontoдля міграції активної роботи між розбіжними базовими гілками.
Чому це важливо
Розділ «Чому це важливо»Інженер інфраструктури на платформі електронної комерції середнього розміру отримав завдання перенести застарілі сервіси автентифікації на Kubernetes. У процесі розробки він створив файл configmap.yaml для зберігання змінних оточення. Для локального тестування він тимчасово вписав ключ доступу AWS IAM з широкими правами доступу до бази даних безпосередньо у файл і зафіксував це в коміті. Через три коміти, усвідомивши помилку, він видалив ключ доступу, замінив його посиланням на Kubernetes Secret і зафіксував виправлення. Гілку було відправлено (push), перевірено, схвалено та злито. Кінцевий стан коду був ідеальним. Однак через два тижні автоматизований сканер облікових даних, використаний зловмисником, просканував історію комітів репозиторію. Сканер знайшов оригінальний коміт, що містив ключ доступу. Протягом одного вікенду нападники запустили сотні дорогих GPU-інстансів у кількох регіонах AWS, що призвело до рахунку за хмарні послуги у вісімдесят тисяч доларів, перш ніж команда безпеки втрутилася.
Інженер припустився критичної помилки: він вважав, що видалення рядка коду та фіксація змін стирає попередній стан. Це не так. Git за замовчуванням є книгою записів, куди можна лише додавати (append-only ledger). Історія комітів — це не просто механізм резервного копіювання; це постійний журнал аудиту та життєво важливий інструмент комунікації. Захаращена історія, повна повідомлень “work in progress”, “fixed typo” та “trying again”, приховує архітектурний задум ваших змін і покладає необґрунтоване когнітивне навантаження на ваших рецензентів. Що ще гірше, вона залишає артефакти, які можуть скомпрометувати всю вашу систему.
У цьому модулі ви навчитеся використовувати інтерактивний rebasing, щоб сформувати історію комітів у чисту, безпечну та логічну розповідь. Ви перейдете від ставлення до Git як до пасивної кнопки збереження до використання його як активного інструменту редагування, гарантуючи, що код, яким ви ділитеся зі світом, є саме тією історією, яку ви маєте намір розповісти.
Філософія перезапису історії
Розділ «Філософія перезапису історії»Перш ніж виконувати команди, ми повинні зрозуміти концептуальне зрушення, необхідне для перезапису історії. Коли ви розробляєте локально, ваші коміти представляють потік свідомості. Ви послідовно вирішуєте проблеми, припускаєтеся помилок, повертаєтеся назад і пробуєте нові підходи. Це правильний спосіб роботи локально — робіть коміти часто, щоб створювати точки збереження.
Однак історія, корисна для вас під час розробки, рідко є корисною для рецензента або майбутнього розробника, який намагається зрозуміти ваші архітектурні рішення. Майбутньому інженеру, який виконує git blame на складній конфігурації Kubernetes Deployment, не потрібно бачити, що вам знадобилося шість спроб, щоб правильно встановити відступи в YAML. Йому потрібен один цілісний коміт, який впроваджує Deployment з вичерпним повідомленням, що пояснює, чому були обрані конкретні ліміти ресурсів.
Золоте правило rebasing
Розділ «Золоте правило rebasing»Перезапис історії передбачає створення абсолютно нових комітів з новими криптографічними хешами (SHA). Якщо ви перепишете коміт, який вже було відправлено до центрального репозиторію та завантажено іншими розробниками, ви створите розбіжну часову шкалу.
Золоте правило: Ніколи не робіть rebase комітів, які існують за межами вашого локального репозиторію.
Якщо ви зробите rebase спільної гілки та виконаєте force-push результату, наступного разу, коли ваші колеги спробують оновити код (pull), Git побачить їхню локальну історію та нову віддалену історію як два абсолютно окремі набори робіт. Він спробує їх злити, що призведе до масових, заплутаних конфліктів і дубльованої історії комітів. Rebasing — це інструмент для підготовки вашого особистого робочого простору перед тим, як ви ним поділитеся. Коли гілка стає публічною і над нею активно працюють інші, ви повинні покладатися на стандартні злиття (merge) або коміти скасування (revert), щоб рухатися далі.
Merging проти Rebasing
Розділ «Merging проти Rebasing»Коли вам потрібно інтегрувати зміни з основної гілки у вашу гілку функції, у вас є два основних механізми.
+---------------------------------------------------+ | СТРАТЕГІЯ MERGE | +---------------------------------------------------+ | | | Feature Branch: [C1] ---> [C2] ---> [C3] | | / \ | | / \ | | Main Branch: [M1] --------> [M2] --------> [M3] | | | | Результат: нелінійна історія з merge-комітом. | | Хронологія точно показує, коли що сталося. | +---------------------------------------------------+
Merge зберігає точну хронологічну історію. Він створює новий “merge-коміт”, який має двох батьків. Це фактично точно, але може призвести до заплутаного “ромбоподібного” графа комітів, який важко читати.
+---------------------------------------------------+ | СТРАТЕГІЯ REBASE | +---------------------------------------------------+ | | | Feature Branch: [C1’] -> [C2’] | | / | | / | | Main Branch: [M1] -> [M2] -> [M3] | | | | Результат: лінійна історія. Коміти гілки функцій | | переписуються так, ніби вони базуються на останній| | версії main. Старі [C1] та [C2] відкидаються. | +---------------------------------------------------+
Rebase бере коміти вашої гілки функції, тимчасово відкладає їх убік, оновлює вашу гілку, щоб вона вказувала на останній коміт основної гілки, а потім повторно відтворює вашу роботу поверх нього. Це створює ідеально лінійну історію, що робить такі інструменти, як git log та git bisect, значно ефективнішими. Компроміс полягає в тому, що це перезаписує історію — оригінальні SHA ваших комітів знищуються і замінюються новими.
Зупиніться та подумайте: Як ви вважаєте, що станеться, якщо під час rebase виникне конфлікт злиття? Це трапляється один раз наприкінці чи інакше?
Відповідь: Оскільки rebase відтворює коміти один за одним, якщо кілька комітів торкаються одного і того ж файлу, який було змінено в основній гілці, вам, можливо, доведеться вирішувати конфлікти для кожного окремого коміту, що відтворюється. Це ітеративне вирішення конфліктів є основним болючим моментом rebasing.
Інтерфейс інтерактивного rebase
Розділ «Інтерфейс інтерактивного rebase»Стандартна команда git rebase <branch> працює автоматично. Інтерактивний rebasing, що викликається з прапорцем -i або --interactive, призупиняє процес і відкриває текстовий редактор, дозволяючи вам перехоплювати та змінювати інструкції, які Git використовує для відтворення комітів.
Щоб розпочати інтерактивний rebase відносно гілки main, виконайте:
git rebase -i mainАбо ж, щоб переписати останні 5 комітів у вашій поточній гілці незалежно від висхідної бази, ви можете використати відносне посилання HEAD:
git rebase -i HEAD~5Коли відкриється текстовий редактор, ви побачите список інструкцій, який виглядає так:
pick 3a2b1c4 Add initial deployment.yamlpick 9f8e7d6 Fix YAML indentation in deploymentpick 5c4b3a2 Add service.yamlpick 1d2c3b4 Add configmap.yaml with hardcoded db passwordpick 7e6d5c4 Remove password, use secret referencepick 8a9b0c1 Add liveness and readiness probes
# Rebase 8273645..8a9b0c1 onto 8273645 (6 commands)## Commands:# p, pick <commit> = use commit# r, reword <commit> = use commit, but edit the commit message# e, edit <commit> = use commit, but stop for amending# s, squash <commit> = use commit, but meld into previous commit# f, fixup <commit> = like "squash", but discard this commit's log message# x, exec <command> = run command (the rest of the line) using shell# d, drop <commit> = remove commitВажлива деталь: коміти перелічені від найстарішого вгорі до найновішого внизу. Це протилежно до git log. Git читає цей файл зверху вниз, застосовуючи кожну інструкцію послідовно.
Арсенал команд
Розділ «Арсенал команд»Розуміння тонких відмінностей між цими командами є важливим для ефективного формування історії.
| Команда | Дія | Основний сценарій використання |
|---|---|---|
pick | Застосовує коміт саме так, як він є. | Дія за замовчуванням. Залишає коміт недоторканим. |
reword | Застосовує коміт, але робить паузу, щоб відкрити редактор для зміни повідомлення. | Виправлення помилки в повідомленні коміту або додавання більш описового контексту. |
edit | Застосовує коміт, а потім повністю зупиняє процес rebase, повертаючи керування терміналу. | Розділення великого коміту на менші або зміна фактичного вмісту файлів історичного коміту. |
squash | Об’єднує вміст цього коміту з комітом безпосередньо над ним. Робить паузу, щоб дозволити вам об’єднати їхні повідомлення. | Об’єднання пов’язаних змін (наприклад, функція та відповідні юніт-тести) в один логічний блок. |
fixup | Об’єднує вміст з комітом над ним, але повністю відкидає повідомлення цього коміту. | Поглинання комітів типу “fix typo” або “WIP” в основний коміт функції без захаращення кінцевого повідомлення. |
drop | Повністю ігнорує коміт. Він не буде відтворений. | Видалення експериментального коду або випадкових комітів. (Ви також можете просто видалити рядок у редакторі). |
exec | Запускає довільну команду оболонки після застосування попереднього рядка. | Автоматичний запуск пакету тестів або linter після кожного коміту, щоб переконатися, що збірка не зламана в середині історії. |
+-------------------------------------------------------------+ | ДВИГУН ВИКОНАННЯ ІНТЕРАКТИВНОГО REBASE | +-------------------------------------------------------------+ | | | [HEAD] Поточний стан | | | | | v | | 1. Від’єднати HEAD на обраному базовому коміті. | | | | 2. Прочитати інструкції з текстового редактора. | | | | 3. Застосувати перший коміт у списку. | | +— Це ‘pick’? Застосувати та перейти до наступного. | | +— Це ‘squash’? Застосувати, чекати редагування тексту. | | +— Це ‘edit’? Застосувати, ЗУПИНИТИ виконання. | | | | 4. Повторювати, поки список не стане порожнім. | | | | 5. Спрямувати оригінальну гілку на новий HEAD. | | | | 6. Згодом зібрати сміття (старі покинуті коміти). | +-------------------------------------------------------------+
Створення ідеального Pull Request
Розділ «Створення ідеального Pull Request»Розглянемо практичний сценарій. Ми створюємо рівень застосунку Kubernetes. Поточна історія нашої гілки — це той захаращений список, який ми бачили раніше. Ми хочемо привести його до ладу, створивши лаконічну логічну історію перед відкриттям Pull Request.
Наші цілі для цього rebase:
- Об’єднати виправлення відступів з початковим комітом deployment.
- Повністю видалити жорстко закодований пароль з історії, залишивши лише кінцевий безпечний стан ConfigMap.
- Об’єднати проби (probes) з комітом deployment.
- Змінити повідомлення фінального коміту deployment на більш описове.
Запускаємо git rebase -i HEAD~6.
Крок 1: Перевпорядкування та Fixup
Розділ «Крок 1: Перевпорядкування та Fixup»Щоб досягти наших цілей, ми повинні фізично перемістити рядки в текстовому редакторі. Переміщуємо fixup для відступів deployment безпосередньо під створення deployment. Переміщуємо додавання проб також вище.
Ми повинні обережно поводитися з секретом. Секрет був доданий у 1d2c3b4 і видалений у 7e6d5c4. Якщо ми використаємо squash або fixup для коміту видалення відносно коміту додавання, отриманий комбінований коміт представлятиме чисту різницю: секрет ніби ніколи й не існував.
Редагуємо файл так:
reword 3a2b1c4 Add initial deployment.yamlfixup 9f8e7d6 Fix YAML indentation in deploymentfixup 8a9b0c1 Add liveness and readiness probespick 5c4b3a2 Add service.yamlpick 1d2c3b4 Add configmap.yaml with hardcoded db passwordfixup 7e6d5c4 Remove password, use secret referenceЗупиніться та подумайте: Подивіться на перший блок (reword, fixup, fixup). Яким буде кінцеве повідомлення коміту?
Відповідь: Оскільки ми використали
rewordдля першого коміту іfixupдля наступних, Git відкриє редактор для першого коміту, дозволяючи нам написати нове повідомлення, і він повністю відкине повідомлення “Fix YAML indentation…” та “Add liveness…”. Результатом буде один коміт з нашим абсолютно новим повідомленням.
Крок 2: Виконання та перейменування
Розділ «Крок 2: Виконання та перейменування»Коли ми зберігаємо і закриваємо редактор, Git починає виконання плану. Він від’єднує HEAD на базовому коміті та починає застосовувати зміни.
- Застосовує
3a2b1c4. Оскільки ми вказалиreword, він негайно відкриває редактор. Змінюємо повідомлення на:feat: Implement Core Application Deployment with Health Checks. Зберігаємо та закриваємо. - Застосовує
9f8e7d6. Оскільки цеfixup, він зливає зміни файлів у новий коміт, не запитуючи повідомлення. - Застосовує
8a9b0c1як ще одинfixup. - Застосовує
5c4b3a2у звичайному режимі. - Застосовує
1d2c3b4. - Застосовує
7e6d5c4. Оскільки цеfixup, прикріплений до створення ConfigMap, додавання та негайне видалення секрету скасовують одне одного.
Отримана історія — це рівно два чистих коміти: Deployment та комбінація Service/ConfigMap. Конфіденційні дані були назавжди видалені з історії гілки.
Просунуті маневри: хірургічне редагування та перенесення
Розділ «Просунуті маневри: хірургічне редагування та перенесення»Команда edit: розділення комітів
Розділ «Команда edit: розділення комітів»Іноді ви створюєте монолітний коміт, який містить зміни для двох абсолютно окремих функцій. Вам потрібно його розділити. Ось тут і стає в пригоді команда edit.
Під час інтерактивного rebase позначте монолітний коміт словом edit. Коли Git дійде до цього коміту, він застосує зміни, а потім зупиниться, повернувши вас у термінал.
Stopped at 5c4b3a2... Add service and ingress manifestsYou can amend the commit now, with git commit --amendOnce you are satisfied with your changes, run git rebase --continueНа цьому етапі файли змінені у вашому робочому каталозі, а монолітний коміт є поточним HEAD. Щоб розділити його, ви повинні фактично “скасувати фіксацію” змін, не втрачаючи модифікацій файлів.
# Скинути HEAD до попереднього коміту, залишивши файли зміненими в робочому деревіgit reset HEAD~1
# Тепер зафіксуйте лише файл сервісуgit add service.yamlgit commit -m "feat: Add internal routing Service"
# Далі зафіксуйте файл ingressgit add ingress.yamlgit commit -m "feat: Expose application via Ingress"
# Відновити операцію rebasegit rebase --continueЗупиніться та подумайте: Що станеться, якщо ви забудете запустити
git reset HEAD~1і просто почнете додавати та фіксувати файли безпосередньо під час паузиedit?Відповідь: Якщо ви пропустите скидання (reset), оригінальний монолітний коміт залишиться недоторканим як ваш поточний HEAD. Будь-які нові коміти, які ви зробите, будуть додані поверх нього, а не замінять його. Ви отримаєте дубльовані або фрагментовані коміти замість того, щоб реально розділити оригінальний.
Ви успішно переписали один історичний коміт у два окремих логічних коміти.
Перенесення за допомогою --onto
Розділ «Перенесення за допомогою --onto»Команда git rebase --onto — це потужний інструмент для перенесення послідовності комітів з однієї бази на іншу. Це надзвичайно корисно в середовищі мікросервісів, де гілки функцій часто залежать від інших гілок функцій.
Уявіть, що ви працюєте над feature-db-migration. Інший член команди працює над feature-api-update, яка відгалужується від вашої гілки міграції, оскільки потребує нової схеми бази даних.
Ваш колега зливає feature-db-migration в main, але вони використовують стратегію “Squash and Merge” на GitHub. Ваші оригінальні SHA комітів зникли, замінені одним новим SHA в main. Ваша гілка feature-api-update тепер базується на комітах-привидах, яких більше не існує у висхідній історії.
Зупиніться та подумайте: Як ви думаєте, що станеться, якби ви запустили стандартний
git rebase mainпрямо зараз?Відповідь: Git побачив би ваші оригінальні коміти
feature-db-migrationяк відмінні від squashed-коміту в main, оскільки їхні SHA відрізняються. Він спробував би відтворити їх усі поверх main, що призвело б до масових конфліктів, оскільки main вже містить ті самі зміни коду в іншій формі.
Вам потрібно відокремити оновлення API від старих комітів-привидів і прищепити їх безпосередньо до main.
+---------------------------------------------------+ | ПЕРЕНЕСЕННЯ ЗА ДОПОМОГОЮ —ONTO | +---------------------------------------------------+ | | | ДО: | | | | Main: [M1] ---> [M2] (Squashed DB Migration) | | | | Ghost: [D1] ---> [D2] (Old DB Migration) | | \ | | API Branch: [A1] ---> [A2] | | | | КОМАНДА: git rebase —onto main D2 api-branch | | | | ПІСЛЯ: | | | | Main: [M1] ---> [M2] | | \ | | API Branch: [A1’] ---> [A2’] | +---------------------------------------------------+
Синтаксис такий:
git rebase --onto <new-base> <old-upstream> <branch-to-move>
У нашому сценарії:
git rebase --onto main feature-db-migration feature-api-updateЦя команда перекладається так: “Візьми всі коміти в feature-api-update, яких НЕМАЄ в feature-db-migration, і відтвори їх поверх main.”
Вирішення конфліктів під час rebase
Розділ «Вирішення конфліктів під час rebase»Оскільки rebase відтворює коміти послідовно, ви можете зіткнутися з конфліктами на півдорозі. Git зупиниться і попередить вас:
CONFLICT (content): Merge conflict in deployment.yamlerror: could not apply 3a2b1c4... Add memory limitsКоли це трапляється, ви перебуваєте в стані від’єднаного HEAD (detached HEAD) на конкретному кроці rebase, який зазнав невдачі.
- Відкрийте конфліктні файли та вирішіть маркери злиття (
<<<<<<<,=======,>>>>>>>). - Зафіксуйте (stage) вирішені файли за допомогою
git add deployment.yaml. - Не запускайте
git commit. Двигун rebase сам керує комітами. - Скажіть двигуну продовжувати, запустивши
git rebase --continue.
Якщо ви зрозуміли, що rebase був помилкою і ви безнадійно заплуталися в конфліктах, ви завжди можете безпечно вийти:
git rebase --abortЦя команда миттєво припиняє операцію rebase і повертає вашу гілку саме в той стан, у якому вона була до того, як ви ввели git rebase -i.
Чи знали ви?
Розділ «Чи знали ви?»- Проєкт ядра Linux суворо забороняє merge-коміти від учасників. Усі патчі, що подаються до ядра, повинні бути перебазовані (rebase) автором для підтримки ідеально лінійної історії, що гарантує ефективну роботу інструменту
git bisectпри пошуку регресій коду. - Команда
fixupбула введена в Git версії 1.7.0 саме тому, що розробники втомилися від повторюваної ручної роботи з видалення повідомлень комітів у текстовому редакторі щоразу, коли вони використовували командуsquash. - Ви можете налаштувати Git так, щоб він автоматично виконував rebasing щоразу, коли ви отримуєте оновлення з віддаленого репозиторію (pull), виконавши
git config --global pull.rebase true. Це вбереже вас від випадкового створення непотрібних merge-комітів при синхронізації вашої локальної гілки з висхідними змінами. - Команда
execв інтерактивному rebase дозволяє запускати команду оболонки (наприклад, синтаксичний linter або набір юніт-тестів) після кожного застосованого коміту. Якщо тест не проходить, rebase зупиняється, дозволяючи вам негайно виправити зламаний коміт, гарантуючи, що ваша історія є працездатною на кожному етапі.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому це трапляється | Як це виправити |
|---|---|---|
| Примусове оновлення (force push) спільної гілки | Ви зробили rebase гілки, над якою вже працюють інші, переписавши історію, від якої вони залежать. | Негайно повідомте колег. Якщо вони не зробили багато роботи, вони можуть виконати git fetch та git reset --hard origin/branch. Якщо зробили — можливо, доведеться скасувати force-push через reflog. |
| Використання squash, коли планувався fixup | Нерозуміння різниці. Ви опиняєтеся в редакторі, захаращеному п’ятьма різними повідомленнями “fixed typo”, які доводиться видаляти вручну. | Закрийте редактор, скасуйте rebase (git rebase --abort) і почніть заново, використовуючи fixup (або f). |
| Застрягання в циклі редагування | Використання команди edit, внесення змін, але запуск git commit замість git commit --amend. Це додає новий коміт замість модифікації того, що на паузі. | Використайте git reset HEAD~1, щоб скасувати помилковий коміт, внесіть зміни, запустіть git commit --amend, а потім git rebase --continue. |
| Ітеративне вирішення того самого конфлікту | Кілька комітів торкаються одного файлу так, що це конфліктує з новою базою. Вам доводиться вирішувати той самий блок коду 3 рази. | Увімкніть git rerere (Reuse Recorded Resolution). Запустіть git config --global rerere.enabled true. Git запам’ятає, як ви вирішили конфлікт першого разу, і автоматично застосує це рішення. |
| Випадкове видалення комітів | Видалення рядка в текстовому редакторі інтерактивного rebase з думкою, що це лише видаляє повідомлення, не усвідомлюючи, що це видаляє весь коміт. | Скасуйте rebase, якщо помітили одразу. Якщо завершили — скористайтеся git reflog, щоб знайти SHA гілки до rebase, і зробіть git reset --hard до нього. |
| Rebase у неправильному напрямку | Запуск git rebase feature-branch, перебуваючи на main, замість зворотного. | Негайно скасуйте. Якщо завершили — скористайтеся git reflog, щоб повернути main до початкового стану. |
Контрольні запитання
Розділ «Контрольні запитання»Запитання 1: Ви робите rebase гілки функції на main. Посередині процесу Git зупиняється і повідомляє про конфлікт у `service.yaml`. Ви відкриваєте файл, вирішуєте конфлікт і зберігаєте його. Яким є ваш наступний крок, щоб продовжити rebase?
Ви повинні додати вирішений файл до індексу за допомогою `git add service.yaml`, а потім виконати `git rebase --continue`. Ви НЕ повинні запускати `git commit`, оскільки двигун інтерактивного rebase вже активно керує створенням коміту за вас. Запуск команди commit вручну призведе до передчасного завершення поточного стану і порушить автоматизовану послідовність, що призведе до заплутаної історії.Запитання 2: У вас є три коміти: Коміт A (Add Deployment), Коміт B (WIP testing) та Коміт C (Fix Deployment configuration). Ви хочете об'єднати їх усі в один коміт з абсолютно новим повідомленням. Яку послідовність команд інтерактивного rebase вам слід використати?
Вам слід використати `reword` для Коміту A, `fixup` для Коміту B та `fixup` для Коміту C. Інструкція `reword` каже Git застосувати базовий коміт, але зупинитися, щоб дати вам написати нове вичерпне повідомлення. Тим часом команди `fixup` наказують Git влити наступні зміни файлів безпосередньо в Коміт A. Що важливо, `fixup` автоматично відкидає непотрібні повідомлення комітів "WIP" та "Fix", залишаючи вас з одним чистим комітом, який містить усі модифікації під вашим новим повідомленням.Запитання 3: Ви випадково зафіксували API-токен у Коміті 2 вашої гілки функції. Зараз ви на Коміті 6. Ви запускаєте інтерактивний rebase, щоб видалити його. Ви змінюєте інструкцію для Коміту 2 на `edit`. Git призупиняє rebase. Які команди ви запускаєте, щоб видалити токен і продовжити?
Спочатку відкрийте файл, що містить токен, безпечно видаліть токен і збережіть файл. Далі додайте виправлений файл до індексу за допомогою `git addЗапитання 4: Ви на півдорозі складного інтерактивного rebase і розумієте, що припустилися жахливої помилки при вирішенні конфлікту. Файли в повному безладі, і ви хочете повністю вийти і повернутися до стану до того, як ввели команду rebase. Що ви робите?
Ви запускаєте `git rebase --abort`, щоб безпечно завершити операцію. Ця команда діє як негайний запасний вихід, коли ви приголомшені складними конфліктами або розумієте, що припустилися помилки в списку інструкцій. Після виконання цієї команди Git повністю зупиняє двигун rebase і видаляє всі тимчасові файли стану. Потім він чисто скидає ваш робочий каталог і покажчик гілки саме туди, де вони були до початку rebase, гарантуючи, що дані не будуть втрачені.Запитання 5: Ви відправили (push) `feature-auth` у віддалений репозиторій учора, і ваш колега завантажив її собі для тестування. Сьогодні ви розумієте, що історія ваших локальних комітів захаращена. Чи варто запускати інтерактивний rebase, щоб почистити її перед відкриттям Pull Request?
Ні, у цій ситуації не слід запускати інтерактивний rebase. Це прямо порушує Золоте правило rebasing, яке говорить, що ви ніколи не повинні перезаписувати історію, якою поділилися з іншими. Оскільки ваш колега вже завантажив гілку, перезапис вашої локальної історії та force-push призведуть до катастрофічної розбіжності його локального репозиторію з віддаленим. Щоб вирішити це, не порушуючи його робочий процес, ви повинні або узгодити з ним видалення його локальної гілки та завантаження вашої нової, або прийняти захаращену історію і продовжити зі стандартними злиттями (merge).Запитання 6: Ваша команда використовує політику "Squash and Merge" для Pull Request. Ви відгалузили `backend-v2` від `backend-v1`. `backend-v1` щойно була злита в main через squash. Вам потрібно оновити `backend-v2` останніми змінами з main. Чому стандартний `git rebase main` тут є поганою ідеєю?
Стандартний `git rebase main` зазнає невдачі, тому що оригінальні коміти `backend-v1` були об'єднані (squashed), що означає, що їхні окремі криптографічні SHA більше не існують у гілці main. Натомість вони були замінені одним новим комітом з абсолютно іншим SHA. Якщо ви виконаєте стандартний rebase, Git спробує сліпо відтворити ваші оригінальні коміти `backend-v1`, які все ще залишаються в історії `backend-v2`, поверх main, що спричинить масові конфлікти, оскільки main уже містить ці самі зміни в конденсованому вигляді. Щоб безпечно вирішити це, ви повинні використати `git rebase --onto`, щоб хірургічно відокремити та перенести лише конкретні коміти `v2` на нову базу.Практична вправа
Розділ «Практична вправа»У цій вправі ви створите захаращену історію комітів, що містить маніфести Kubernetes та витік секрету, а потім використаєте інтерактивний rebasing, щоб перетворити її на чисту професійну історію.
Налаштування
Розділ «Налаштування»Запустіть наступний bash-скрипт у безпечному порожньому каталозі, щоб згенерувати репозиторій та захаращену історію.
mkdir k8s-rebase-lab && cd k8s-rebase-labgit initgit branch -M mainecho "# K8s Application" > README.mdgit add README.md && git commit -m "Initial commit"git checkout -b feature-web-app
# Коміт 1cat <<EOF > deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata: name: web-appEOFgit add deployment.yaml && git commit -m "add deployment"
# Коміт 2cat <<EOF > configmap.yamlapiVersion: v1kind: ConfigMapmetadata: name: app-configdata: DB_PASSWORD: "super-secret-admin-pass"EOFgit add configmap.yaml && git commit -m "wip: add configmap for db"
# Коміт 3echo " labels: {app: web}" >> deployment.yamlgit add deployment.yaml && git commit -m "fix typo in deployment labels"
# Коміт 4cat <<EOF > service.yamlapiVersion: v1kind: Servicemetadata: name: web-svcEOFgit add service.yaml && git commit -m "add service"
# Коміт 5sed -i.bak 's/super-secret-admin-pass/REDACTED/' configmap.yaml && rm configmap.yaml.bakgit add configmap.yaml && git commit -m "remove password from configmap"
# Коміт 6echo " ports: [{port: 80}]" >> service.yamlgit add service.yaml && git commit -m "finish service ports"Завдання
Розділ «Завдання»Зараз у вас є 6 захаращених комітів у гілці feature-web-app. Ваша мета — використати git rebase -i main, щоб скоротити цю історію до рівно двох чистих комітів.
- Запустити Rebase: Ініціюйте інтерактивний rebase відносно гілки
main. - Консолідувати Deployment: Перевпорядкуйте інструкції так, щоб виправлення помилки в deployment (Коміт 3) йшло одразу після початкового коміту deployment (Коміт 1). Використайте
fixup, щоб об’єднати їх. - Консолідувати Service: Перевпорядкуйте інструкції так, щоб додавання порту сервісу (Коміт 6) йшло одразу після початкового коміту сервісу (Коміт 4). Використайте
fixup, щоб об’єднати їх. - Видалити секрет: Перевпорядкуйте коміти ConfigMap так, щоб видалення (Коміт 5) йшло одразу після додавання (Коміт 2). Використайте
fixup, щоб влити видалення в додавання. Це гарантує, що відкритий пароль ніколи не з’явиться у фінальній історії. - Перейменувати коміти: Використайте команду
rewordдля решти основних комітів, щоб дати їм професійні описові повідомлення.- Коміт 1 повинен мати назву:
feat: Add Web Application Deployment - Коміт 2 повинен мати назву:
feat: Configure Application Services and Environment(Ви можете використати squash/fixup, щоб об’єднати коміти сервісу та configmap разом).
- Коміт 1 повинен мати назву:
Критерії успіху
Розділ «Критерії успіху»- Запустіть
git log --oneline. Ви повинні побачити рівно три коміти загалом: початковий коміт README, коміт Deployment та коміт Service/ConfigMap. - Запустіть
git log -p. Переконайтеся, що рядокsuper-secret-admin-passне з’являється ніде в історії diff. - Не повинно бути комітів, що містять повідомлення “fix typo”, “wip” або “finish service”.
Посібник з рішення
- Запустіть
git rebase -i main. - Початковий текстовий редактор виглядатиме так (скорочені хеші):
pick 1111111 add deploymentpick 2222222 wip: add configmap for dbpick 3333333 fix typo in deployment labelspick 4444444 add servicepick 5555555 remove password from configmappick 6666666 finish service ports- Відредагуйте файл, щоб змінити порядок і команди. Перемістіть пов’язані елементи разом. Використовуйте
rewordдля базових елементів іfixupдля модифікацій.
reword 1111111 add deploymentfixup 3333333 fix typo in deployment labelsreword 4444444 add servicefixup 6666666 finish service portspick 2222222 wip: add configmap for dbfixup 5555555 remove password from configmap(Примітка: щоб об’єднати Service та ConfigMap в один коміт, як просилося в останньому кроці, ви можете змінити pick на configmap на fixup, прикріплений до service).
reword 1111111 add deploymentfixup 3333333 fix typo in deployment labelsreword 4444444 add servicefixup 6666666 finish service portsfixup 2222222 wip: add configmap for dbfixup 5555555 remove password from configmap- Збережіть і закрийте редактор.
- Git двічі зупиниться, щоб дозволити вам відредагувати повідомлення комітів для двох елементів, які ви позначили як
reword. - Введіть
feat: Add Web Application Deploymentдля першого таfeat: Configure Application Services and Environmentдля другого. - Перевірте успіх за допомогою
git log -p.
Наступний модуль
Розділ «Наступний модуль»Тепер, коли ви вмієте створювати ідеальну історію, настав час дізнатися, як відновитися, коли щось піде зовсім не так, у Модулі 4: Мережа безпеки.