Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке Git hooks?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Git hooks** - це скрипти, які Git запускає автоматично при конкретних подіях (pre-commit, pre-push тощо) для перевірок або автоматизації. Зберігаються в `.git/hooks/`. Код виходу 0 продовжує операцію, будь-що інше скасовує її. **Головне:** hooks локальні за замовчуванням, для командної роботи використовуй Husky.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Git hooks** - це скрипти, які Git запускає автоматично в конкретних точках свого процесу: перед комітом, перед пушем, після злиття гілок. ## Теорія ### TL;DR - Hooks - як паспортний контроль в аеропорту: Git зупиняється в конкретному місці, запускає твій скрипт, і якщо він повертає ненульовий код виходу, операція скасовується. - Hooks зберігаються в `.git/hooks/` і працюють тільки локально. Колеги їх не отримують при клонуванні. - Для локальних перевірок до 10 секунд (lint, тести) - hooks. Для обов'язкового enforcement в команді - CI/CD. - `git commit --no-verify` обходить pre-commit hooks, тому вони не є рівнем безпеки. - Husky дозволяє версіонувати hooks разом з кодом і автоматично ділитися ними в команді. ### Швидкий приклад ```bash # .git/hooks/pre-commit #!/bin/sh # Запускається перед кожним комітом на цій машині if ! npm run lint; then echo "Lint провалився - виправ перед комітом" exit 1 # Ненульовий код скасовує коміт fi ``` Спочатку зроби файл виконуваним: `chmod +x .git/hooks/pre-commit`. Без цього Git ігнорує файл без будь-якого попередження, і коміт проходить так, ніби hook взагалі не існує. ### Як Git запускає hooks Git шукає у `.git/hooks/` виконуваний файл з назвою, що відповідає події - наприклад, `pre-commit`. При виконанні `git commit` Git запускає підпроцес shell. Код виходу 0 означає «продовжити», будь-що інше скасовує операцію. Hooks успадковують змінні середовища Git, зокрема `$GIT_DIR`. Чотири основні фази: - **pre**: запускається до дії (`pre-commit`, `pre-push`) і може заблокувати її - **during**: змінює поточну операцію (`prepare-commit-msg`, `commit-msg`) - **post**: запускається після завершення дії (`post-commit`, `post-merge`) і нічого не може скасувати - **server-side**: на віддаленому сервері (`pre-receive`, `post-receive`), потрібен доступ до сервера ### Клієнтські vs серверні hooks Клієнтські hooks лежать у `.git/hooks/` на локальній машині. Серверні hooks знаходяться на самому remote-сервері, наприклад у self-hosted GitLab або Gitea. GitHub-репозиторії не дають прямого доступу до серверних hooks. Важливий наслідок: будь-хто в команді може обійти клієнтський hook, видаливши файл або запустивши `git commit --no-verify`. Для справжнього enforcement використовуй GitHub Actions або CI-пайплайн. ### Коли використовувати - Pre-commit лінтинг: ловити проблеми зі стилем до того як вони потраплять в репо - Pre-push тести: запускати тест-сьют перед пушем - Валідація commit message: перевіряти формат [conventional commits](https://www.conventionalcommits.org/) через `commit-msg` - Post-merge оновлення залежностей: автоматично запускати `npm install` після пулу, якщо `package.json` змінився Hooks не підходять для перевірок, що тривають більше 10 секунд, або для правил, які команда не повинна мати можливості обійти. ### Ділимось hooks через Husky Директорія `.git/` не комітиться в репо. Тому hooks не передаються при клонуванні. Husky вирішує це, вказуючи Git на версіоновану директорію `.husky/`. ```bash # Встановлення і ініціалізація Husky npm install husky --save-dev npx husky install # Створює директорію .husky/ # Додаємо pre-commit hook npx husky add .husky/pre-commit "npx lint-staged" ``` ```json // .lintstagedrc.json { "*.{js,ts,jsx,tsx}": ["eslint --fix", "git add"] } ``` Husky v9 спрощує це ще більше: використовує `git config core.hooksPath .husky/` напряму, замість npm lifecycle скриптів з v4. ### Типові помилки **Забутий `chmod +x`:** ```bash # Файл існує, але не є виконуваним .git/hooks/pre-commit ``` Git ігнорує файл без жодного повідомлення про помилку. Коміт проходить, нічого не запускається, і ти витрачаєш час здогадуючись чому hook не працює. Виправлення: `chmod +x .git/hooks/pre-commit`. Це трапляється майже з кожним розробником вперше. **Хардкод шляхів:** ```bash #!/bin/sh node_modules/.bin/eslint . # Зламається на свіжому клоні ``` Краще: `npx eslint .` або `npm run lint`. Хардкодований шлях до `node_modules` падає одразу після `git clone`, коли залежності ще не встановлено. **Спроба скасувати дію в post-hook:** ```bash # post-commit npm run notify-slack || exit 1 # exit 1 тут не має ефекту ``` Post-hooks запускаються після того як операція вже виконана. Ненульовий код виходу там ні на що не впливає. Щоб заблокувати дію, використовуй відповідний pre-hook. **Очікування що hooks запустяться в CI:** GitHub Actions клонує репо з нуля і не отримує локальних hooks. Hooks - це інструмент локальної розробки. Якщо хочеш ті ж самі перевірки в CI, налаштуй їх окремо у workflow-файлі. ### Де зустрічається - **Husky + lint-staged** (60k+ зірок на GitHub): запускає ESLint тільки на staged-файлах, стандарт у React і Next.js репо - **Lefthook**: YAML-конфігурація, поширений у Ruby/Rails проектах на GitLab - **pre-commit.com framework**: керування hooks для кількох мов, використовується в Airbnb і проекті dask - **commit-msg hook**: перевірка формату conventional commits перед збереженням повідомлення ### Follow-up питання **Q:** Як обійти hook в екстреній ситуації? **A:** `git commit --no-verify`. Це пропускає `pre-commit` і `commit-msg` hooks. Корисно для WIP-комітів або коли зламаний hook блокує всю команду. **Q:** Яка різниця між `pre-commit` і `commit-msg` hooks? **A:** `pre-commit` запускається до того як Git відкриває редактор для повідомлення. `commit-msg` запускається після того як ти написав повідомлення, але до збереження коміту. Для перевірки коду - `pre-commit`, для перевірки формату повідомлення - `commit-msg`. **Q:** Чому hooks не запускаються в CI? **A:** CI-runner клонує репо з нуля. `.git/hooks/` існує тільки локально і не є частиною версійного контролю. Hooks є лише на тих машинах, де їх явно налаштували. **Q:** Чим Husky v9 відрізняється від v4? **A:** Husky v4 зберігав конфіг у `package.json` і встановлював hooks через npm lifecycle скрипти. v9 використовує `git config core.hooksPath .husky/` напряму і вимагає явного виклику `husky install`. Менше магії, простіше налагодження. **Q:** Якщо є і `pre-commit`, і `prepare-commit-msg`, який запускається першим? **A:** Git запускає їх у фіксованому порядку: спочатку `pre-commit`, потім `prepare-commit-msg`, потім `commit-msg`, потім `post-commit`. Цей порядок не можна змінити. Якщо обидва hooks змінюють один і той же стан, потрібно враховувати послідовність вручну або використовувати Lefthook для координації. ## Приклади ### Базовий: лінтинг тільки staged файлів ```bash #!/bin/sh # .git/hooks/pre-commit (потрібен chmod +x) # Перевіряє тільки staged JS файли, не весь проект changed_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$') for file in $changed_files; do npx eslint "$file" || exit 1 done ``` Це швидше ніж `eslint .` на великих репо, бо перевіряє лише файли у поточному коміті. Якщо будь-який файл провалюється, цикл виходить з кодом 1 і Git скасовує коміт. На чистому проекті без staged JS файлів цикл виконується нуль разів і завершується з кодом 0. ### Середній рівень: Husky + lint-staged в Next.js проекті ```bash # .husky/pre-commit #!/bin/sh . "$(dirname "$0")/_/husky.sh" npx lint-staged ``` ```json // .lintstagedrc.json { "*.{js,ts,jsx,tsx}": ["eslint --fix", "git add"], "*.{css,scss}": ["stylelint --fix", "git add"] } ``` `lint-staged` автоматично виправляє проблеми і повторно додає виправлені файли в стейдж. Якщо файл не вдається виправити автоматично, коміт скасовується. Файл `.husky/pre-commit` відстежується Git, тому кожен розробник після `npm install` отримує ті ж самі перевірки завдяки скрипту `prepare`, який запускає `husky install`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.