Олег Марков
Хуки Git git hooks
Введение
Хуки Git (git hooks) — это механизм, который позволяет автоматически запускать скрипты при наступлении определённых событий в Git. Событие — это, например, создание коммита, переключение ветки, приём пуша на сервер или слияние веток.
Вы можете использовать хуки, чтобы:
- проверять стиль кода перед коммитом;
- запускать тесты;
- запрещать пуш в главную ветку напрямую;
- автоматически форматировать код;
- синхронизировать задачи трекера и сообщения коммитов;
- запускать сборку и деплой на сервере.
Смотрите, я покажу вам, как это устроено, какие бывают типы хуков и как шаг за шагом настроить их под ваш рабочий процесс.
Что такое Git hook и как он работает
Общая идея
Хук Git — это обычный исполняемый файл (скрипт), который Git вызывает автоматически при наступлении конкретного события. Сам Git не «знает» содержимое скрипта, он просто запускает его и передаёт аргументы, если это предусмотрено для данного хука.
Если скрипт:
- завершается с кодом возврата 0 — Git считает, что всё прошло успешно и продолжает операцию;
- возвращает ненулевой код — Git прерывает операцию (для некоторых типов хуков), а вы видите сообщение об ошибке.
Давайте разберёмся на примере простого клиентского хука pre-commit, который выполняется перед созданием коммита.
Где хранятся хуки и как их включить
Папка .git/hooks
В каждом Git-репозитории есть директория .git/hooks. Если вы заглянете в неё, то увидите файлы-шаблоны с расширением .sample, например:
pre-commit.samplepre-push.samplecommit-msg.samplepost-merge.sample- и другие
Git создаёт их как примеры. Они не активны, пока вы их не переименуете и не сделаете исполняемыми.
Смотрите, здесь я показываю, как включить один из таких хуков в Linux или macOS:
cd /path/to/your/repo/.git/hooks
# Переименовываем пример в рабочий хук
mv pre-commit.sample pre-commit
# Делаем файл исполняемым
chmod +x pre-commit
# Теперь создадим простой pre-commit хук
cat > pre-commit << 'EOF'
#!/bin/sh
# Здесь мы просто выводим сообщение перед коммитом
echo "Запущен pre-commit хук"
# Код возврата 0 - разрешаем коммит
exit 0
EOF
chmod +x pre-commit
Комментарии в скрипте помогают вам (и команде) быстрее понять, что именно делает хук.
Путь к глобальным хукам
Иногда удобно иметь единый набор хуков для всех локальных репозиториев пользователя. Git позволяет задать путь к директории с глобальными хуками:
# Укажем директорию, где будут лежать глобальные хуки
git config --global core.hooksPath ~/.git-hooks
# Создадим директорию для глобальных хуков
mkdir -p ~/.git-hooks
# Пример глобального pre-commit хука
cat > ~/.git-hooks/pre-commit << 'EOF'
#!/bin/sh
# Здесь мы запрещаем коммиты с сообщением "WIP"
if git log -1 --pretty=%B | grep -qi "WIP"; then
echo "Коммиты с сообщением 'WIP' запрещены"
exit 1
fi
exit 0
EOF
chmod +x ~/.git-hooks/pre-commit
Обратите внимание, что глобальные хуки не будут автоматически добавлены в каждый репозиторий — вы просто указываете Git, где искать хуки по умолчанию. Это удобно, если вы хотите единые проверки стиля для всех своих проектов.
Типы хуков: клиентские и серверные
Все хуки можно условно разделить на две большие группы.
Клиентские хуки
Работают в локальном репозитории разработчика. Они помогают контролировать поведение при:
- написании и изменении сообщений коммитов;
- создании коммита;
- выполнении
rebase,merge,checkoutи других операций; - пуше изменений на удалённый сервер.
Клиентские хуки особенно полезны, когда вы хотите:
- запускать линтеры или форматирование кода перед коммитом;
- проверять, что коммит не содержит секретов;
- контролировать структуру сообщения коммита;
- не пушить в определённые ветки.
Серверные хуки
Работают на стороне Git-сервера (например, bare-репозиторий на вашем сервере, Gitolite или простая папка с git init --bare). Они срабатывают при приёме пуша или после него, а также при обработке обновлений ссылок.
Серверные хуки удобно использовать, если вы хотите:
- внедрить обязательные проверки для всех разработчиков;
- ограничить доступ к веткам (например, только через Pull Request);
- запускать CI/CD-процессы (сборку, тесты, деплой).
Теперь давайте подробнее разберём основные хуки и посмотрим, как они работают.
Основные клиентские хуки
Хук pre-commit
pre-commit вызывается перед созданием коммита, сразу после того как вы выполнили команду:
git commit
Он выполняется до того, как Git запустит редактор для ввода сообщения (если сообщение не передано через -m). Это удобное место для:
- запуска линтеров;
- форматирования кода;
- проверки, что в коммит не попадают временные файлы;
- быстрой проверки тестов.
Смотрите пример простого pre-commit, который не даёт закоммитить файлы с отладочными принтами:
#!/bin/sh
# Получаем список файлов, которые попадут в коммит
# --cached - берём из индекса, а не из рабочей директории
FILES=$(git diff --cached --name-only --diff-filter=ACM)
# Если файлов нет, просто выходим
[ -z "$FILES" ] && exit 0
# Проверяем каждый файл на наличие отладочного кода
for FILE in $FILES; do
# Здесь мы пропускаем бинарные и удалённые файлы
if [ ! -f "$FILE" ]; then
continue
fi
# Ищем строки с "console.log" или "debugger"
if grep -nE "console\.log|debugger" "$FILE" >/dev/null 2>&1; then
echo "Найден отладочный код в файле $FILE"
echo "Удалите console.log / debugger перед коммитом"
exit 1
fi
done
# Если проблем нет - разрешаем коммит
exit 0
Обратите внимание:
- мы используем комментарии, чтобы пояснить шаги;
- достаточно вернуть
exit 1, чтобы отменить коммит; - такие проверки можно постепенно расширять.
Использование инструментов форматирования в pre-commit
Частый сценарий — автоматически форматировать код (например, gofmt, prettier, black) перед коммитом.
Пример для JavaScript/TypeScript с использованием prettier:
#!/bin/sh
# Форматируем только изменённые staged-файлы
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.js$|\.ts$')
[ -z "$FILES" ] && exit 0
echo "Форматирование файлов через prettier..."
# Для каждого файла вызываем prettier
for FILE in $FILES; do
# Запускаем форматирование
npx prettier --write "$FILE"
# Добавляем уже отформатированный файл обратно в индекс
git add "$FILE"
done
echo "Форматирование завершено"
exit 0
Здесь мы не блокируем коммит, а исправляем код и пересобираем индекс.
Хук prepare-commit-msg
prepare-commit-msg вызывается после создания файла сообщения коммита, но до открытия редактора. Он получает путь к файлу сообщения и может изменять его.
Этот хук удобен, когда вы хотите:
- автоматически добавлять номер задачи в начало сообщения;
- генерировать шаблон сообщения.
Давайте посмотрим, как можно автоматически добавлять идентификатор ветки в сообщение коммита. Допустим, ветки называются так: feature/PROJ-123-some-task.
#!/bin/sh
# $1 - путь к файлу с сообщением коммита
MSG_FILE="$1"
# Определяем текущую ветку
BRANCH_NAME=$(git symbolic-ref --short HEAD 2>/dev/null)
# Если это не обычная ветка (например, detached HEAD) - просто выходим
[ -z "$BRANCH_NAME" ] && exit 0
# Ищем в названии ветки шаблон вида PROJ-123
ISSUE_ID=$(echo "$BRANCH_NAME" | grep -oE '[A-Z]+-[0-9]+')
# Если ID задачи не найден - выходим
[ -z "$ISSUE_ID" ] && exit 0
# Добавляем ID задачи в начало сообщения, если его там ещё нет
if ! grep -q "$ISSUE_ID" "$MSG_FILE"; then
# Здесь мы изменяем файл сообщения
sed -i.bak "1s/^/[$ISSUE_ID] /" "$MSG_FILE"
fi
exit 0
Как видите, этот код подставляет номер задачи из ветки прямо в сообщение коммита.
Хук commit-msg
commit-msg вызывается после того, как пользователь ввёл сообщение коммита, но до завершения коммита. Он получает один аргумент — путь к файлу с сообщением — и должен вернуть 0 или 1.
Этот хук часто используют, чтобы:
- проверить формат сообщения (например, Conventional Commits);
- требовать наличие номера задачи;
- запрещать пустые или слишком короткие сообщения.
Смотрите пример проверки простого формата сообщения: type(scope): description.
#!/bin/sh
MSG_FILE="$1"
# Читаем первую строку сообщения
FIRST_LINE=$(head -n 1 "$MSG_FILE")
# Проверяем формат через регулярное выражение
# Допускаем типы feat, fix, chore, refactor
echo "$FIRST_LINE" | grep -Eq '^(feat|fix|chore|refactor)(\([a-z0-9_-]+\))?: .+'
if [ $? -ne 0 ]; then
echo "Сообщение коммита должно быть в формате:"
echo " type(scope): description"
echo "Примеры:"
echo " feat(api): add auth endpoint"
echo " fix(ui): correct button color"
exit 1
fi
exit 0
Если сообщение не проходит проверку, пользователь увидит подсказку и коммит не будет создан.
Хук pre-push
pre-push срабатывает перед отправкой данных на удалённый репозиторий. Это удобное место, чтобы:
- запускать более тяжёлые тесты;
- запрещать пуш в определённые ветки;
- проверять синхронизацию с удалённой веткой.
Теперь вы увидите, как выглядит пример pre-push, который запрещает пуш напрямую в ветку main:
#!/bin/sh
# $1 - имя удалённого репозитория (origin)
# $2 - URL удалённого репозитория
REMOTE_NAME="$1"
# Читаем пары local_ref local_sha remote_ref remote_sha из stdin
while read local_ref local_sha remote_ref remote_sha
do
# Здесь remote_ref может быть, например, refs/heads/main
if echo "$remote_ref" | grep -q "refs/heads/main"; then
echo "Пуш в ветку main запрещён. Используйте Pull Request."
exit 1
fi
done
exit 0
Git передаёт информацию о том, какие ветки вы пушите, через стандартный ввод, и хук может принять решение заблокировать операцию.
Хук post-checkout, post-merge и другие
Существуют и другие полезные клиентские хуки:
post-checkout— вызывается послеgit checkout;post-merge— послеgit merge;post-rewrite— послеgit commit --amendилиgit rebase.
Они не влияют на успех операции (возврат ненулевого кода игнорируется), но позволяют выполнять синхронизацию окружения, очистку кэша, обновление зависимостей.
Пример post-merge, который запускает установку зависимостей, если изменился файл package-lock.json:
#!/bin/sh
# Проверяем, изменился ли package-lock.json в результате merge
if git diff --name-only HEAD@{1} HEAD | grep -q "package-lock\.json"; then
echo "Обнаружены изменения в package-lock.json"
echo "Запуск npm install..."
# Запускаем установку зависимостей
npm install
echo "Установка зависимостей завершена"
fi
exit 0
Здесь мы используем специальный синтаксис HEAD@{1}, чтобы сравнить состояние до merge и после.
Основные серверные хуки
Серверные хуки находятся в директории hooks внутри bare-репозитория на сервере (репозиторий, созданный через git init --bare). Они позволяют централизованно контролировать поведение при пуше.
Хук pre-receive
pre-receive вызывается на сервере до того, как будут приняты объекты и обновлены ссылки. Git передаёт в стандартный ввод список строк:
- старый SHA;
- новый SHA;
- ссылка (например,
refs/heads/main).
Смотрите пример, который запрещает форсированные пуши в любую ветку:
#!/bin/sh
# Читаем обновления ссылок из stdin
while read old_sha new_sha ref_name
do
# Если старый SHA не нулевой и новый SHA не является потомком старого,
# то это, скорее всего, force push
if [ "$old_sha" != "0000000000000000000000000000000000000000" ]; then
# Проверяем, что новый коммит содержит старый в своей истории
if ! git merge-base --is-ancestor "$old_sha" "$new_sha"; then
echo "Force push в $ref_name запрещён"
exit 1
fi
fi
done
exit 0
Такой хук защищает историю веток на сервере, даже если локальные настройки разработчика разрешают push --force.
Хук update
update вызывается отдельно для каждой обновляемой ссылки. Он получает три аргумента:
- имя ссылки;
- старый SHA;
- новый SHA.
Этот хук удобно использовать для более точечного контроля:
- запрет пуша в конкретные ветки;
- проверка прав пользователя (если вы интегрируете Git с системой аутентификации).
Пример update, который запрещает пуш в ветку main, но разрешает в другие:
#!/bin/sh
REF_NAME="$1"
OLD_SHA="$2"
NEW_SHA="$3"
# Разрешаем создание новых веток и удаление
# (OLD_SHA или NEW_SHA может быть нулевым)
NULL_SHA="0000000000000000000000000000000000000000"
# Если это ветка main и она не удаляется
if [ "$REF_NAME" = "refs/heads/main" ] && [ "$NEW_SHA" != "$NULL_SHA" ]; then
echo "Запрещено обновлять ветку main напрямую"
echo "Создайте отдельную ветку и используйте Pull Request"
exit 1
fi
exit 0
Хук post-receive
post-receive вызывается после успешного приёма пуша и обновления ссылок. Он идеален для запуска CI/CD:
- сборка проекта;
- прогон тестов;
- деплой на сервер.
Покажу вам, как можно организовать простой автодеплой с помощью post-receive. Допустим, у нас есть bare-репозиторий /srv/git/project.git и рабочая директория /srv/www/project.
#!/bin/sh
# Путь к рабочей директории, куда мы будем выкатывать код
WORK_TREE="/srv/www/project"
GIT_DIR="/srv/git/project.git"
# Читаем обновления ссылок из stdin
while read old_sha new_sha ref_name
do
# Нас интересует только ветка main
if [ "$ref_name" = "refs/heads/main" ]; then
echo "Обновление ветки main - выполняем деплой"
# Выгружаем свежий код в рабочую директорию
git --work-tree="$WORK_TREE" --git-dir="$GIT_DIR" checkout -f main
# Запускаем сборку или перезапуск сервиса
cd "$WORK_TREE" || exit 1
# Здесь может быть любая команда деплоя
./deploy.sh
echo "Деплой завершён"
fi
done
exit 0
Комментарии в скрипте помогают ясно увидеть, что происходит при каждом пуше в main.
Написание хуков: язык, права доступа, окружение
Какой язык использовать
Git не ограничивает вас в выборе языка. В хук-файле вы можете написать:
- shell-скрипт (
sh,bash), что чаще всего и делают; - Python;
- Ruby;
- Node.js;
- любой другой язык, который установлен в системе.
Главное — указать корректную «шапку» (shebang) в начале файла.
Примеры:
#!/bin/sh
# Простой shell-скрипт
echo "Shell хук"
#!/usr/bin/env python3
# Пример хука на Python
import sys
# Здесь мы печатаем аргументы, переданные Git
print("Args from Git:", sys.argv)
sys.exit(0)
#!/usr/bin/env node
// Пример хука на Node.js
// Здесь мы читаем аргументы командной строки
console.log("Hook called with args", process.argv);
// Код возврата 0 - успех
process.exit(0);
Важно: файл должен быть исполняемым для пользователя, под которым запускается Git (chmod +x).
Переменные окружения
Git передаёт хуку не только аргументы, но и окружение:
GIT_DIR— путь к каталогу.git(или bare-репозиторию);PWD— текущая директория;PATH— путь до исполняемых файлов.
Вам нужно следить, чтобы:
- используемые в хуке команды были доступны в
PATH; - путь к интерпретатору (bash, python, node) был корректным на целевой машине.
Чтобы избежать проблем, имеет смысл использовать #!/usr/bin/env ..., так легче переносить хуки между разными системами.
Распространённые сценарии использования Git hooks
Автоматический запуск тестов
Частая практика — запускать тесты в pre-commit или pre-push.
Пример pre-push, который выполняет тесты перед пушем:
#!/bin/sh
echo "Запуск тестов перед пушем..."
# Здесь мы запускаем тесты вашего проекта
# Например, для Node.js:
npm test
TEST_RESULT=$?
if [ $TEST_RESULT -ne 0 ]; then
echo "Тесты завершились с ошибкой. Пуш отменён."
exit 1
fi
echo "Тесты прошли успешно. Выполняем пуш..."
exit 0
Такой подход позволяет не отправлять на сервер очевидно «ломающий» код.
Проверка на секреты и ключи
Хуки можно использовать, чтобы проверять, не попали ли в коммит файлы с паролями, ключами или токенами.
Пример pre-commit, который ищет подозрительные строки в staged-файлах:
#!/bin/sh
FILES=$(git diff --cached --name-only --diff-filter=ACM)
[ -z "$FILES" ] && exit 0
# Регулярные выражения для простых проверок
PATTERNS='(AWS_SECRET_ACCESS_KEY|PRIVATE KEY|BEGIN RSA|password\s*=|token\s*=)'
for FILE in $FILES; do
if [ ! -f "$FILE" ]; then
continue
fi
# Проверяем только текстовые файлы
if file "$FILE" | grep -qi "text"; then
if grep -En "$PATTERNS" "$FILE" >/dev/null 2>&1; then
echo "Похоже, в файле $FILE есть секретные данные"
echo "Проверьте файл и удалите секреты перед коммитом"
exit 1
fi
fi
done
exit 0
Это не идеальная защита, но уже хороший дополнительный барьер.
Поддержка единого стиля коммитов в команде
Комбинация prepare-commit-msg и commit-msg позволяет привести сообщения коммитов к единому формату.
Например:
prepare-commit-msgдобавляет шаблон с подсказками;commit-msgстрого проверяет формат первой строки.
Тем не менее важно помнить, что локальные клиентские хуки не версионируются вместе с репозиторием (по умолчанию) и не навязываются другим разработчикам. Поэтому для командных правил обычно:
- либо используют серверные хуки;
- либо подключают инструменты вроде Husky (для JavaScript) или pre-commit (Python), которые управляют хуками через файлы конфигурации в репозитории.
Версионирование и распространение хуков в команде
Почему хуки не коммитятся по умолчанию
Каталог .git/hooks лежит внутри .git, а содержимое .git не входит в рабочее дерево репозитория. Поэтому файлы хуков:
- не отслеживаются Git;
- не попадают в коммиты;
- не передаются коллегам автоматически.
Это сделано намеренно: хуки могут выполнять произвольный код, и их неконтролируемое распространение небезопасно.
Как всё же делиться хуками
Есть несколько практичных способов.
Вариант 1. Скрипт установки из папки проекта
Вы можете хранить шаблоны хуков в каталоге проекта, например .githooks, и добавить скрипт установки:
Структура:
.githooks/pre-commit.githooks/commit-msgscripts/install-git-hooks.sh
Смотрите, как может выглядеть скрипт установки:
#!/bin/sh
# Здесь мы задаём путь к каталогу с хуками в проекте
HOOKS_DIR=".githooks"
# Здесь - путь к локальному каталогу хуков Git
GIT_HOOKS_DIR=".git/hooks"
echo "Установка хуков из $HOOKS_DIR в $GIT_HOOKS_DIR"
# Создаём каталог, если он ещё не существует
mkdir -p "$GIT_HOOKS_DIR"
# Копируем все файлы из .githooks в .git/hooks
for HOOK in "$HOOKS_DIR"/*; do
HOOK_NAME=$(basename "$HOOK")
cp "$HOOK" "$GIT_HOOKS_DIR/$HOOK_NAME"
chmod +x "$GIT_HOOKS_DIR/$HOOK_NAME"
echo "Установлен хук: $HOOK_NAME"
done
echo "Установка хуков завершена"
Разработчик один раз запускает:
sh scripts/install-git-hooks.sh
и получает все актуальные хуки.
Вариант 2. Использование core.hooksPath на проект
Можно настроить путь к хукам конкретного репозитория:
git config core.hooksPath .githooks
После этого Git будет искать хуки в .githooks вместо .git/hooks. Дальше вы просто кладёте файлы хуков в .githooks и коммитите их, как обычные файлы.
Это более прозрачный способ — хуки версионируются и синхронизируются вместе с кодом. Но имейте в виду, что Git всё равно не выполнит хуки автоматически на машинах коллег, пока они сами не выполнят настройку core.hooksPath.
Практические рекомендации и подводные камни
Не перегружайте хуки тяжёлыми задачами
Если pre-commit выполняет 10 минут, разработчики начнут его отключать. Старайтесь:
- держать хуки быстрыми;
- запускать тяжёлые проверки в
pre-pushили на сервере (CI); - кэшировать результаты, если возможно.
Хорошая стратегия:
- в
pre-commit— быстрые линтеры и форматирование; - в
pre-push— более полный набор тестов; - на сервере — «истинный» источник правды: полный прогон CI.
Учитывайте кроссплатформенность
Если в команде разные ОС (Windows, Linux, macOS), важно:
- избегать специфичных команд (
sed -iбез учёта разных реализаций,bash-конструкций, не поддерживаемых вsh); - по возможности писать хуки на языках, одинаково работающих на всех платформах (Python, Node.js).
Также стоит обратить внимание на окончания строк:
- Windows — чаще
CRLF; - Unix-системы —
LF.
Некоторые команды (grep, sed) могут по-разному вести себя при смешанных окончаниях строк.
Обработка ошибок и логирование
Если хук завершился с ошибкой, разработчик должен понимать, почему. Старайтесь:
- писать понятные сообщения в
stderrилиstdout; - указывать, что нужно сделать, чтобы исправить ситуацию;
- логировать важные детали для отладки.
Пример:
#!/bin/sh
echo "Запуск проверки форматирования..." 1>&2
# Здесь мы запускаем gofmt для Go-файлов
UNFORMATTED=$(gofmt -l .)
if [ -n "$UNFORMATTED" ]; then
echo "Неформатированные файлы:" 1>&2
echo "$UNFORMATTED" 1>&2
echo "Исправьте форматирование с помощью 'gofmt -w .' и повторите коммит" 1>&2
exit 1
fi
exit 0
Заключение
Хуки Git — это мощный встроенный механизм автоматизации, который позволяет интегрировать проверки, форматирование, тесты и даже деплой прямо в рабочий процесс Git. По сути, это программируемые «точки расширения» вокруг ключевых операций Git: коммита, пуша, слияния, переключения веток и приёма изменений на сервере.
Вы можете настроить:
- локальные (клиентские) хуки, чтобы поддерживать качество кода на своей машине;
- серверные хуки, чтобы обеспечить единые правила для всей команды и запустить CI/CD-процессы;
- инфраструктуру распространения хуков, чтобы все разработчики работали по одинаковым правилам.
Важно подходить к хукам как к части архитектуры проекта:
- держать их в репозитории (через
.githooksиcore.hooksPathили собственный инсталлер); - документировать, что именно делает каждый хук;
- не перегружать «горячие точки» дорогими операциями;
- следить за кроссплатформенностью и понятными сообщениями об ошибках.
Если вы выстроите эту систему аккуратно, хуки станут незаметным, но очень полезным помощником, который освобождает от рутины и помогает поддерживать единые стандарты в команде.
Частозадаваемые технические вопросы по Git hooks
Как временно отключить все хуки при выполнении команды Git
Иногда нужно сделать коммит или пуш, игнорируя хуки (например, для срочного исправления). Можно воспользоваться переменной окружения:
# Отключаем хуки для одного коммита
HUSKY=0 git commit -m "..." # для проектов с Husky
# Универсальный способ - временно переименовать каталог с хуками
mv .git/hooks .git/hooks.bak
git commit -m "..."
mv .git/hooks.bak .git/hooks
Для серверных хуков часто достаточно временно изменить права доступа файла (убрать исполняемый бит) и вернуть обратно.
Почему мой хук не запускается вообще
Проверьте несколько вещей:
- файл хука находится в правильной директории (
.git/hooksили в той, что указана вcore.hooksPath); - имя файла без расширения (
pre-commit, а неpre-commit.sh); - у файла есть права на выполнение (
chmod +x .git/hooks/pre-commit); - строка shebang указывает на существующий интерпретатор (
#!/bin/sh,#!/usr/bin/env python3).
Также убедитесь, что хук соответствует событию (например, pre-push не сработает при git commit).
Как передать данные из одного хука в другой
Обычно хуки изолированы, но вы можете использовать:
- временные файлы (например, в
/tmpили в.git/hooks/tmp); - переменные окружения (если хуки вызываются в рамках одного процесса сценария);
- специальные файлы Git (например, добавлять информацию в сообщение коммита и затем читать её в
commit-msg).
Пример — prepare-commit-msg записывает в файл .git/hooks/context, а commit-msg читает его и добавляет данные в сообщение.
Как запускать один и тот же хук для нескольких событий
Если вы хотите переиспользовать код, то:
- поместите общую логику в отдельный скрипт, например
scripts/git-hook-common.sh; - в каждом хуке вызывайте его.
Пример:
#!/bin/sh
# файл .git/hooks/pre-commit
# Запускаем общий скрипт
sh scripts/git-hook-common.sh pre-commit
#!/bin/sh
# файл scripts/git-hook-common.sh
HOOK_NAME="$1"
# Здесь вы можете выполнять общие проверки
echo "Запущен общий скрипт для хука $HOOK_NAME"
Так проще поддерживать логику в одном месте.
Как отлаживать сложные хуки
Несколько приёмов:
- добавляйте подробные выводы с помощью
echoи перенаправляйте их в лог-файл, напримерecho "DEBUG..." >> /tmp/git-hook.log; - временно уберите
set -e(в bash), чтобы скрипт не завершался преждевременно; - запускайте скрипт вручную из командной строки, передавая те же аргументы, что даёт Git (можно посмотреть их через отладочные
echo "$@"в начале скрипта).
Постройте личный план изучения Git до уровня Middle — бесплатно!
Git — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Git
Лучшие курсы по теме

Основы Git
Антон Ларичев
TypeScript с нуля
Антон Ларичев