EnterWorktree (name mode) writes core.hooksPath into the shared repo config; ExitWorktree doesn't restore it
Environment
Claude Code desktop app on macOS 26.5.1 (CLI claude --version: 2.1.97). Multi-session use on the same repos via EnterWorktree/ExitWorktree, worktree.baseRef: "fresh".
Observed behavior (proven by controlled experiment)
Starting from core.hooksPath unset in a repo:
| Action | core.hooksPath afterwards |
|---|---|
| git worktree add (manual) | unset (clean) |
| EnterWorktree with path: (enter existing worktree) | unset (clean) |
| EnterWorktree with name: (create worktree) | <abs>/.git/hooks — written to the SHARED repo config |
| ExitWorktree action=remove afterwards | still <abs>/.git/hooks — not restored |
Because this lands in the shared .git/config, it outlives the worktree and applies to the main checkout and all other worktrees. Any repo that wires its versioned hooks via core.hooksPath (e.g. npm prepare → git config core.hooksPath .githooks) has them silently disabled for every checkout the moment any session creates a name-mode worktree — and re-disabled after every repair, since every new name-mode worktree rewrites it. We chased this as a mystery config-mutator across four repos before isolating it; combined with stale hook copies it produced an afternoon of blocked pushes across parallel sessions.
Related doc-vs-behavior gap
ExitWorktree action=remove documents "deletes the worktree directory and its branch", but after a mid-session git branch -m worktree-<name> task/<name> rename it (a) refuses removal counting already-pushed commits as discardable, and (b) after discard_changes: true removes the directory but leaves the renamed local branch behind.
Suggestions
- Use
extensions.worktreeConfig+ per-worktreeconfig.worktreefor the hooksPath override instead of mutating shared state; or restore the prior value on exit. - Track the worktree by path rather than original branch name so a renamed branch is still recognized as the worktree's own.
Happy to provide the experiment transcript.