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 стає відстежуваною. Починайте з найпростішого правила, переконайтеся, що воно працює, потім додавайте наступне.