Блог

Назад
Git під капотом: Відновлення даних, інтерактивний ребейз та пошук через Pickaxe

20 лютого 2026 р.

Git під капотом: Відновлення даних, інтерактивний ребейз та пошук через Pickaxe

Більшість розробників сприймають Git лише як простий інструмент для резервного копіювання файлів, обмежуючись командами git add, git commit та git push. Проте насправді Git - це повноцінна контентно-адресована файлова система, якою керує графова база даних об'єктів (комітів, дерев та блобів).

Коли проект масштабується, а команди починають працювати над великою кількістю паралельних фіч, вміння ефективно взаємодіяти з цією базою стає життєво необхідним. У цьому посібнику ми детально розглянемо просунуті механіки Git: реорганізацію історії комітів, порятунок вихідного коду після катастрофічного скидання стану, автоматизацію пошуку багів та глибокий пошук змін в історії проекту.


1. Інтерактивний ребейз: Наведення ладу в історії

Перед тим як відправляти свою гілку на рецензування колегам, слід переконатися, що історія ваших комітів виглядає чистою та зрозумілою. Пул-реквест, який містить два десятки комітів із повідомленнями на кшталт "wip", "fixed typo" чи "debug", суттєво уповільнює процес перевірки та забруднює історію проекту. Інтерактивне перебазування (interactive rebase) дозволяє переписати локальну історію до того, як вона стане публічною.

Щоб запустити цей процес, виконайте команду в консолі, вказавши, на скільки кроків назад ви хочете змінити історію:

# Редагувати останні 6 комітів на поточній гілці
git rebase -i HEAD~6

Ця команда відкриє текстовий редактор за замовчуванням зі списком ваших комітів, відсортованих від найстарішого до найновішого:

pick a1b2c3d Feat: add user auth controller
pick e4f5g6h Fix: validation logic
pick i7j8k9l wip: test database
pick m0n1o2p typo
pick q3r4s5t Feat: add token regeneration

Для редагування списку замініть слово pick перед потрібними комітами на відповідні інструкції:

  • reword (або r): Зберігає зміни з цього коміту, але зупиняє процес для того, щоб ви могли змінити текст повідомлення (наприклад, для відповідності вимогам лінтера).
  • squash (або s): Об'єднує цей коміт із попереднім, дозволяючи об'єднати їхні описи.
  • fixup (або f): Об'єднує цей коміт із попереднім, але повністю видаляє його опис (ідеально для прибирання дрібних правок та виправлень помилок).
  • edit (або e): Призупиняє ребейз на цьому коміті, даючи вам можливість змінити вміст файлів, додати нові файли або розділити один коміт на кілька частин.

Вирішення конфліктів під час ребейзу

Якщо кілька комітів змінюють одні й ті самі рядки коду, процес зупиниться, і Git виведе попередження про конфлікт. Не варто панікувати чи скасовувати процес. Відкрийте конфліктні файли у вашому редакторі коду, знайдіть маркери конфлікту (<<<<<<< та >>>>>>>), виберіть правильний варіант коду та збережіть файл.

Після усунення конфліктів виконайте такі команди в терміналі:

# Додати виправлені файли в індекс
git add src/controllers/auth.js

# Продовжити процес ребейзу
git rebase --continue

Якщо ви зрозуміли, що зробили помилку, і хочете повністю скасувати процес ребейзу, повернувши гілку до початкового стану, виконайте:

git rebase --abort

2. Аварійне відновлення: Рятуємо видалений код

Напевно, кожен розробник хоча б раз випадково виконував команду git reset --hard і розумів, що затер важливі файли, або видаляв не ту гілку. Оскільки Git зберігає всі дані у своїй базі як об'єкти, ваш код майже ніколи не зникає миттєво.

Сценарій А: Відновлення комітів через git reflog

Git веде спеціальний внутрішній журнал, у якому записує всі зміни покажчика HEAD за останні 90 днів. Цей журнал називається reflog.

git reflog

У виводі команди ви побачите список усіх ваших дій: перемикання гілок, коміти, злиття та скидання:

e4f5g6h HEAD@{0}: reset: moving to HEAD~2
a1b2c3d HEAD@{1}: commit: Feat: add user auth controller
f9d8s7a HEAD@{2}: checkout: moving from main to dev

Щоб відновити стан вашої гілки до помилкового скидання, знайдіть потрібний крок в історії (наприклад, HEAD@{1}) та виконайте скидання на цей ідентифікатор:

git reset --hard HEAD@{1}

Сценарій Б: Відновлення незбережених staged файлів через git fsck

Що робити, якщо ви виконали git reset --hard, але ваші файли були лише додані в індекс (git add) і ніколи не комітилися? У журналі git reflog не буде жодних записів про ці зміни.

Проте, як тільки ви виконуєте команду git add, Git одразу створює об'єкт вмісту (так званий blob) у своїй внутрішній базі даних, навіть якщо ви не зробили коміт. Знайти такі покинуті об'єкти можна за допомогою низькорівневої перевірки файлової системи:

git fsck --lost-found

Ця команда виведе список усіх втрачених об'єктів у базі даних:

dangling blob 0320b556abad0c96fe268505dfd85e540fe41c27
dangling blob 3770afac5696caebe41ec29070957e81ff750212

Git зберігає ці знайдені об'єкти у каталозі .git/lost-found/other/. Ви можете переглянути їхній вміст за допомогою команди cat-file:

# Переглянути вміст конкретного блобу
git cat-file -p 0320b556abad0c96fe268505dfd85e540fe41c27

Коли ви знайдете потрібний файл, просто перенаправте його вміст назад у файл вашого проекту:

git cat-file -p 0320b556abad0c96fe268505dfd85e540fe41c27 > src/utils/recovered.js

Ваш незбережений файл тепер успішно відновлено!


3. Автоматичний пошук багів за допомогою git bisect

У великих проектах буває важко визначити, який саме комміт призвів до виникнення багу. Ручний перебір десятків чи сотень комітів може забрати години. Інструмент git bisect дозволяє автоматизувати цей процес за допомогою алгоритму бінарного пошуку.

                          Бінарний пошук Git Bisect
                          
[Гарний коміт] -- [x] -- [x] -- [?] -- [x] -- [x] -- [Поганий коміт]
                                 |
                        Тестуємо цей коміт!

Ручний процес бінарного пошуку

Запустіть процес бінарного пошуку та вкажіть його межі:

git bisect start
# Позначаємо поточний стан як поганий (баг присутній)
git bisect bad
# Вказуємо хеш коміту в минулому, коли багу точно не було
git bisect good c4e5f6a

Git автоматично переключить ваш репозиторій на коміт посередині між цими двома точками. Запустіть додаток або тести, щоб перевірити наявність багу.

  • Якщо баг присутній: виконайте git bisect bad.
  • Якщо багу немає: виконайте git bisect good.

Git продовжуватиме ділити історію навпіл, поки точно не вкаже на перший коміт, який зламав код. Після завершення вийдіть із режиму пошуку:

git bisect reset

Автоматизація пошуку через скрипти

Якщо у вас є написаний тест (наприклад, Jest тест, що падає при виникненні багу), ви можете повністю доручити пошук комп'ютеру. Git сам перевірятиме кожен крок за допомогою вашого скрипту:

git bisect start HEAD c4e5f6a
# Автоматичний запуск юніт-тестів на кожному кроці
git bisect run npm run test:unit

Git самостійно перевірить історію та знайде зламаний коміт за лічені секунди.


4. Пошук змін в історії за допомогою Pickaxe

Стандартні інструменти пошуку (як-от grep або ripgrep) шукають дані лише в поточному стані файлів на жорсткому диску. Якщо потрібна функція була видалена кілька місяців тому, ви не знайдете її цими методами. Для таких завдань у Git є вбудований двигун пошуку Pickaxe.

Щоб знайти всі коміти, у яких додавався чи видалявся певний рядок коду (наприклад, назва функції), використовуйте прапорець -S:

# Шукати коміти, у яких з'явився чи зник рядок 'calculateTaxRates'
git log -S "calculateTaxRates" --oneline

Щоб переглянути детальні зміни (пачі) для кожного з цих комітів, додайте прапорець -p:

git log -S "calculateTaxRates" -p

Якщо вам потрібен пошук за регулярними виразами, використовуйте прапорець -G:

# Пошук за регулярним виразом в історії дифу
git log -G "const\s+[\w]+Settings\s*=" --oneline

Це дуже допомагає при розслідуванні інцидентів, коли потрібно з'ясувати, коли саме з конфігурації видалили ключі доступу або змінили адресу зовнішнього сервера.


5. Просунуті методи роботи зі Stash

Команда git stash корисна для швидкого очищення робочої області, але її додаткові параметри дають набагато більше гнучкості.

Збереження невідстежуваних файлів

За замовчуванням git stash зберігає зміни тільки в тих файлах, які вже відстежуються системою. Нові створені файли ігноруються. Щоб зберегти їх у сховище, використовуйте прапорець -u (untracked):

git stash -u

Інтерактивне збереження

Якщо ви змінили кілька файлів, але хочете відправити в тимчасове сховище лише частину змін, запустіть інтерактивний режим:

git stash -p

Git покаже кожен змінений шматок коду окремо та запитає:

  • y: Зберегти цей шматок у stash.
  • n: Не зберігати його.
  • q: Вийти та зберегти лише вибране.

Створення іменованих стэшів

Ніколи не покладайтеся на порядкові номери на кшталт stash@{0}, оскільки вони змінюються при кожному новому додаванні. Задавайте кастомний опис:

git stash save "рефакторинг процесу оплати - в процесі"

Для перегляду списку та застосування конкретного збереження:

git stash list
# Застосувати конкретний stash зі списку без його видалення
git stash apply stash@{2}

Щоб застосувати зміни та одразу видалити їх зі стеку:

git stash pop stash@{2}

Використання цих просунуті методів роботи з Git дозволить вам підтримувати порядок в історії змін, надійно захистить ваш код від випадкового видалення та значно спростить відладку.


6. Лайфхак: Автоматизація рутини через складні аліаси в .gitconfig

Часто розробники обмежуються простими скороченнями на кшталт co = checkout. Але Git дозволяє створювати набагато складніші аліаси, що запускають shell-скрипти прямо з терміналу Git. Це ідеально підходить для рутинних операцій, які складаються з кількох кроків.

Відкрийте ваш глобальний файл конфігурації ~/.gitconfig та додайте наступні просунуті аліаси у секцію [alias]:

[alias]
  # 1. Видалення всіх локальних гілок, які вже були злиті з main/master
  sweep = "!f() { git checkout main && git pull && git branch --merged | grep -v '\\*' | grep -v 'main' | xargs -r git branch -d; }; f"

  # 2. Красивий детальний лог з деревом та часом комітів
  lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative

  # 3. Швидке скасування останнього коміту зі збереженням змін у робочій області
  undo = reset --soft HEAD~1

  # 4. Швидке створення нової гілки та перехід на неї
  new = checkout -b

  # 5. Додавання всіх змін, створення коміту та push одним рядком (корисно для швидких правок документації)
  lazy = "!f() { git add -A && git commit -m \"$1\" && git push; }; f"

Як це працює:

  • Символ оклику ! на початку аліасу вказує Git, що наступна команда повинна виконуватися в оточенні стандартної оболонки shell (sh/bash), а не як внутрішня команда Git.
  • Визначення функції f() { ... }; f є класичним шаблоном для передачі позиційних параметрів (наприклад, опису коміту $1 в аліасі lazy).

Використання таких скорочень дозволяє перетворити довгі ланцюжки команд на швидкі комбінації, які ви виконуєте за частку секунди. Наприклад, виконавши git lazy "docs: update readme", ви миттєво додасте зміни, створите коміт та відправите код на сервер.