·

Git Hooks Not Running in a Worktree? Fix the Path

Prepared and checked using AI. Sources are linked in the article.

git-worktrees workflow ai-coding

If Git hooks are not running in a worktree, first check core.hooksPath. Linked worktrees normally use the repository’s shared hooks directory, so creating a worktree does not, by itself, remove a pre-commit or pre-push hook. A configured path can point Git somewhere else, though, and a relative path may exist in the main checkout but be missing from the agent’s checkout.

Understand which path Git uses

A linked worktree has its own working directory and private Git state, while its .git file points to that private directory and back to the common repository. That makes the visible .git file a poor place to look for hooks. With no override, Git resolves the hooks directory through the common repository; a hook installed there is available to the linked worktrees.

Git can instead use core.hooksPath. For ordinary, non-bare worktrees, Git runs hooks from the worktree root, and ignores hook files without the executable bit. pre-commit runs during a commit; pre-push runs before a push. Check the exact hook filename: pre-commit, commit-msg, and pre-push are distinct entry points.

A relative core.hooksPath such as .githooks is resolved from the root of the worktree performing the commit or push. An absolute path refers to the same filesystem location from every worktree. The relative choice is useful when each branch should use the hook scripts checked out with that branch; the absolute choice is useful when one maintained directory should serve all worktrees. Either choice fails to run the intended hook if the selected directory lacks it.

For more on what worktrees share and keep separate, see Git worktrees explained.

Inspect the effective configuration before changing it

Run these commands inside the agent worktree. Git’s --show-origin and --show-scope options identify where a config value came from, including a worktree, local, or global setting:

git config --show-origin --show-scope --get core.hooksPath
git rev-parse --show-toplevel
git rev-parse --git-dir
git rev-parse --git-common-dir
git rev-parse --git-path hooks

An unset core.hooksPath produces no value from the first command; that points you toward Git’s default hooks directory. The rev-parse options identify the worktree root, private Git directory, common Git directory, and resolved Git path. git rev-parse --git-path hooks reports the effective hooks directory: it honors core.hooksPath when set, and otherwise resolves the shared default.

If a path is configured, go to the worktree root and inspect that path. For example, if the reported value is .githooks, inspect .githooks/pre-commit or .githooks/pre-push there. Check that the file exists, has the exact hook name, and is executable. If the value is /dev/null, hooks have been disabled through Git configuration. If it is an absolute path into another checkout, determine whether that checkout still exists and whether using its scripts is intentional.

Also inspect the command that produced the commit or push. A manual commit can run hooks while an agent command uses a bypass flag. For commits, --no-verify bypasses pre-commit and commit-msg; for pushes, it bypasses pre-push. If the hook is present and executable, this distinction can explain why two commit paths behave differently.

Use a tracked hook directory across agent worktrees

For a team that wants branch-local scripts, keep hook files in a tracked directory such as .githooks. Configure the repository once:

git config --local core.hooksPath .githooks

Put an executable file at .githooks/pre-commit, and add a .githooks/pre-push file if you need a push check. Because the relative path is resolved in each worktree, each agent uses the scripts present in its checkout. Review changes to those scripts like other code: an agent branch can change what its own hooks run.

For example, a project could keep its commit check in ./scripts/check-commit.sh and call it from this hook:

#!/bin/sh
set -eu
cd "$(git rev-parse --show-toplevel)"
./scripts/check-commit.sh

Make the hook executable before committing it:

chmod +x .githooks/pre-commit
git add .githooks/pre-commit

This setup assumes that ./scripts/check-commit.sh exists in the branch and that its required tools are available in that worktree. A missing dependency usually makes a hook fail when invoked; a missing or non-executable hook file can make it appear not to run at all. Check these separately before changing Git configuration.

The --local setting belongs to this repository’s shared configuration, so it applies to its linked worktrees unless a worktree-specific value overrides it. Hook files committed to the project can travel with the project, but the local Git config setting does not become a tracked project file. Someone using a fresh clone must configure the path there as part of their setup.

If you already use hooks in the default shared Git directory, you may not need core.hooksPath. Remove an accidental local override with git config --local --unset core.hooksPath, then inspect the effective value again; a global or worktree-specific value may still apply. Confirm that the intended file is in the directory Git now selects.

Handle Husky and one-worktree exceptions

For a Husky-managed project, do not assume .githooks is the right value. Husky’s hook troubleshooting checks for core.hooksPath pointing to .husky/_. Inspect that value from the agent worktree and check that its .husky files are present. Husky’s manual setup uses a prepare script and npm run prepare to set up its files; if a fresh worktree lacks those generated files, run the project’s setup in that worktree after reviewing its scripts. Do not replace a working Husky path with .githooks unless the project is intentionally changing hook systems.

If one agent worktree genuinely needs a different path, Git supports worktree-specific configuration. Before enabling it, inspect the shared repository config for core.bare and core.worktree. If either entry exists, move it from the shared config to the main worktree’s config.worktree, preserving its value, before running the first command below. Enabling the extension ends Git’s special treatment of those shared entries as applying only to the main worktree. Then set the hook path inside the chosen worktree:

git config extensions.worktreeConfig true
cd ../agent-experiment
git config --worktree core.hooksPath .agent-hooks

Here ../agent-experiment and .agent-hooks are examples. Ensure .agent-hooks exists in that worktree and contains the intended executable files. The extensions.worktreeConfig setting enables per-worktree configuration across the repository, while the --worktree setting changes the chosen worktree. Git warns that older versions may refuse repositories using the extension, so use it when the separate configuration is worth that compatibility cost.

Avoid setting core.hooksPath to /dev/null as a standing fix for a troublesome agent hook. It disables all Git hooks selected through that setting. Fix the path or the hook’s failing command, then verify the resulting commit and push workflow.

Verify the hook without relying on a successful commit

Check the exact file Git should invoke, then temporarily add exit 1 to a pre-commit hook and attempt a commit you are prepared to abort. If the hook runs, the commit stops; remove the temporary line afterward. If the commit proceeds, recheck the effective path, executable bit, filename, and bypass flags. For pre-push, inspect the file and configuration before using a real remote as a test target.

Run the same check from each relevant worktree. A relative path can resolve to a valid directory in one checkout and an absent directory in another branch. When multiple agents prepare branches at once, make hook setup part of the worktree setup procedure and review their resulting diffs before integration; reviewing AI-generated code covers that next step.

Local hooks help catch mistakes early, but they cannot enforce a repository-wide rule. Git recommends remote-side hooks or CI for checks that must be enforced, because users can bypass local hooks. Keep required checks in that shared gate even when every agent worktree has working hooks.

Where Parallel Code fits

Parallel Code is our free, open-source desktop app for macOS and Linux. It runs several AI coding agents in parallel, each isolated in its own Git worktree, and gives you a diff-first review surface. The hook path and setup choices above apply to those worktrees just as they do to worktrees you create yourself.

Frequently asked questions

Does a linked worktree get its own .git/hooks directory?

The linked worktree has private Git state, but Git’s default hooks directory is shared through the common repository. Use git rev-parse --git-path hooks from the worktree to locate the effective hooks directory: it honors core.hooksPath when set and otherwise shows the shared default.

Should core.hooksPath be absolute or relative?

Use a relative path when each worktree should run the hook scripts in its own checkout. Use an absolute path when every worktree should run scripts from one maintained location; make sure that location remains available.

Why does the hook work in the main checkout but not an agent worktree?

Inspect the effective core.hooksPath in both places, then check whether the selected file exists and is executable in the agent worktree. A relative path may point to files absent from its branch, and a hook manager may need its setup run there.

Are working local hooks enough to require checks on agent commits?

No. Local hooks can be bypassed, including with --no-verify for the relevant commit or push hooks. Put mandatory checks in CI or on the remote as well.