Перейти до вмісту
20 хв читання

Git за межами основ: Воркфлоу, що економлять години щотижня

Interactive rebase, cherry-pick, bisect, worktrees, порятунок через reflog та стратегії гілкування, які реально працюють. Git-команди, якими я користуюся щодня, а більшість розробників не знає.

Поділитися:X / TwitterLinkedIn

Більшість розробників вивчають п'ять Git-команд і на цьому зупиняються. add, commit, push, pull, merge. Можливо, checkout і branch, якщо почувають себе сміливо. Цього вистачає на перший рік. Потім ваша гілка має 47 комітів з повідомленнями на кшталт «fix» і «wip» і «please work», ви випадково скинули щось, чого не слід було, і витрачаєте 40 хвилин на Stack Overflow, намагаючись скасувати невдалий мердж.

Я використовую Git роками. Не побіжно — інтенсивно. Кілька гілок, кілька репозиторіїв, кілька співпрацівників, цілий день, кожен день. Далі — команди та воркфлоу, якими я реально користуюсь. Не ті, що добре виглядають у туторіалі. Ті, що економлять мені реальний час, щотижня.

Interactive Rebase: Приберіть свій хаос, поки ніхто не бачить#

Ваша гілка має дванадцять комітів. Половина з них — «fix typo». Один каже «undo previous commit». Інший — «actually fix it this time». Ви збираєтесь відкрити PR. Нікому не потрібно бачити цю історію.

Interactive rebase — це спосіб переписати історію вашої гілки перед тим, як поділитися нею. Він дозволяє об'єднувати коміти разом, переформулювати повідомлення, змінювати порядок або видаляти їх повністю.

Основна команда#

bash
git rebase -i HEAD~5

Це відкриває редактор з вашими останніми 5 комітами, від найстарішого:

bash
pick a1b2c3d Add user authentication endpoint
pick d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
pick l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth tests

Кожен рядок починається з команди. Змініть pick на одну з цих:

  • squash (або s) — Об'єднати цей коміт з тим, що вище, поєднати повідомлення
  • fixup (або f) — Те саме, що squash, але відкинути повідомлення цього коміту
  • reword (або r) — Зберегти коміт, але змінити його повідомлення
  • drop (або d) — Видалити цей коміт повністю
  • edit (або e) — Зупинити rebase на цьому коміті, щоб ви могли його змінити

Реальний сеанс очищення#

Ось що я реально роблю. Та брудна історія вище перетворюється на:

bash
pick a1b2c3d Add user authentication endpoint
fixup d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
fixup l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth tests

Збережіть і закрийте. Тепер у вас три чистих коміти замість п'яти. Виправлення помилки друку складається в коміт автентифікації. Виправлення бага rate limit складається в коміт rate limiting. Ваш ревю'ер PR бачить чистий, логічний прогрес.

Зміна порядку комітів#

Ви можете буквально переставляти рядки. Якщо коміт тестів повинен іти перед комітом rate limiting, просто перемістіть рядок:

bash
pick a1b2c3d Add user authentication endpoint
pick p3q4r5s Update auth tests
pick h7i8j9k Add rate limiting

Git переграє ваші коміти в цьому новому порядку. Якщо є конфлікти, він зупиниться і дасть вам їх вирішити.

Скорочення Autosquash#

Якщо ви знаєте, що коміт є виправленням попереднього, позначте це при коміті:

bash
git commit --fixup=a1b2c3d

Це створює коміт з повідомленням fixup! Add user authentication endpoint. Потім при rebase:

bash
git rebase -i --autosquash HEAD~5

Git автоматично переставляє fixup-коміти прямо під їхні цілі та позначає їх як fixup. Ви просто зберігаєте і закриваєте. Без ручного редагування.

Я використовую це постійно. Це найшвидший спосіб ітерувати на гілці, зберігаючи фінальну історію чистою.

Золоте правило#

Ніколи не робіть rebase комітів, які вже запушені до спільної гілки. Якщо інші люди базували роботу на цих комітах, переписування історії спричинить реальні проблеми. Робіть rebase своїх власних feature-гілок перед мерджем. Ніколи не робіть rebase main.

Якщо ви вже запушили свою feature-гілку і потрібно зробити rebase:

bash
git push --force-with-lease

Прапорець --force-with-lease безпечніший за --force. Він відмовляється пушити, якщо хтось інший запушив у ту ж гілку з часу вашого останнього fetch. Він не запобігає всім проблемам, але ловить найпоширенішу.

Cherry-Pick: Хірургічне перенесення комітів#

Cherry-pick бере конкретний коміт з однієї гілки та застосовує його до іншої. Не мердж. Не rebase. Просто один коміт, чисто застосований.

Коли я це реально використовую#

Найпоширеніший сценарій: я виправив баг на своїй feature-гілці, але виправлення також потрібно в main або release-гілці прямо зараз. Я не хочу мерджити всю свою feature-гілку. Я хочу лише це одне виправлення.

bash
# Знайти хеш коміту виправлення
git log --oneline feature/user-auth
# a1b2c3d Fix null pointer in session validation
 
# Переключитися на main і cherry-pick
git checkout main
git cherry-pick a1b2c3d

Готово. Виправлення на main як новий коміт з тими ж змінами.

Cherry-Pick без комітування#

Іноді ви хочете застосувати зміни, але не комітити їх ще. Можливо, ви хочете об'єднати кілька cherry-pick в один коміт або трохи модифікувати зміни:

bash
git cherry-pick --no-commit a1b2c3d

Зміни стейджені, але не закомічені. Ви можете їх модифікувати, додати більше змін, потім комітити, коли готові.

Cherry-Pick діапазону#

Потрібно кілька послідовних комітів? Використовуйте синтаксис діапазону:

bash
git cherry-pick a1b2c3d..f6g7h8i

Це cherry-pick все після a1b2c3d до та включаючи f6g7h8i. Зверніть увагу, що сам a1b2c3d виключений. Якщо хочете його включити:

bash
git cherry-pick a1b2c3d^..f6g7h8i

Обробка конфліктів#

Конфлікти cherry-pick працюють як конфлікти мерджу. Коли один виникає:

bash
# Git скаже вам, що є конфлікт
# Виправте конфліктні файли, потім:
git add .
git cherry-pick --continue

Або якщо ви передумали:

bash
git cherry-pick --abort

Одне застереження: cherry-picked коміти створюють нові хеші комітів. Якщо ви пізніше мерджите оригінальну гілку, Git зазвичай достатньо розумний, щоб обробити дублювання. Але якщо ви агресивно cherry-pick між гілками, які врешті зімерджуються, можете побачити неочікувані конфлікти. Використовуйте хірургічно, а не як стратегію мерджу.

Git Bisect: Бінарний пошук багів#

Щось зламалось. Ви знаєте, що воно працювало два тижні тому. Відтоді було 200 комітів. Який зламав?

Ви можете перевіряти кожен коміт вручну. Або можете використати git bisect, який використовує бінарний пошук для знаходження точного коміту-порушника за log2(n) кроків. Для 200 комітів це близько 7-8 перевірок замість 200.

Ручний спосіб#

bash
# Почати bisect
git bisect start
 
# Позначити поточний коміт як поганий (баг існує тут)
git bisect bad
 
# Позначити відомий добрий коміт (баг тут не існував)
git bisect good v2.1.0

Git переключає на коміт посередині між добрим і поганим. Протестуйте його. Потім:

bash
# Якщо баг існує на цьому коміті:
git bisect bad
 
# Якщо баг не існує на цьому коміті:
git bisect good

Git звужує діапазон вдвічі кожен раз. Після 7-8 кроків він повідомляє:

bash
a1b2c3d4e5f6g7h is the first bad commit
commit a1b2c3d4e5f6g7h
Author: Some Developer <dev@example.com>
Date: Tue Feb 18 14:23:01 2026 +0300
 
    Refactor session handling to use async middleware

Тепер ви знаєте точно, який коміт вніс баг. Коли закінчите:

bash
git bisect reset

Це поверне вас туди, де ви почали.

Автоматизований спосіб (ось де справжня сила)#

Якщо у вас є тест, що може виявити баг, ви можете автоматизувати весь процес:

bash
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run npm test -- --grep "session validation"

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

Ви можете використовувати будь-який скрипт:

bash
git bisect run ./test-regression.sh

Де test-regression.sh це:

bash
#!/bin/bash
npm run build 2>/dev/null || exit 125  # 125 означає "пропустити цей коміт"
npm test -- --grep "session" || exit 1  # 1 означає "поганий"
exit 0                                   # 0 означає "добрий"

Код виходу 125 особливий — він каже bisect пропустити цей коміт (корисно, якщо коміт не компілюється). Це одна з тих можливостей, що здається нішевою, поки вона вам не знадобиться, і тоді вона зберігає вам цілий день.

Реальний сеанс Bisect#

Ось як це виглядає на практиці:

bash
$ git bisect start
$ git bisect bad HEAD
$ git bisect good abc1234
Bisecting: 97 revisions left to test after this (roughly 7 steps)
[def5678...] Commit message here
 
$ npm test -- --grep "login flow"
# Тести падають
 
$ git bisect bad
Bisecting: 48 revisions left to test after this (roughly 6 steps)
[ghi9012...] Another commit message
 
$ npm test -- --grep "login flow"
# Тести проходять
 
$ git bisect good
Bisecting: 24 revisions left to test after this (roughly 5 steps)
...
 
# Після ~7 ітерацій:
abc1234def5678ghi is the first bad commit

Сім кроків, щоб знайти голку в стозі з 200 комітів.

Git Worktrees: Кілька гілок, нуль стешів#

Це найменш використовувана можливість Git. Я впевнений, що більшість розробників не знає про її існування.

Проблема: ви глибоко в feature-гілці. Файли змінені повсюди. Тоді хтось каже «можеш швиденько глянути цей продакшен-баг?» У вас три варіанти:

  1. Стешити все, переключити гілки, виправити, переключитися назад, попнути стеш. Сподіватися, що нічого не піде не так.
  2. Комітити свою напівготову роботу з повідомленням «wip». Некрасиво, але функціонально.
  3. Заклонити репозиторій ще раз в іншу директорію.

Або варіант 4: worktrees.

Що таке Worktree#

Worktree — це друга (або третя, або четверта) робоча директорія, пов'язана з тим самим репозиторієм. Кожен worktree має свою переключену гілку, свої робочі файли, свій індекс. Але вони ділять ті самі .git-дані, тож ви не дублюєте весь репозиторій.

Додавання Worktree#

bash
# Ви на feature/user-auth, глибоко в роботі
# Потрібно виправити баг на main:
 
git worktree add ../hotfix-session main

Це створює нову директорію ../hotfix-session з переключеним main. Ваша поточна директорія залишається точно такою, якою була. Нічого стешено, нічого комітнуто, нічого порушено.

bash
cd ../hotfix-session
# Виправити баг
git add .
git commit -m "Fix null pointer in session validation"
git push origin main
cd ../my-project
# Продовжити роботу над своєю фічею, ніби нічого не трапилось

Створення нової гілки в Worktree#

bash
git worktree add ../hotfix-nav -b hotfix/nav-crash main

Це створює worktree І створює нову гілку hotfix/nav-crash на основі main.

Керування Worktrees#

bash
# Перелічити всі worktrees
git worktree list
# /home/dev/my-project         abc1234 [feature/user-auth]
# /home/dev/hotfix-session     def5678 [main]
 
# Видалити worktree, коли закінчили
git worktree remove ../hotfix-session
 
# Якщо директорія вже видалена:
git worktree prune

Чому це краще за стеш#

Стеш підходить для швидких переключень контексту. Але worktrees кращі для будь-чого, що займає більше п'яти хвилин:

  • Ваша IDE залишається відкритою на вашій feature-гілці. Без переіндексації, без втрати позиції прокрутки.
  • Ви можете запускати тести в обох директоріях одночасно.
  • Немає ризику конфліктів стешу або забування, що ви стешили.
  • Ви можете мати довготривалий dev-сервер в одному worktree та чисту збірку в іншому.

Зазвичай я тримаю два-три активних worktrees: мою основну feature-гілку, worktree main для швидких перевірок, і іноді worktree для ревю, де я перевіряю чийсь PR.

Одне застереження#

Ви не можете мати одну й ту ж гілку переключену в двох worktrees. Це зроблено навмисно — це запобігає конфліктним змінам однієї гілки в двох місцях. Якщо спробуєте, Git відмовить.

Reflog: Кнопка скасування для всього#

Ви зробили hard reset і втратили коміти. Видалили гілку. Зробили rebase і все пішло жахливо не так. Ви думаєте, що ваша робота зникла.

Ні. Git майже ніколи насправді нічого не видаляє. Reflog — ваша страхувальна сітка.

Що таке Reflog#

Кожного разу, коли HEAD переміщується — кожен коміт, checkout, rebase, reset, мердж — Git записує це в reflog. Це лог кожного місця, де був ваш HEAD, по порядку.

bash
git reflog

Вивід:

bash
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: Add payment processing
b7a8c9d HEAD@{2}: commit: Update user dashboard
e0f1g2h HEAD@{3}: commit: Fix auth token refresh
i3j4k5l HEAD@{4}: checkout: moving from feature/payments to main

Кожен запис має індекс (HEAD@{0}, HEAD@{1} тощо) і опис того, що відбулося.

Відновлення після Hard Reset#

Ви випадково виконали git reset --hard HEAD~3 і втратили три коміти. Вони прямо тут, у reflog:

bash
# Подивіться, що ви втратили
git reflog
 
# Коміт перед скиданням — це HEAD@{1}
git reset --hard f4e5d6c

Всі три коміти повернулися. Кризу відвернуто.

Відновлення видаленої гілки#

Ви видалили гілку з незмердженою роботою:

bash
git branch -D feature/experimental
# Ой, там було два тижні роботи

Коміти все ще існують. Знайдіть їх:

bash
git reflog | grep "feature/experimental"
# Або просто перегляньте reflog на предмет останнього коміту на тій гілці
 
# Знайшли. Відтворити гілку на тому коміті:
git branch feature/experimental a1b2c3d

Гілка повернулася з усіма своїми комітами.

Відновлення після невдалого Rebase#

Ви зробили rebase і все пішло не так. Конфлікти всюди, неправильні коміти, хаос:

bash
# Reflog показує, де ви були до rebase
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): ...
# ...
# f4e5d6c HEAD@{5}: rebase (start): checkout main
# b7a8c9d HEAD@{6}: commit: Your last good commit
 
# Повернутися до стану перед rebase
git reset --hard b7a8c9d

Ви повернулися точно туди, де були до початку rebase. Ніби його ніколи не було.

30-денна страхувальна сітка#

За замовчуванням Git зберігає записи reflog 30 днів (90 днів для досяжних комітів). Після цього вони можуть бути зібрані збирачем сміття. Отже, у вас є місяць, щоб усвідомити помилку. На практиці цього більш ніж достатньо.

Ви можете перевірити термін дії:

bash
git config gc.reflogExpire
# за замовчуванням: 90.days.ago (для досяжних)
git config gc.reflogExpireUnreachable
# за замовчуванням: 30.days.ago (для недосяжних)

Якщо ви параноїк, збільшіть:

bash
git config --global gc.reflogExpireUnreachable "180.days.ago"

Особисте правило#

Перед будь-якою деструктивною операцією — hard reset, force push, видалення гілки — я спочатку виконую git log --oneline -10. Я подумки фіксую поточний HEAD. Це займає дві секунди і вже не раз врятувало мене від паніки, якої не потрібно було.

Стеш правильно: Це не просто git stash#

Більшість людей використовують стеш так:

bash
git stash
# робимо щось
git stash pop

Це працює, але це еквівалент кидання всього в коробку з написом «барахло». Коли у вас три стеші, ви поняття не маєте, який — який.

Називайте свої стеші#

bash
git stash push -m "WIP: user auth form validation"

Тепер при переліку стешів:

bash
git stash list
# stash@{0}: On feature/auth: WIP: user auth form validation
# stash@{1}: On main: Quick fix attempt for nav bug
# stash@{2}: On feature/payments: Experiment with Stripe webhooks

Ви бачите точно, що містить кожен стеш.

Включення невідстежуваних файлів#

За замовчуванням git stash стешить лише відстежувані файли. Нові файли, які ви ще не додали, залишаються:

bash
# Стешити все, включаючи нові файли
git stash push --include-untracked -m "WIP: new auth components"
 
# Або навіть включити ігноровані файли (рідко потрібно)
git stash push --all -m "Full workspace snapshot"

Я використовую --include-untracked майже кожного разу. Залишання нових файлів при переключенні гілок спричиняє плутанину.

Часткове стешування#

Це те, про що більшість людей не знає. Ви можете стешити конкретні файли:

bash
# Стешити лише конкретні файли
git stash push -m "Just the auth changes" src/auth/ src/middleware.ts

Або використати patch-режим для стешування конкретних ханків всередині файлів:

bash
git stash push --patch -m "Partial: only the validation logic"

Git пройде через кожну зміну інтерактивно і запитає, чи хочете ви її стешити. y для так, n для ні, s для розділення ханку на менші частини.

Apply проти Pop#

bash
# Pop: застосувати і видалити зі списку стешів
git stash pop stash@{2}
 
# Apply: застосувати, але залишити у списку стешів
git stash apply stash@{2}

Я використовую apply, коли не впевнений, чи стеш застосується чисто. Якщо є конфлікт, стеш зберігається. З pop, якщо є конфлікт, стеш все одно залишається в списку (багато людей цього не знають), але я віддаю перевагу явному наміру apply.

Перегляд вмісту стешу#

bash
# Подивитися, які файли змінились у стеші
git stash show stash@{0}
 
# Подивитися повний diff
git stash show -p stash@{0}

Створення гілки зі стешу#

Якщо ваш стеш виріс у щось більш суттєве:

bash
git stash branch feature/auth-validation stash@{0}

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

Стратегії гілкування: Яка реально працює#

Є три мейнстрімних стратегії гілкування. Кожна має контекст, де вона блищить, і контексти, де спричиняє біль.

Gitflow#

Класика. main, develop, feature/*, release/*, hotfix/*. Створена Vincent Driessen у 2010 році.

bash
# Feature-гілка
git checkout -b feature/user-auth develop
# ... робота ...
git checkout develop
git merge --no-ff feature/user-auth
 
# Release-гілка
git checkout -b release/2.1.0 develop
# ... фінальні виправлення ...
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0"
git checkout develop
git merge --no-ff release/2.1.0
 
# Hotfix
git checkout -b hotfix/session-fix main
# ... виправлення ...
git checkout main
git merge --no-ff hotfix/session-fix
git checkout develop
git merge --no-ff hotfix/session-fix

Коли працює: Мобільні застосунки, десктопне ПЗ, все з іменованими версійними релізами та одночасною підтримкою кількох версій. Якщо ви випускаєте v2.1 і v3.0 і потрібно патчити обидва, Gitflow це обробляє.

Коли не працює: Веб-застосунки з безперервним деплоєм. Якщо ви деплоїте в продакшен 5 разів на день, церемонія release-гілок і develop-гілок — чистий оверхед. Більшість веб-команд, що приймають Gitflow, закінчують з develop-гілкою, яка вічно зламана, і release-гілками, які ніхто не розуміє.

GitHub Flow#

Просто. У вас є main. Ви створюєте feature-гілки. Ви відкриваєте PR. Ви мерджите в main. Ви деплоїте main.

bash
git checkout -b feature/user-auth main
# ... робота ...
git push origin feature/user-auth
# Відкрити PR, отримати ревю, змерджити
# main завжди готовий до деплою

Коли працює: Малі та середні команди, що випускають веб-застосунки. Безперервний деплой. Якщо main завжди деплоїться, це все, що вам потрібно. Саме це використовує цей сайт.

Коли не працює: Коли потрібно підтримувати кілька release-версій, або коли є довгий цикл QA перед деплоєм. GitHub Flow припускає, що main швидко потрапляє в продакшен.

Trunk-Based Development#

Усі комітять у main («trunk») напряму або через дуже короткоживучі гілки (менше дня). Жодних довготривалих feature-гілок.

bash
# Короткоживуча гілка (мерджиться того ж дня)
git checkout -b fix/auth-token main
# ... маленька, сфокусована зміна ...
git push origin fix/auth-token
# PR ревю'ється та мерджиться протягом годин, не днів

Коли працює: Високопродуктивні команди з хорошим CI/CD, комплексними тест-сюїтами та прапорами функцій. Google, Meta та більшість великих технологічних компаній використовують trunk-based development. Це змушує робити маленькі, інкрементальні зміни та усуває пекло мерджів.

Коли не працює: Команди без хорошого тестового покриття або CI. Якщо мердж у trunk означає деплой непротестованого коду, ви будете постійно ламати продакшен. Вам також потрібні прапори функцій для будь-чого, що займає більше дня:

bash
# Прапор функції в коді
if (featureFlags.isEnabled('new-checkout-flow')) {
  renderNewCheckout();
} else {
  renderLegacyCheckout();
}

Моя рекомендація#

Для більшості веб-команд розробки: починайте з GitHub Flow. Це просто, це працює, і не потребує інструментарію окрім того, що GitHub/GitLab вже надає.

Якщо ваша команда виросте за 15-20 інженерів і ви деплоїте кілька разів на день, зверніть увагу на trunk-based development з прапорами функцій. Інвестиції в інфраструктуру прапорів функцій окупаються зменшенням конфліктів мерджу та швидшою ітерацією.

Якщо ви випускаєте версійне ПЗ (мобільні застосунки, CLI-інструменти, бібліотеки): Gitflow або спрощена версія. Вам дійсно потрібні ці release-гілки.

Не вибирайте стратегію, тому що якийсь блог-пост сказав, що це найкраща. Виберіть ту, що відповідає тому, як ви реально випускаєте.

Git Hooks: Автоматизуйте те, що постійно забуваєте#

Git hooks — це скрипти, що запускаються автоматично в певних точках воркфлоу Git. Вони локальні для вашої машини (не пушаться на ремоут), що означає, що вам потрібен спосіб ділитися ними з командою.

Хуки, які реально мають значення#

pre-commit — Запускається перед кожним комітом. Використовуйте для лінтингу та форматування:

bash
#!/bin/bash
# .git/hooks/pre-commit
 
# Запустити ESLint лише на стейджених файлах
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
 
if [ -n "$STAGED_FILES" ]; then
  echo "Running ESLint on staged files..."
  npx eslint $STAGED_FILES --quiet
  if [ $? -ne 0 ]; then
    echo "ESLint failed. Fix errors before committing."
    exit 1
  fi
fi
 
# Запустити Prettier на стейджених файлах
if [ -n "$STAGED_FILES" ]; then
  echo "Running Prettier..."
  npx prettier --check $STAGED_FILES
  if [ $? -ne 0 ]; then
    echo "Prettier check failed. Run 'npx prettier --write' first."
    exit 1
  fi
fi

commit-msg — Валідує формат повідомлення коміту. Ідеально для впровадження conventional commits:

bash
#!/bin/bash
# .git/hooks/commit-msg
 
COMMIT_MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
 
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
  echo "Invalid commit message format."
  echo "Expected: type(scope): description"
  echo "Example: feat(auth): add session refresh endpoint"
  echo ""
  echo "Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
  exit 1
fi

pre-push — Запускається перед пушем. Використовуйте для тестів:

bash
#!/bin/bash
# .git/hooks/pre-push
 
echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
  echo "Tests failed. Push aborted."
  exit 1
fi

Husky проти нативних хуків#

Нативні хуки живуть у .git/hooks/. Проблема: директорія .git не відстежується Git, тому ви не можете ділитися хуками через репозиторій. Кожен має налаштовувати їх вручну.

Husky вирішує це. Він зберігає конфігурації хуків у репозиторії та налаштовує .git/hooks автоматично при npm install:

bash
npx husky init

Це створює директорію .husky/. Додавайте хуки як файли:

bash
# .husky/pre-commit
npx lint-staged

У поєднанні з lint-staged ви отримуєте швидкі, цілеспрямовані pre-commit перевірки:

json
{
  "lint-staged": {
    "*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.css": ["prettier --write"],
    "*.json": ["prettier --write"]
  }
}

Це запускає ESLint і Prettier лише на файлах, які ви фактично комітите. Не на всій кодовій базі. Швидко.

Коли пропускати хуки#

Іноді потрібно комітити без запуску хуків. Термінові hotfix-и, work-in-progress коміти на вашій власній гілці:

bash
git commit --no-verify -m "WIP: debugging production issue"

Використовуйте це помірковано. Якщо ви регулярно пропускаєте хуки, ваші хуки, ймовірно, надто повільні або надто суворі.

Аліаси, що економлять час#

Мій .gitconfig еволюціонував роками. Це аліаси, що вижили — ті, якими я реально користуюсь щодня, а не ті, що я додав, бо вони виглядали розумно.

Гарний лог#

Стандартний git log багатослівний. Це дає вам чистий, кольоровий, графовий вигляд:

bash
git config --global alias.lg "log --oneline --graph --all --decorate"

Використання:

bash
git lg
# * a1b2c3d (HEAD -> main) Fix session validation
# | * d4e5f6g (feature/payments) Add Stripe integration
# | * h7i8j9k Update payment models
# |/
# * l0m1n2o Merge PR #42
# * p3q4r5s Add user dashboard

Я запускаю це 20 разів на день. Це найшвидший спосіб зрозуміти стан вашого репозиторію.

Скасувати останній коміт#

Зберегти зміни, просто скасувати коміт:

bash
git config --global alias.undo "reset HEAD~1 --mixed"

Використання:

bash
git undo
# Коміт зник, але всі зміни все ще у вашій робочій директорії

Я використовую це, коли комічу занадто рано, забуваю файл або хочу реструктурувати зміни.

Зняти все зі стейджу#

bash
git config --global alias.unstage "reset HEAD --"

Використання:

bash
git unstage src/auth/session.ts
# Файл знятий зі стейджу, але зміни збережені

Перелічити всі аліаси#

Бо ви забудете, що налаштували:

bash
git config --global alias.aliases "config --get-regexp ^alias\\."

Більше аліасів, якими я користуюсь#

bash
# Показати, що ви збираєтесь комітити
git config --global alias.staged "diff --staged"
 
# Короткий статус
git config --global alias.st "status -sb"
 
# Amend без зміни повідомлення
git config --global alias.amend "commit --amend --no-edit"
 
# Показати останній коміт
git config --global alias.last "log -1 HEAD --stat"
 
# Pull з rebase замість мерджу
git config --global alias.up "pull --rebase --autostash"
 
# Видалити гілки, що змерджені в main
git config --global alias.cleanup "!git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d"

Аліас up особливо хороший. pull --rebase тримає вашу історію лінійною замість створення мердж-комітів при кожному pull. --autostash автоматично стешить і відновлює ваші зміни, якщо у вас є брудні файли. Ось яким pull мав бути за замовчуванням.

Аліас cleanup видаляє локальні гілки, що змерджені в main. З часом ви накопичуєте десятки застарілих гілок. Запускайте це щотижня.

Команди, якими я користуюсь щодня#

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

Ультимативна команда логу#

bash
git log --oneline --graph --all

Це показує кожну гілку, кожен мердж, всю топологію вашого репозиторію в компактному вигляді. Це перше, що я запускаю, коли підтягую зміни. Вона відповідає на запитання «що зараз відбувається в цьому репозиторії?»

Diff стейджених змін#

bash
git diff --staged

Це показує, що збирається бути комітнутим. Не те, що змінилось у вашій робочій директорії — те, що фактично стейджено. Я завжди запускаю це перед комітом. Завжди. Це ловить випадкові включення, debug-інструкції, console.log, яких не повинно бути.

bash
# Подивитися нестейджені зміни
git diff
 
# Подивитися стейджені зміни
git diff --staged
 
# Подивитися обидва
git diff HEAD

Показати конкретний коміт#

bash
git show a1b2c3d

Показує повний diff окремого коміту. Корисно при перегляді історії, розумінні того, що коміт фактично змінив.

bash
# Показати лише файли, що змінились
git show --stat a1b2c3d
 
# Показати конкретний файл на конкретному коміті
git show a1b2c3d:src/auth/session.ts

Останнє неймовірно корисне. Ви можете переглянути будь-який файл у будь-якій точці історії, не переключаючи коміт.

Blame з діапазонами рядків#

bash
git blame -L 42,60 src/auth/session.ts

Показує, хто останнім модифікував рядки 42-60. Корисніше, ніж blame всього файлу, що зазвичай перевантажує.

bash
# Показати повідомлення коміту теж, не лише хеш
git blame -L 42,60 --show-name src/auth/session.ts
 
# Ігнорувати зміни пробілів (дуже корисно)
git blame -w -L 42,60 src/auth/session.ts
 
# Показати коміт перед blamed (копати глибше)
git log --follow -p -- src/auth/session.ts

Прапорець -w важливий. Без нього blame приписуватиме рядки тому, хто останнім переформатував файл, а це рідко та людина, яку ви шукаєте.

Пошук рядка по всій історії#

bash
# Знайти, коли функція була додана або видалена
git log -S "validateSession" --oneline
 
# Знайти, коли з'явився regex-патерн
git log -G "session.*timeout" --oneline

-S («кирка») знаходить коміти, де кількість входжень рядка змінилась. -G знаходить коміти, де diff відповідає regex. Обидва потужні для археології — з'ясування, коли щось було введено або видалено.

Показати, що змінилось між двома точками#

bash
# Що змінилось між двома гілками
git diff main..feature/auth
 
# Що змінилось на цій гілці з моменту відгалуження від main
git diff main...feature/auth
 
# Перелічити лише змінені файли
git diff main...feature/auth --name-only
 
# Статистичний вигляд (файли + вставки/видалення)
git diff main...feature/auth --stat

Дві крапки проти трьох крапок має значення. Дві крапки показують різницю між кінцями обох гілок. Три крапки показують, що змінилось на правій стороні з моменту відгалуження від лівої. Три крапки — це зазвичай те, що ви хочете при ревю feature-гілки.

Очищення невідстежуваних файлів#

bash
# Подивитися, що було б видалено (пробний запуск)
git clean -n
 
# Видалити невідстежувані файли
git clean -f
 
# Видалити невідстежувані файли та директорії
git clean -fd
 
# Видалити невідстежувані та ігноровані файли (ядерний варіант)
git clean -fdx

Завжди запускайте з -n спочатку. git clean -fdx видалить ваші node_modules, .env, артефакти збірки — все, що не відстежується Git. Корисно для справді свіжого старту, але деструктивно.

Відновити один файл з іншої гілки#

bash
# Отримати версію файлу з main-гілки без переключення гілок
git restore --source main -- src/config/database.ts

Або з конкретного коміту:

bash
git restore --source a1b2c3d -- src/config/database.ts

Це чистіше, ніж старий синтаксис git checkout main -- path/to/file, і не впливає на HEAD.

Збираємо все разом: Реальний воркфлоу#

Ось як виглядає типовий день з цими інструментами:

bash
# Ранок: перевірити, що відбувається
git lg
git fetch --all
 
# Почати фічу
git checkout -b feature/session-refresh main
 
# Працювати, комітити інкрементально
git add -p                        # Стейджити конкретні ханки, не цілі файли
git commit -m "Add token refresh endpoint"
git commit -m "Add refresh token rotation"
git commit -m "Fix: handle expired refresh tokens"
git commit -m "Add integration tests"
 
# Перервано: потрібно виправити продакшен-баг
git worktree add ../hotfix main
cd ../hotfix
# ... виправити, комітнути, запушити, PR змерджено ...
cd ../my-project
git worktree remove ../hotfix
 
# Назад до роботи над фічею
git commit -m "Fix edge case in token validation"
 
# Готовий до PR: очистити історію
git rebase -i main
# Об'єднати fix-коміти, переформулювати для ясності
 
# Запушити та відкрити PR
git push -u origin feature/session-refresh
 
# Щось зламалось на стейджингу? Знайти який коміт:
git bisect start
git bisect bad HEAD
git bisect good main
git bisect run npm test
 
# Ой, я скинув не те
git reflog
git reset --hard HEAD@{2}

Кожна з цих команд займає секунди. Разом вони економлять години. Не гіпотетичні години — реальні години, щотижня, які я б інакше витратив на розплутування Git-месів, ручний пошук багів або втрату роботи через переключення контексту.

Git — це інструмент, що винагороджує глибину. Основи проведуть вас через день. Але команди в цій статті — це те, що розділяє «я використовую Git» від «Git реально робить мене швидшим». Вивчайте їх інкрементально. Виберіть одну нову техніку цього тижня та використовуйте її, поки вона не стане м'язовою пам'яттю. Потім виберіть іншу.

Ваше майбутнє я, що дивиться на продакшен-баг о 23:00, подякує вам за знання git bisect.

Схожі записи