PreToolUse Hook で Bash 内の git コマンドを捕捉し、main への直接コミット・commit メッセージ違反・危険な操作を防ぐ。
Claude Codeはgit commit、git push、git checkoutを直接実行する権限を持っています。ほとんどの場合は便利ですが、次のような問題が起きる可能性もあります:
Hooksはこれらの操作が実行される前に介入できます——インターセプト、検証、規約の強制、または危険な操作のブロック。
Claude CodeのすべてのGit操作はBashツールを通じて実行されます。インターセプトポイントはここです:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [...]
}
]
}
}
Hookは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
fi
最も一般的な要件。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
多くのチームはConventional Commits(feat: xxx、fix: xxx)を使います。コミット前に検証します:
#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
echo "$cmd" | grep -qE '^git commit' || exit 0
msg=$(echo "$cmd" | grep -oP '(?<=-m )["\x27].*?["\x27]' | tr -d '"'"'" || true)
[[ -z "$msg" ]] && exit 0
if ! echo "$msg" | grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{3,}'; then
echo "commitメッセージのフォーマットが正しくありません。" >&2
echo "正しいフォーマット:<type>(<scope>): <description>" >&2
echo "例:feat(auth): add OAuth login" >&2
echo "使用可能なtype:feat | fix | docs | style | refactor | test | chore | perf | ci | build | revert" >&2
exit 2
fi
#!/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 "Co-Authored-By署名がありません。" >&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
#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
if echo "$cmd" | grep -qE 'git push.*--force(-with-lease)?.*\b(main|master)\b'; then
echo "main/masterへのforce pushは禁止です。" >&2
exit 2
fi
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
#!/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
.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")
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
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 "commitメッセージのフォーマットが正しくありません。" >&2
echo "フォーマット:<type>(<scope>): <description>" >&2
exit 2
fi
fi
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
matcherにはBashを使う(Gitではない)
Claude CodeにはGit専用ツールがなく、gitコマンドはすべてBashで実行されます。
正規表現に頼りすぎない
gitコマンドには様々な書き方があります。最も一般的な危険パターンだけをインターセプトし、残りはClaudeの判断を信頼しましょう。
グローバル vs プロジェクト単位
mainブランチ保護とフォーマット検証はプロジェクト単位(.claude/settings.json)に。監査ログはグローバルに、~/.claude/git-audit.logへ統一して記録します。
HooksでGitワークフローを管理する核心:PreToolUse + Bash matcher + gitコマンド検出。
最初に設定すべき3つのルール:
1. mainブランチへの直接コミットをブロック
2. commitメッセージフォーマットの検証
3. force push、reset --hardなどの危険な操作をインターセプト
監査ログと組み合わせることで、Claudeのすべてのgit操作を追跡可能にできます。