PreToolUse hook matcher does not fire for Bash(git commit) and Bash(git push) on protected branch

Resolved 💬 4 comments Opened Mar 19, 2026 by wrburgess Closed May 26, 2026

Bug Report

Description

A PreToolUse hook configured to block git commit and git push on protected branches (main, master, develop) did not fire. Claude Code was able to commit and push directly to main without the hook intercepting the tool calls.

Hook Configuration

.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit|Bash(git commit:*)|Bash(git push:*)|Bash(git merge:*)|Bash(git rebase:*)",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/enforce-branch-creation.sh"
          }
        ]
      }
    ]
  }
}

.claude/hooks/enforce-branch-creation.sh:

#!/bin/bash

CURRENT_BRANCH=$(cd "$CLAUDE_PROJECT_DIR" && git rev-parse --abbrev-ref HEAD)
PROTECTED_BRANCHES=("main" "master" "develop")

for branch in "${PROTECTED_BRANCHES[@]}"; do
  if [ "$CURRENT_BRANCH" = "$branch" ]; then
    echo "Cannot make changes on '$CURRENT_BRANCH' branch. Please create a new branch first." >&2
    exit 2
  fi
done

exit 0

Expected Behavior

When on the main branch, any Bash tool call running git commit or git push should trigger the PreToolUse hook, which exits with code 2 and blocks the action.

Actual Behavior

Claude Code executed git commit -m "..." and git push on main without the hook firing. Two commits were pushed directly to main:

  1. git commit -m "fix: Replace perform_enqueued_jobs..." — not blocked
  2. git push — not blocked
  3. git rm ... && git commit -m "revert: Remove notification specs..." — not blocked
  4. git push — not blocked

Possible Cause

The matcher pattern Bash(git commit:*) may be matching against the tool's description field (after the colon) rather than the command field. If Claude provides a description like "Commit CI fix" rather than one starting with git commit, the pattern might not match. Alternatively, the * glob may not work as expected with the Bash(...) matcher syntax.

The Write and Edit matchers in the same rule do fire correctly on main (they block file edits), so the hook script itself works — the issue is specifically with the Bash(git commit:*) and Bash(git push:*) matcher patterns.

Environment

  • Claude Code CLI
  • Model: claude-opus-4-6 (1M context)
  • OS: macOS (Darwin 25.3.0)
  • Shell: zsh

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗