Free

Zarządzanie przepływem pracy git z hookami: Claude też musi przestrzegać zasad

Użyj PreToolUse Hooks do przechwytywania poleceń git w Bash — blokuj bezpośrednie commity do main, złe formaty wiadomości i niebezpieczne operacje.


Claude Code ma uprawnienia do bezpośredniego uruchamiania git commit, git push i git checkout. Zazwyczaj to wygodne — ale oznacza też, że Claude może:

  • Zacommitować prosto do main
  • Napisać wiadomość commita w dowolnym formacie
  • Zapomnieć o dodaniu współautorstwa
  • Wykonać force push, kiedy nie powinien

Hooki pozwalają przechwytywać te operacje zanim zostaną wykonane — weryfikować, egzekwować standardy lub całkowicie blokować niebezpieczne działania.


Podejście: PreToolUse do przechwytywania komend git w Bash

Wszystkie operacje git w Claude Code są wykonywane przez narzędzie Bash. Punkt przechwycenia to:

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

Hook otrzymuje pełny ciąg komendy przez stdin. Sprawdzamy, czy zawiera konkretne operacje git, i decydujemy — przepuścić czy zablokować.

Podstawowy wzorzec:

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

if echo "$cmd" | grep -qE '^git commit'; then
  echo "Nie spełnia wymagań" >&2
  exit 2  # Blokujemy i powiadamiamy Claude
fi

Reguła 1: Zakaz bezpośrednich commitów do main

Najczęstsze wymaganie. Claude powinien pracować na gałęziach feature, a nie pushować bezpośrednio do 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 "Bezpośrednie commity do $current_branch są niedozwolone. Najpierw przełącz się na gałąź feature." >&2
  exit 2
fi

Po otrzymaniu tej wiadomości Claude samodzielnie przełączy się na odpowiednią gałąź.


Reguła 2: Obowiązkowy format wiadomości commita

Wiele zespołów używa Conventional Commits: feat: ..., fix: ..., docs: .... Ale wiadomości Claude nie muszą pasować do Twojego formatu.

Weryfikujemy przed commitem:

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

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

# Wyciągamy wiadomość po -m
msg=$(echo "$cmd" | grep -oP '(?<=-m )["\x27].*?["\x27]' | tr -d '"'"'" || true)

if [[ -z "$msg" ]]; then
  exit 0  # Brak flagi -m, prawdopodobnie używany jest edytor — pomijamy
fi

if ! echo "$msg" | grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{3,}'; then
  echo "Nieprawidłowy format wiadomości commita." >&2
  echo "Wymagany format: <type>(<scope>): <description>" >&2
  echo "Przykład: feat(auth): add OAuth login" >&2
  echo "Dopuszczalne typy: feat | fix | docs | style | refactor | test | chore | perf | ci | build | revert" >&2
  exit 2
fi

Claude otrzyma informację zwrotną i przepisze wiadomość commita w prawidłowym formacie.


Reguła 3: Obowiązkowe współautorstwo

Chcesz, żeby każdy commit Claude zawierał informację o autorstwie — do śledzenia udziału AI. Ale nie chcesz wpisywać tego ręcznie za każdym razem.

PostToolUse jest tu lepszym rozwiązaniem — sprawdzamy po zakończeniu commita i prosimy Claude o --amend, jeśli brakuje współautorstwa:

#!/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  # Unikamy nieskończonej pętli

last_msg=$(git log -1 --format="%B" 2>/dev/null)
if ! echo "$last_msg" | grep -q 'Co-Authored-By'; then
  echo "Commit nie zawiera współautorstwa." >&2
  echo "Uruchom: git commit --amend -m \"\$(git log -1 --format='%s')\" --trailer 'Co-Authored-By: Claude Sonnet 4.6 <[email protected]>'" >&2
  exit 2
fi

Reguła 4: Blokowanie niebezpiecznych operacji git

Niektóre komendy trudno cofnąć:

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

# Blokujemy force push do głównych gałęzi
if echo "$cmd" | grep -qE 'git push.*--force(-with-lease)?.*\b(main|master)\b'; then
  echo "Force push do main/master jest niedozwolony." >&2
  exit 2
fi

# Blokujemy reset --hard (wymagamy jawnego zezwolenia)
if echo "$cmd" | grep -qE 'git reset --hard'; then
  echo "git reset --hard to operacja destrukcyjna — zablokowana. Jeśli naprawdę potrzebujesz, dodaj komentarz # ALLOW: z podaniem powodu." >&2
  exit 2
fi

# Blokujemy usuwanie zdalnych gałęzi
if echo "$cmd" | grep -qE 'git push.*--delete|git push.*:'; then
  echo "Usuwanie zdalnych gałęzi jest niedozwolone. Zrób to ręcznie na GitHubie, jeśli potrzebujesz." >&2
  exit 2
fi

Sztuczka z # ALLOW: dla reset --hard pozwala Claude „odblokować" operację, gdy jest naprawdę potrzebna — wymagając przy tym jawnego uzasadnienia.


Reguła 5: Dziennik audytu git

Rejestrujemy wszystkie operacje git wykonane przez 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

Używaj PostToolUse — rejestrujemy tylko faktycznie wykonane komendy.


Pełna konfiguracja: kombinowany hook dla przepływu pracy 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. Blokujemy commity i pushe do main/master
if echo "$cmd" | grep -qE '^git (commit|push)'; then
  if [[ "$current_branch" == "main" || "$current_branch" == "master" ]]; then
    echo "Aktualna gałąź to $current_branch — bezpośrednie commity i pushe są zablokowane. Przełącz się na gałąź feature." >&2
    exit 2
  fi
fi

# 2. Weryfikacja formatu wiadomości commita
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 "Nieprawidłowy format wiadomości commita." >&2
    echo "Format: <type>(<scope>): <description>" >&2
    echo "Przykład: fix(api): handle null response from upstream" >&2
    exit 2
  fi
fi

# 3. Blokujemy niebezpieczne operacje (jeśli nie ma komentarza ALLOW)
if echo "$cmd" | grep -qE 'git push.*--force|git reset --hard|git push.*--delete'; then
  if ! echo "$cmd" | grep -q '# ALLOW:'; then
    echo "Niebezpieczna operacja zablokowana: $cmd" >&2
    echo "Aby kontynuować, dodaj na końcu komendy # ALLOW: <powód>" >&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'"
          }
        ]
      }
    ]
  }
}

Kilka ważnych uwag

Użyj Bash jako matcher, nie Git

Claude Code nie ma dedykowanego narzędzia Git — wszystkie komendy git przechodzą przez Bash. Matcher musi być "Bash".

Nie polegaj nadmiernie na wyrażeniach regularnych

Komendy git mają wiele form: git commit -m "msg", git commit --message="msg", GIT_AUTHOR_NAME=xxx git commit. Zbyt restrykcyjny regex pominie przypadki brzegowe. Przechwytuj najpowszechniejsze niebezpieczne wzorce i ufaj osądowi Claude w pozostałych.

Konfiguracja globalna vs. projektowa

Ochrona głównej gałęzi i weryfikacja formatu commitów — do projektowego configa (.claude/settings.json), bo nazwy gałęzi i konwencje commitów różnią się między projektami. Dziennik audytu — do globalnego, zapisujemy do wspólnego pliku ~/.claude/git-audit.log.


Podsumowanie

Zarządzanie przepływem pracy git za pomocą hooków sprowadza się do: PreToolUse + Bash matcher + wykrywanie komend git.

Trzy reguły warte skonfigurowania w pierwszej kolejności:
1. Zakaz bezpośrednich commitów do main
2. Obowiązkowy format wiadomości commitów
3. Blokowanie force push, reset --hard i innych destrukcyjnych operacji

W połączeniu z dziennikiem audytu każde działanie Claude na git staje się możliwe do śledzenia. Zacznij od najprostszej reguły, upewnij się, że działa, a potem dodawaj kolejne.