Free

Управление git-процессами с помощью хуков: порядок для коммитов Claude

Используйте PreToolUse хуки для перехвата git-команд в Bash — блокируйте прямые коммиты в main, неверный формат сообщений и опасные операции.


Claude Code имеет разрешение напрямую запускать git commit, git push и git checkout. Обычно это удобно, но также означает, что Claude может:

  • Закоммитить прямо в main
  • Написать произвольное сообщение коммита
  • Забыть добавить со-авторство
  • Выполнить force push, когда не следует

Хуки позволяют перехватывать эти операции до их выполнения — проверять, применять стандарты или полностью блокировать опасные действия.


Подход: PreToolUse для перехвата git-команд в Bash

Все git-операции в Claude Code выполняются через инструмент Bash. Значит, точка перехвата такая:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [...]
      }
    ]
  }
}

Хук получает полную строку команды через stdin. Проверяем, содержит ли она конкретные git-операции, и решаем — разрешить или заблокировать.

Базовый шаблон:

#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

if echo "$cmd" | grep -qE '^git commit'; then
  echo "Не соответствует стандартам" >&2
  exit 2  # Блокируем и уведомляем Claude
fi

Правило 1: Запрет прямых коммитов в main

Самое распространённое требование. Claude должен работать в feature-ветках, а не пушить напрямую в main.

#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

echo "$cmd" | grep -qE '^git (commit|push)' || exit 0

current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)

if [[ "$current_branch" == "main" || "$current_branch" == "master" ]]; then
  echo "Прямые коммиты в $current_branch запрещены. Сначала переключитесь на feature-ветку." >&2
  exit 2
fi

Получив это сообщение, Claude самостоятельно переключится на подходящую ветку.


Правило 2: Обязательный формат сообщения коммита

Многие команды используют Conventional Commits: feat: ..., fix: ..., docs: .... Но сообщения Claude не обязательно соответствуют вашему формату.

Проверяем перед коммитом:

#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

echo "$cmd" | grep -qE '^git commit' || exit 0

# Извлекаем сообщение после -m
msg=$(echo "$cmd" | grep -oP '(?<=-m )["\x27].*?["\x27]' | tr -d '"'"'" || true)

if [[ -z "$msg" ]]; then
  exit 0  # Нет флага -m, вероятно, используется редактор — пропускаем
fi

if ! echo "$msg" | grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{3,}'; then
  echo "Неверный формат сообщения коммита." >&2
  echo "Требуемый формат: <type>(<scope>): <description>" >&2
  echo "Пример: feat(auth): add OAuth login" >&2
  echo "Допустимые типы: feat | fix | docs | style | refactor | test | chore | perf | ci | build | revert" >&2
  exit 2
fi

Claude получит обратную связь и перепишет сообщение коммита в правильном формате.


Правило 3: Обязательное со-авторство

Вы хотите, чтобы каждый коммит Claude содержал указание авторства — для отслеживания участия ИИ. Но не хотите каждый раз вводить это вручную.

PostToolUse здесь подходит лучше — проверяем после завершения коммита и просим Claude добавить через --amend, если авторство отсутствует:

#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

echo "$cmd" | grep -qE '^git commit' || exit 0
echo "$cmd" | grep -q 'amend' && exit 0  # Избегаем бесконечного цикла

last_msg=$(git log -1 --format="%B" 2>/dev/null)
if ! echo "$last_msg" | grep -q 'Co-Authored-By'; then
  echo "В коммите отсутствует со-авторство." >&2
  echo "Выполните: git commit --amend -m \"\$(git log -1 --format='%s')\" --trailer 'Co-Authored-By: Claude Sonnet 4.6 <[email protected]>'" >&2
  exit 2
fi

Правило 4: Блокировка опасных git-операций

Некоторые команды сложно отменить:

#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

# Блокируем force push в основные ветки
if echo "$cmd" | grep -qE 'git push.*--force(-with-lease)?.*\b(main|master)\b'; then
  echo "Force push в main/master запрещён." >&2
  exit 2
fi

# Блокируем reset --hard (требуем явного разрешения)
if echo "$cmd" | grep -qE 'git reset --hard'; then
  echo "git reset --hard — деструктивная операция, заблокирована. Если действительно необходимо, добавьте комментарий # ALLOW: с указанием причины." >&2
  exit 2
fi

# Блокируем удаление удалённых веток
if echo "$cmd" | grep -qE 'git push.*--delete|git push.*:'; then
  echo "Удаление удалённых веток запрещено. При необходимости сделайте это вручную на GitHub." >&2
  exit 2
fi

Трюк с # ALLOW: для reset --hard позволяет Claude «разблокировать» операцию, когда она действительно нужна — при этом требуя явного обоснования.


Правило 5: Журнал аудита git

Записываем все git-операции, выполненные Claude:

#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

echo "$cmd" | grep -q '^git' || exit 0

echo "$(date '+%Y-%m-%d %H:%M:%S') GIT: $cmd" >> ~/.claude/git-audit.log

Используйте PostToolUse — записываем только фактически выполненные команды.


Полная конфигурация: комбинированный хук для git-процессов

.claude/hooks/git-guard.sh:

#!/bin/bash
set -e

input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command // empty')

[[ -z "$cmd" ]] && exit 0
echo "$cmd" | grep -q '^git' || exit 0

current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")

# 1. Блокируем коммиты и пуши в main/master
if echo "$cmd" | grep -qE '^git (commit|push)'; then
  if [[ "$current_branch" == "main" || "$current_branch" == "master" ]]; then
    echo "Текущая ветка $current_branch — прямые коммиты и пуши заблокированы. Переключитесь на feature-ветку." >&2
    exit 2
  fi
fi

# 2. Проверка формата сообщения коммита
if echo "$cmd" | grep -qE '^git commit.*-m'; then
  msg=$(echo "$cmd" | grep -oP '(?<=-m )["\x27][^\x27"]*["\x27]' | tr -d '"'"'" || true)
  if [[ -n "$msg" ]] && ! echo "$msg" | grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{3,}'; then
    echo "Неверный формат сообщения коммита." >&2
    echo "Формат: <type>(<scope>): <description>" >&2
    echo "Пример: fix(api): handle null response from upstream" >&2
    exit 2
  fi
fi

# 3. Блокируем опасные операции (если нет разрешающего комментария ALLOW)
if echo "$cmd" | grep -qE 'git push.*--force|git reset --hard|git push.*--delete'; then
  if ! echo "$cmd" | grep -q '# ALLOW:'; then
    echo "Опасная операция заблокирована: $cmd" >&2
    echo "Чтобы продолжить, добавьте в конец команды # ALLOW: <причина>" >&2
    exit 2
  fi
fi

settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/git-guard.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'cmd=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .command); echo \"$cmd\" | grep -q \"^git\" && echo \"$(date +\\'%Y-%m-%d %H:%M:%S\\') $cmd\" >> ~/.claude/git-audit.log || true'"
          }
        ]
      }
    ]
  }
}

Несколько важных моментов

Используйте Bash в качестве matcher, а не Git

У Claude Code нет специального инструмента Git — все git-команды выполняются через Bash. Matcher должен быть "Bash".

Не полагайтесь чрезмерно на регулярные выражения

Git-команды бывают разными: git commit -m "msg", git commit --message="msg", GIT_AUTHOR_NAME=xxx git commit. Слишком строгий regex пропустит крайние случаи. Перехватывайте наиболее распространённые опасные паттерны и доверяйте суждению Claude в остальном.

Глобальные vs. проектные настройки

Защита основной ветки и проверка формата коммитов — в проектный конфиг (.claude/settings.json), так как имена веток и соглашения об именовании отличаются в разных проектах. Журнал аудита — в глобальный, ведём общий файл ~/.claude/git-audit.log.


Итог

Управление git-процессами с помощью хуков сводится к: PreToolUse + Bash matcher + обнаружение git-команд.

Три правила, которые стоит настроить в первую очередь:
1. Запрет прямых коммитов в main
2. Обязательный формат сообщений коммитов
3. Блокировка force push, reset --hard и других деструктивных операций

Вместе с журналом аудита каждое действие Claude с git становится отслеживаемым. Начните с самого простого правила, убедитесь, что оно работает, затем добавляйте следующее.