Worktree creation rewrites shared core.hooksPath, silently disabling repo-managed git hooks for the whole clone

Open 💬 2 comments Opened Jun 10, 2026 by sstubbs-olemiss

Environment

  • Claude Code version: v2.1.170
  • Platform: Linux
  • Verified: 2026-06-10

Description

When Claude Code creates a session worktree (the desktop "worktree" checkbox, --worktree, or isolation: "worktree" for agents), it checks for <mainRepo>/.husky and then <mainRepo>/.git/hooks, and if the repo's core.hooksPath differs from that path it runs:

git config core.hooksPath <absolute path to mainRepo/.git/hooks>

with the cwd set to the new worktree, logging "Configured worktree to use hooks from main repository".

The problem: linked worktrees share the clone's config file, so this git config write lands in the SHARED .git/config. That silently disables any repo-managed custom hooks directory (for example the common committed-hooks pattern core.hooksPath=hooks) for the main checkout AND every other worktree, not just the session worktree.

Two factors make this worse:

  1. .git/hooks always exists (git populates it with sample files), so the "does hooksPath differ from .git/hooks" comparison fires for essentially every repo that uses a custom hooksPath.
  2. The rewrite is re-asserted on every new worktree creation, so even if the user notices and restores their core.hooksPath, the next Claude Code worktree session clobbers it again. There is no way to durably keep the setting.

Steps to reproduce

  1. Create a repo with a committed hooks directory:

``
git init demo && cd demo
mkdir hooks
printf '#!/bin/sh\necho "pre-push gate ran"\nexit 1\n' > hooks/pre-push
chmod +x hooks/pre-push
git config core.hooksPath hooks
git add . && git commit -m "init"
``

  1. Start a Claude Code session with worktree isolation (desktop worktree checkbox, claude --worktree, or an agent with isolation: "worktree").
  2. From the MAIN checkout, run git config core.hooksPath.

Expected: still hooks.
Actual: it now points at <repo>/.git/hooks (absolute path), and the committed pre-push hook no longer runs anywhere in the clone.

Real-world impact

In a work repo using committed hooks via core.hooksPath=hooks, all pre-push quality gates were silently disabled for the entire clone for several days before anyone noticed. The repo ultimately had to ship forwarding shim scripts inside .git/hooks (each shim exec-ing the corresponding committed hook) just to coexist with the rewrite.

Suggested fixes

Any of these would resolve it, listed in order of preference:

  1. Respect an existing core.hooksPath that already resolves to a repo-managed directory. Only rewrite when core.hooksPath is unset, or when .husky actually exists (the husky case appears to be what this logic was built for).
  2. If hooks must be redirected for the session worktree, scope the write to that worktree only: enable extensions.worktreeConfig and use git config --worktree core.hooksPath ... so the shared .git/config is never touched.
  3. At minimum, surface the config change to the user instead of applying it silently. A one-line notice that "core.hooksPath was changed from X to Y in the shared repo config" would have saved days of silently-skipped pre-push gates.

View original on GitHub ↗

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