Skip to content

Worktree creation writes an ABSOLUTE core.hooksPath into config.worktree, so worktrees run the MAIN checkout's hooks #88747

Description

@DearMaestro

Preflight Checklist

Not a duplicate of the known shared-config bug — this is the half that is still wrong

#27474, #72714 and #85039 all report worktree creation clobbering the shared config
($GIT_COMMON_DIR/config / .git/config). On 2.1.237 that no longer happens here — the shared
config keeps its correct relative value:

$ git config --local --get core.hooksPath
.husky/_                                  # ← intact, not rewritten

What is written is a per-worktree override, in the worktree scope #27474 suggested moving to:

$ cat .git/worktrees/<name>/config.worktree
[core]
	longpaths = true
	hooksPath = /Users/…/<MAIN CHECKOUT>/.husky/_     # ← absolute, points at another checkout

So the destructive half looks fixed. But the path is still absolute, and #27474's author already
called that out: "even git config --worktree is pointless since, if sharing hooks is the goal,
it's already achieved by git-config resolution."
That is exactly right, and the absolute form has
its own failure mode, which is the reason for this report.

Consequence

extensions.worktreeConfig = true makes the worktree scope outrank the shared one, and husky's shim
resolves the real script from its own location:

s=$(dirname "$(dirname "$0")")/$n     # .husky/_/pre-push  ->  .husky/pre-push

With $0 absolute, $s is absolute too, so every hook fired from the worktree executes the MAIN
checkout's scripts — on whatever branch that checkout happens to be sitting on.
Nothing errors; the
hook simply prints the steps that other branch has.

Two ways it bites, and the second is the dangerous one:

  1. A PR that ADDS a hook step gets no local exercise. The step never fires. This is how we found
    it: a new pre-push step was present in the worktree's .husky/pre-push and simply absent from the
    git push output.
  2. A PR that WEAKENS or REMOVES a hook step still runs the old, stronger hook — so the push goes
    green on a change that disabled a gate. For any team whose hooks are the only gate, that is a
    silently removed safety net.

A quieter third: whichever branch the main checkout is on defines the gate for every worktree.
Ours was on a feature branch, not main, for the whole period.

Attribution — by control, not by reading

check result
plain git worktree add (control) writes no config.worktree; core.hooksPath resolves to .husky/_ from local scope
husky 9.1.7 source writes `${dir}/_` — relative — and never mentions longpaths
git grep across the repo nothing tracked writes either key
registered tool-made worktrees 5 of 5 carry the same longpaths + absolute hooksPath pair
the one hand-made worktree beside them no config.worktree at all

Repro

  1. In a repo using husky (so .git/config has the relative core.hooksPath = .husky/_), create a
    worktree via EnterWorktree or an Agent spawn with isolation: "worktree".
  2. From inside it: git config --show-scope --get-all core.hooksPath
local     .husky/_
worktree  /abs/path/to/MAIN/.husky/_        # ← wins
  1. Add a distinctive echo to that worktree's .husky/pre-push, commit, push. The line does not
    appear — the main checkout's hook ran instead.

Expected

Write nothing. Git already resolves a relative core.hooksPath per worktree, which is what the
repo's shared config asks for. If a value must be written, write the relative one.

Workaround

Per worktree, worktree-scoped so it touches nothing shared:

git config --worktree core.hooksPath .husky/_

⚠️ Only safe when that worktree has its own .husky/_ shim. If it does not, switching to a relative
path aims git at a directory that does not exist and disables hooks entirely — worse than the
bug. We hit that case for real on one worktree.

Secondary observations

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:toolsbugSomething isn't workinghas reproHas detailed reproduction stepsplatform:macosIssue specifically occurs on macOS

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions