1. Спочатку зупинити лавину, потім лікувати
Claude пішов не туди — і хочеться відповісти емоційно: «ні-ні, виправ усе назад і зроби нормально». У цей момент ви втрачаєте керування. У вас уже один поганий diff, а таким запитом ви дописуєте другий, ще менш зрозумілий.
Приклад із Commerce OS. Ви просили винести логіку ухвалення рішення щодо refund із RefundController в окремий RefundService. Обсяг вузький. А Claude «заодно» зачепив OrderService, змінив таймаут у PaymentProviderClient і пройшовся форматуванням по чужому файлу. Скажете «полагодь усе» — він лагодить поверх власного хаосу. Стати зрозуміліше не стане.
Спершу зупиніть лавину: перестаньте просити правки й подивіться на стан проєкту як на факт. Git для цього і потрібен. Він не сперечається й не обіцяє, що «тепер точно все готово». Він показує: які файли змінено, чим вони відрізняються, які коміти є.
| Ситуація | Імпульсивна реакція | Робоча реакція |
|---|---|---|
| Claude зачепив зайві файли | «Виправ усе назад» | Подивитися git status, потім git diff по кожному підозрілому файлу |
| Тести впали після серії правок | «Спробуй ще раз полагодити» | Вирішити, що залишити, а що відкотити, і повернути проєкт у зрозумілий стан |
| З’явився поганий коміт | «Зараз поверх нього зробимо хороший» | Обрати усвідомлений rollback: revert, reset або нову гілку |
Розробку за підтримки ШІ дорослою робить не швидкість першої правки. А вміння спокійно відновитися після невдалої.
2. Git розділяє чернетку, staging і історію
Щоб команди відкату не здавалися заклинаннями, тримайте в голові три шари Git. Без цього restore, revert і reset зливаються в кашу: усе «щось відкочується», а що саме — незрозуміло.
Модель проста. Файли на диску — поточний робочий стан. Staging area — зміни, які чекають на наступний коміт. Історія комітів — те, що вже зафіксовано.
| Шар | Що це | Що зазвичай із ним роблять |
|---|---|---|
| Робочий каталог | Файли на диску просто зараз | Дивляться git diff, повертають через git restore |
| Підготовлені зміни | Те, що вже додали в staging area | Перевіряють перед комітом, за потреби прибирають зі staged |
| Історія комітів | Уже збережені кроки | Відміняють через git revert або локально переписують через git reset |
Чому це важливо в AI-роботі? Тому що Claude за один прохід може зачепити всі три шари. Просто змінити файли. Підготувати їх до коміту. Або вже створити коміт. Команда відновлення залежить від того, де саме зараз проблема.
Ще одна межа. Git повертає код і історію проєкту, але не зовнішні ефекти. Запустили Claude міграцію БД, смикнули зовнішній сервіс, змінили віддалену систему — Git поверне файли, але не це. У цій лекції ми говоримо про файлове та комітне відновлення. Те, що Git робить дуже добре.
3. Перша пара команд: git status і git diff
Після невдалої ітерації хочеться одразу натиснути Undo. Але перший крок — діагностичний: зрозуміти масштаб. Команд для цього потрібно дві.
git status відповідає на питання що зачеплено: на якій ви гілці, які файли змінено. Часто вже видно, що Claude вийшов за scope. Працювали з refund-flow, а у виводі OrderService і PaymentProviderClient — сигнал подивитися diff по них.
git status
# На гілці refund-refactor
# Зміни, які не підготовлено до коміту:
# modified: src/main/java/com/acme/commerce/refund/RefundController.java
# modified: src/main/java/com/acme/commerce/order/OrderService.java
# modified: src/main/java/com/acme/commerce/payments/PaymentProviderClient.java
git diff відповідає на питання у чому саме проблема. Відкривати diff по всьому проєкту одразу не потрібно — на довгому завданні зручніше по конкретних файлах.
git diff -- src/main/java/com/acme/commerce/payments/PaymentProviderClient.java
# - private int timeoutMs = 5000;
# + private int timeoutMs = 30000;
# + // додаткові зміни форматування нижче
Тут і видно, що правка не стосується завдання. Просили винести refund logic — отримали зміну таймауту в payment-клієнті. Це не «може, знадобиться». Це зайве: або поясніть серйозною причиною, або відкочуйте без сентиментів.
Маршрут ухвалення рішення:
flowchart TD
A[Невдала AI-ітерація] --> B[git status]
B --> C[git diff]
C --> D{Є потрібні зміни?}
D -- Ні --> E[Відкотити зайве]
D -- Так --> F{Є commit?}
F -- Ні --> G[git restore по файлах]
F -- Так --> H{Історія вже shared?}
H -- Так --> I[git revert]
H -- Ні --> J[git reset або нова гілка]
Алгоритм прибирає метушню. Спочатку бачите реальність, потім обираєте мінімальний rollback.
4. Карта rollback: restore, revert, reset, гілка
На словах команди відкату схожі: щось повернути, прибрати, скасувати. На практиці задачі різні, і не та команда ускладнює життя. Думайте не назвами, а сценаріями: що сталося і наскільки далеко ви зайшли.
| Команда / підхід | Коли застосовувати | Що змінюється | Де потрібна обережність |
|---|---|---|---|
|
Зайві правки є у файлах, але коміту ще немає | Повертає вміст файла | Можна випадково стерти корисні незбережені зміни |
|
Поганий коміт уже є, і історію краще не переписувати | Створює новий коміт-відміну | Історія стає довшою, але безпечнішою |
|
Поганий коміт є лише у вашій локальній гілці | Зсуває вказівник гілки назад | Небезпечний на спільній історії та за звички робити все бездумно |
| Нова гілка | Поточна спроба занадто брудна й дорога для порятунку | Ви починаєте заново з чистої бази | Потрібно не втратити корисне, якщо воно все ж таки було |
Коміту ще немає — найм’якший варіант git restore. Основний інструмент проти «Claude пішов не туди», особливо коли корисне перемішане із зайвим у кількох файлах.
git restore src/main/java/com/acme/commerce/order/OrderService.java
git restore src/main/java/com/acme/commerce/payments/PaymentProviderClient.java
git diff
# у diff залишилися лише зміни, пов’язані з refund
Файл уже в staging area — спершу приберіть його звідти, потім поверніть вміст:
git restore --staged src/main/java/com/acme/commerce/order/OrderService.java
git restore src/main/java/com/acme/commerce/order/OrderService.java
# файл прибрано і зі staged, і з робочого diff
Поганий крок став комітом — виходить git revert. Спокійна команда: не робить вигляд, що коміту не було, а чесно створює новий, який його скасовує. Для історії, яку вже бачили інші, це зазвичай найкращий шлях.
git log --oneline -3
# a1b2c3d M2: виділив RefundService
# 7f8e9d0 M1: додав тести refund-flow
git revert a1b2c3d
# створено новий commit, що скасовує зміни a1b2c3d
git reset сильніший і небезпечніший. Він не скасовує коміт, а пересуває гілку назад — ніби кроків не було. Зручно, якщо коміт локальний і його ніхто не бачив. На спільній історії — погана ідея. Правило просте: сумніваєтеся — revert, не reset.
git reset --hard HEAD~1
# останній локальний commit прибрано з гілки
# усі незбережені зміни також буде втрачено
І чесний сценарій: спробу дешевше кинути, ніж рятувати. Іноді найрозумніший крок — Claude накоїв стільки, що ви вже самі не розумієте, що в diff корисне, а що сміття.
git switch -c refund-refactor-retry main
# нова чиста гілка від main для повторної акуратної спроби
Стара гілка залишається як слід спроби. Іноді з неї потім дістанете кілька ідей, але рятувати все відразу ви більше не зобов’язані.
5. Маленькі коміти роблять rollback дешевим
Проблеми з відкатом починаються раніше помилки — у момент поганої дисципліни комітів. Один гігантський коміт «refactor refund flow + cleanup + fix tests + small improvements» частково відкотити майже не можна: або скасовуєте все, або ріжете коміт вручну.
Дешевше жити з маленькими milestone-комітами. Зробили характеризаційні тести — зафіксували. Виділили RefundService без зміни публічного контракту — зафіксували. Винесли поріг схвалення refund у конфіг — зафіксували. Тоді будь-який rollback локальний, а не катастрофічний.
git log --oneline -3
# f34a1d2 M3: виніс refund-threshold в application.yml
# c91be10 M2: виділив RefundService
# 6aa43ff M1: додав характеризаційні тести refund-flow
Припустімо, поганим виявився крок M3. Ви не чіпаєте корисні M1 і M2 — відкочуєте лише останній крок і продовжуєте з зрозумілої бази. Не втрачаєте цілий день через одну невдалу ідею.
Тому milestone і commit ідуть поруч. Milestone без коміту — лише намір. Коміт без milestone — абстрактний запис в історії. Разом вони дають дешевий rollback: скасувати один зрозумілий крок, не чіпаючи решту.
6. Сценарій Commerce OS: рятуємо refund-flow
Зберемо все в одну сцену. У Commerce OS завдання: винести decision logic із RefundController в RefundService, не змінюючи публічний API і не чіпаючи інтеграцію з платіжним провайдером. Обсяг вузький, тести на refund-flow додано на минулому milestone. Ви дали Claude зрозумілий крок — а він увімкнув режим «ініціативний стажер».
Після правок змінено не лише RefundController і новий RefundService, а й OrderService із PaymentProviderClient. У PaymentProviderClient зріс таймаут, у OrderService — перестановки методів і зайве форматування. Саме той момент, коли не можна писати «виправ усе». Спершу дивіться на Git.
git status
git restore src/main/java/com/acme/commerce/order/OrderService.java
git restore src/main/java/com/acme/commerce/payments/PaymentProviderClient.java
./gradlew test --tests "*Refund*" # ЗБІРКА УСПІШНА
git add src/main/java/com/acme/commerce/refund/RefundController.java \
src/main/java/com/acme/commerce/refund/RefundService.java \
src/test/java/com/acme/commerce/refund/RefundControllerTest.java
git commit -m "Виділив RefundService без зміни публічного контракту"
Сенс не в командах, а в порядку. Спершу прибираєте зайве. Потім запускаєте перевірку по refund-напрямку. І тільки коли залишається чистий diff всередині узгодженого scope — робите commit.
Варіант складніший: поганий коміт уже збережено локально. Не надіслано нікому, живе лише у вашій гілці — допустимий акуратний reset. Потрапив у спільну історію — беріть revert. Історія буде довшою, але це чесна довжина: через два дні ви або колега зрозумієте, що був невдалий крок, його окремо скасували, робота пішла далі.
І доросла думка: не кожен diff треба рятувати. Гілка настільки брудна, що простіше відкрити нову й повторити вузький крок із нуля. Новачкам це здається «втратою прогресу», але частіше економить години. Витягувати золото з суцільного піску — дорогий спосіб не визнати, що перша спроба була поганою.
7. Підключаємо Claude знову після відкату
Після rollback головне — не повернутися в ту саму пастку. Відкотили зайве, а потім знову пишете «ну тепер дороби все нормально» — історія повториться. Claude має отримати не докір, а чистий вузький стан проєкту й точне наступне завдання.
Спирайтеся на task spec і поточний diff. Скажіть буквально: ось гілка, ось які файли залишилися чистими, ось чого робити не можна, ось обов’язкова команда перевірки. Тут корисна нова чітка формулювання, а не «ну ти ж пам’ятаєш, що було пів години тому».
Зараз у гілці refund-refactor залишено лише зміни по refund-flow.
Не чіпай OrderService і PaymentProviderClient.
Зроби лише один крок: перенеси decision logic у RefundService,
збережи публічний API RefundController і не змінюй конфігурацію таймаутів.
Після змін покажи список файлів і команду перевірки.
Це працює краще за емоційне «перероби». Ви задаєте Claude не настрій, а межі. А Git до цього моменту прибрав хаос і повернув проєкт у стан, де наступна ітерація знову дешева й зрозуміла.
Головний зсув уроку: Git-first recovery — це не недовіра до Claude і не «робити все вручну». Це звичка спочатку працювати з фактами: дивитися на diff, обирати мінімальний rollback, тримати історію гілки зрозумілою. У такому режимі Claude перестає бути джерелом паніки і стає тим, ким має бути в здоровому workflow: сильним помічником, якого все одно потрібно читати.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ