Git-хуки
| Где лежат | .git/hooks/ (или core.hooksPath) |
|---|---|
| Типы | клиентские и серверные |
| Популярные клиентские | pre-commit, commit-msg, pre-push |
| Серверные | pre-receive, update, post-receive |
| Менеджеры | pre-commit (Python), husky (Node.js), lefthook (Go) |
| Обход | git commit --no-verify |
Git-хуки (англ. git hooks) — программы, которые Git автоматически запускает в определённые моменты своей работы: перед созданием коммита, после смены ветки, перед отправкой изменений на сервер, при приёме пуша на сервере. Если хук завершается с ненулевым кодом, Git отменяет операцию. Хуки используют, чтобы ловить ошибки как можно раньше — запускать линтер и форматтер, проверять сообщения коммитов, не пропускать в репозиторий секреты — ещё до того, как изменения попадут в CI/CD.
Как устроены
Хуки — это исполняемые файлы с фиксированными именами в каталоге .git/hooks/. При git init там создаются примеры с суффиксом .sample; чтобы включить хук, достаточно убрать суффикс и сделать файл исполняемым.[1] Язык не важен: подойдёт shell, Python, Node.js — что угодно с правильной строкой #!.
Каталог .git/ не попадает в репозиторий, поэтому хуки не передаются при клонировании — это сделано намеренно, иначе любой репозиторий мог бы выполнить код на машине разработчика. Чтобы поделиться хуками с командой, их кладут в отслеживаемую папку и указывают Git путь к ней:
git config core.hooksPath .githooks
Либо используют менеджеры хуков, о которых ниже.
Клиентские хуки
| Хук | Когда | Типичное применение |
|---|---|---|
pre-commit |
перед созданием коммита | линтеры, форматтеры, поиск секретов |
prepare-commit-msg |
перед открытием редактора | шаблон, номер задачи из имени ветки |
commit-msg |
после ввода сообщения | проверка формата (Conventional Commits) |
post-commit |
после коммита | уведомления |
post-checkout, post-merge |
после смены ветки, слияния | установить зависимости, если изменился lock-файл |
pre-rebase |
перед rebase | запрет переписывать опубликованные ветки |
pre-push |
перед отправкой | быстрые тесты |
Пример простого pre-commit, не дающего закоммитить отладочный вывод:
#!/bin/sh
if git diff --cached -U0 | grep -E '^\+.*(console\.log|breakpoint\(\))'; then
echo "Уберите отладочный код перед коммитом" >&2
exit 1
fi
Хук должен проверять именно индекс (git diff --cached), а не рабочую копию — иначе в проверку попадут незастейдженные правки.
Серверные хуки
На сервере выполняются pre-receive, update и post-receive.[2] Они могут отклонить пуш (например, в защищённую ветку или без подписи) или запустить действие после него — исторически так делали простейший деплой: post-receive выкладывал код в каталог сайта. На GitHub и GitLab произвольные серверные хуки обычно недоступны (GitLab разрешает их на собственной установке); вместо них используют правила защиты веток, push rules и вебхуки, а проверки — в CI.
Менеджеры хуков
- pre-commit — фреймворк на Python с тысячами готовых хуков; конфигурация в
.pre-commit-config.yaml, каждое средство ставится в изолированное окружение. Работает для любых языков.[3] - husky — популярный выбор в экосистеме JavaScript, хуки хранятся в
.husky/; часто в паре сlint-staged, который проверяет только изменённые файлы.[4] - lefthook — быстрый менеджер на Go с параллельным запуском и конфигурацией в YAML.[5]
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/gitleaks/gitleaks
rev: v8.21.0
hooks:
- id: gitleaks
После pre-commit install хуки срабатывают на каждом коммите и проверяют только изменённые файлы; pre-commit run --all-files прогоняет весь репозиторий. Здесь Ruff проверяет Python-код, а gitleaks ищет случайно добавленные ключи и пароли.
Проверка сообщений коммитов
Хук commit-msg часто проверяет соответствие соглашению Conventional Commits (feat: ..., fix(api): ...) с помощью commitlint.[6] На таких сообщениях строится автоматическая генерация CHANGELOG и выбор следующей версии по правилам семантического версионирования.
Ограничения
Клиентские хуки — удобство, а не гарантия: их легко обойти флагом --no-verify, а у нового участника они не установлены, пока он не выполнит установку. Поэтому все обязательные проверки дублируют в CI, а хуки делают быстрыми — несколько секунд, — иначе разработчики начнут их отключать. Долгие тесты лучше оставить для pre-push или CI.