Хук — исполняемый файл, который Git запускает перед коммитом, после ввода сообщения или перед отправкой в удалённый репозиторий. Проверки работают локально и ловят ошибку за секунды, а не после падения CI. Но хуки не копируются при клонировании и отключаются флагом --no-verify, поэтому нужна общая точка входа для команды.
Как устроены хуки
Git ищет исполняемые файлы в каталоге .git/hooks. При инициализации репозитория там появляются примеры с суффиксом .sample: они не активны, пока вы не уберёте суффикс и не выставите бит исполнения. Многие хуки получают аргументы, а pre-push и серверные — данные на стандартный ввод. Результат возвращается кодом выхода: ноль — продолжить, ненулевой — прервать. Ошибки печатайте в stderr, иначе Git может их проглотить.
Клиентские хуки делятся на группы. pre-commit запускается до создания коммита и идеален для быстрых проверок. commit-msg получает путь к файлу с сообщением и проверяет формат. pre-push вызывается перед отправкой и подходит для тестов, которые не должны занимать секунды. Серверные хуки, например pre-receive, живут на Git-сервере и не зависят от настроек разработчика.
ls -l .git/hooks
cat .git/hooks/pre-commit.sample
git config --get core.hooksPath
git help githooks
Первый pre-commit: секреты и мусор
Начните с проверок, которые дают мгновенную пользу и почти не дают ложных срабатываний: запрет больших файлов, поиск отладочных маркеров, грубая проверка на приватные ключи. Смотрите на индекс, а не на рабочую копию: в коммит попадёт только добавленное через git add.
#!/bin/sh
set -e
git diff --cached --name-only --diff-filter=ACM | while IFS= read -r file; do
[ -f "$file" ] || continue
size=$(wc -c < "$file")
[ "$size" -le 1048576 ] || { echo "Слишком большой файл: $file" >&2; exit 1; }
done
if git grep --cached -n -I -E 'BEGIN (RSA|OPENSSH|EC) PRIVATE KEY' -- .; then
echo "В индексе найден приватный ключ" >&2
exit 1
fi
exit 0
Скрипт простой и не пытается угадать все форматы секретов. Для настоящего проекта подключите специализированный сканер, но даже такая проверка отсекает случайно добавленный id_rsa. Не забывайте про chmod +x .git/hooks/pre-commit, иначе Git молча проигнорирует файл.
commit-msg и единый формат сообщений
Второй полезный хук проверяет сообщение коммита. Если команда приняла Conventional Commits, правило описывается регулярным выражением. Хук получает путь к временному файлу в $1; читайте первую строку и сравнивайте с шаблоном. Длинную тему отклоняйте: её неудобно читать в истории.
#!/bin/sh
subject=$(head -n 1 "$1")
pattern='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9._-]+\))?: .{1,72}$'
printf '%s' "$subject" | grep -Eq "$pattern" || {
echo "Формат: feat(scope): краткое описание до 72 символов" >&2
exit 1
}
exit 0
Проверка должна быть предсказуемой. Если правило разрешает русский текст после двоеточия, не ограничивайте его латиницей. Если проект использует трекер задач, требуйте номер задачи, но не усложняйте шаблон до предела.
pre-push: тесты перед отправкой
pre-push получает имя удалённого репозитория и URL, а на stdin — список ссылок. Полноценные тесты занимают минуты, поэтому запускайте быстрый набор: линтер, проверку типов, модульные тесты затронутых модулей. Тяжёлые интеграционные тесты оставьте CI, иначе разработчики начнут отключать хуки.
#!/bin/sh
set -e
echo "Быстрые проверки перед push..."
python -m ruff check .
python -m pytest -q tests/unit
exit 0
Не запускайте в хуке команды, которые меняют рабочее дерево: форматирование и автоматические коммиты внутри pre-commit легко приводят к путанице. Пусть форматтер исправляет код отдельной командой, а хук только сообщает о нарушении.
Общие хуки в репозитории
Каталог .git/hooks не версионируется, поэтому договориться о проверках через него нельзя. Решение — хранить скрипты в репозитории и указать Git на них через core.hooksPath. Создайте каталог .githooks, положите туда исполняемые файлы и добавьте настройку. Каждый разработчик выполняет одну команду после клонирования; её же включите в скрипт первичной настройки.
mkdir -p .githooks
cp .git/hooks/pre-commit .githooks/pre-commit
git config core.hooksPath .githooks
chmod +x .githooks/pre-commit
Путь в core.hooksPath может быть абсолютным или относительным. Относительный удобнее, но проверьте поведение в связанных worktree: Git разрешает его от корня рабочего дерева. Если хуки должны быть обязательными, продублируйте ключевые проверки в CI: настройка core.hooksPath локальна и не гарантирует дисциплину.
pre-commit framework и сопровождение
Писать bash-скрипты для каждой проверки утомительно. Framework pre-commit управляет хуками из файла .pre-commit-config.yaml, скачивает и кэширует версии инструментов и запускает проверки только по изменённым файлам. После установки пакета выполните pre-commit install — он пропишет точку входа в .git/hooks/pre-commit. Полный прогон запускается командой pre-commit run --all-files.
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-added-large-files
args: [--maxkb=512]
- id: check-merge-conflict
Фиксируйте rev на теге, а не на ветке: иначе проверки изменятся без вашего участия. В примере указан конкретный тег; актуальный смотрите на странице проекта. Обновляйте версии отдельным коммитом через pre-commit autoupdate. Кэш framework хранится в домашнем каталоге, поэтому первый запуск может быть долгим, особенно в CI.
Флаг —no-verify и дисциплина
Флаг --no-verify пропускает клиентские хуки. Это аварийный выход для срочной правки, а не ежедневный режим. Если хук мешает, упростите его, а не отключайте. Проверки должны занимать единицы секунд; всё, что дольше, переносится в pre-push или CI. Выводите понятное сообщение: что не так и как это исправить.
- Хуки не заменяют CI: они лишь ускоряют обратную связь.
- Не храните секреты в репозитории, даже если хук их ищет.
- Проверяйте индекс, а не только рабочее дерево.
- Делайте скрипты идемпотентными и без интерактивных запросов.
- Документируйте установку хуков в README или
make setup. - Для Windows-разработчиков проверьте переводы строк и права файлов.
git commit --no-verify -m "wip"
pre-commit run --all-files
git config --unset core.hooksPath
git log -1 --format=%B
Итог
Git hooks — дешёвый способ перенести часть проверок влево, на машину разработчика. Начните с pre-commit для больших файлов и секретов, добавьте commit-msg для единого формата и pre-push для быстрых тестов. Храните скрипты в репозитории через core.hooksPath, а ключевые правила дублируйте в CI. Тогда хук останется помощником, а не единственной линией обороны.