-
Notifications
You must be signed in to change notification settings - Fork 444
Description
Describe the bug
netlify dev fails to detect repository root in git worktrees
Description
The Netlify CLI fails to properly detect the repository root when running inside a git worktree. This causes netlify dev to fall back to the user's home directory as the cwd, breaking all relative path resolution in netlify.toml and preventing the CLI from fetching environment variables from Netlify.
Environment
- netlify-cli version: 23.13.3
- OS: macOS (darwin-arm64)
- Node: v22.21.1
Steps to Reproduce
-
Create a git worktree from an existing repository:
cd ~/git/my-repo git worktree add ~/git/my-repo-worktree some-branch
-
Run
netlify linkin the worktree to link a site -
Run
netlify dev(ornetlify dev --cwd ./apps/my-appfor monorepos):cd ~/git/my-repo-worktree netlify dev
Expected Behavior
The CLI should detect ~/git/my-repo-worktree as the repository root and resolve all paths relative to it.
Actual Behavior
The CLI falls back to the user's home directory (/Users/username) as the cwd:
❯ Initial build environment
config: /Users/frank/git/knowfun-sandbox/apps/app-shareyourfans/netlify.toml
context: dev
cwd: /Users/frank # <-- Should be /Users/frank/git/knowfun-sandbox
This causes multiple failures:
-
Path resolution errors:
Base directory does not exist: /Users/frank/apps/app-shareyourfans -
Environment variables not loaded:
Warning: Could not find account for project 'undefined' with account slug 'undefined' -
Plugin resolution failures:
Plugin could not be found using local path: ../../netlify/plugins/netlify-plugin-bundle-env -
Monorepo workspace detection fails:
[project.ts]: detectFrameworksInPath - undefined
Root Cause
The issue is in @netlify/config at lib/options/repository_root.js:
export const getRepositoryRoot = async function ({ repositoryRoot, cwd }) {
if (repositoryRoot !== undefined) {
return repositoryRoot;
}
const repositoryRootA = await findUp('.git', { cwd, type: 'directory' });
if (repositoryRootA === undefined) {
return cwd;
}
return dirname(repositoryRootA);
};The findUp call specifies type: 'directory', but in a git worktree, .git is a file (not a directory) containing a pointer to the main repository:
# Contents of .git file in a worktree
gitdir: /Users/frank/git/my-repo/.git/worktrees/my-repo-worktree
Since findUp is looking for a directory and .git is a file, it never finds it and falls back to cwd — which through some other code path ends up as the user's home directory.
Suggested Fix
Modify the findUp call to find both files and directories by removing the type constraint:
const repositoryRootA = await findUp('.git', { cwd });Or explicitly handle both cases:
const repositoryRootA = await findUp('.git', { cwd, type: 'directory' })
?? await findUp('.git', { cwd, type: 'file' });Workaround
Users can patch their local installation:
sed -i '' "s/, type: 'directory'//" node_modules/netlify-cli/node_modules/@netlify/config/lib/options/repository_root.jsOr set the REPOSITORY_ROOT environment variable (though this alone doesn't fully fix the issue since monorepo detection still fails):
REPOSITORY_ROOT=/path/to/worktree netlify devAdditional Context
Git worktrees are commonly used for:
- Working on multiple branches simultaneously
- Testing changes in isolation without stashing
- Running long builds while continuing development
This is a significant workflow that should be supported by the CLI.
Steps to reproduce
See Claude's writeup above
Configuration
[build]
publish = "dist/"
command = "npm run build"
edge_functions = "src/edge-functions/"
[build.environment]
NODE_VERSION = "22.18.0"
NPM_VERSION = "11.5.2"
NODE_SOURCEMAP = "true"
NODE_OPTIONS = "--max_old_space_size=4096"
[functions]
node_bundler = "esbuild"
directory = "src/functions/"
external_node_modules = ["@sentry/serverless", "@block65/webcrypto-web-push"]
deno_import_map = "deno-import-map.json"
[dev]
command = "vite"
targetPort = 5175
port = 8887
autoLaunch = false
[[plugins]]
package = "../../netlify/plugins/netlify-plugin-bundle-env"
[[redirects]]
from = "/service-worker.js.map"
to = "/index.html"
status = 404
force = true
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/dev-sw.js"
[headers.values]
Cache-Control = "no-cache"
[[headers]]
for = "/service-worker.js"
[headers.values]
Cache-Control = "no-cache"
Environment
System:
OS: macOS 26.2
CPU: (16) arm64 Apple M4 Max
Memory: 50.29 GB / 128.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 22.21.1 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 11.7.0 - /usr/local/bin/npm
pnpm: 7.27.0 - /usr/local/bin/pnpm
bun: 1.3.6 - /opt/homebrew/bin/bun
Deno: 2.4.5 - /Users/frank/.deno/bin/deno