Blog / git worktrees
Git worktree missing .env and node_modules? Copy them in
A new git worktree has no .env, no node_modules and no spare port. Copy the ignored files, clone the install in a second, and give each worktree its own port.
A new git worktree is a clean checkout of your tracked files and nothing else. Your .env, node_modules, build caches and local config are all gitignored, so none of them come along. An agent started there runs the dev server, finds no database URL and no packages, and spends its first ten minutes installing and guessing.
The fix is three steps you do once per worktree: copy the ignored files you need, bring the install over (fast), and give the worktree its own port. Below is each step, then the cleanup, with every command tested on a real repo.
What a new worktree is missing#
Make a worktree and look inside:
git worktree add ../shop-fix -b fix/cart
ls -a ../shop-fixYou get .git, .gitignore, package.json and the rest of your tracked files. You don't get:
| Missing | Why it matters |
|---|---|
.env, .env.local | The app starts without its database URL or keys |
node_modules | Every command fails until someone installs |
Local config (config/secrets.json and similar) | Tests read the wrong settings |
| A port of its own | Two dev servers both want port 3000 |
The worktree also shares one thing you might not expect: the .git directory. Commits, branches and stashes are shared across all worktrees of a repository.
Step 1: copy the ignored files you need#
With Claude Code#
Claude Code reads a .worktreeinclude file at the root of your repository. It uses .gitignore syntax and lists the ignored files to copy into every worktree Claude Code creates:
.env
.env.local
config/secrets.jsonOnly files that match a pattern and are gitignored are copied, so tracked files are never duplicated. It applies to claude --worktree <name>, to subagents that run in their own worktree, and to parallel sessions in the desktop app. We checked it with claude -p --worktree test: the new worktree under .claude/worktrees/test had the .env from the main checkout. The Claude Code worktree docs have the details.
Commit .worktreeinclude so everyone on the team gets the same list. Add .claude/worktrees/ to .gitignore so those worktrees don't show up as untracked files.
With any other agent, or plain git#
The same file works for worktrees you make yourself. From the main checkout, this copies every untracked file that matches .worktreeinclude into the new worktree:
git ls-files -z --others --ignored --exclude-from=.worktreeinclude \
| xargs -0 -I{} cp -p {} ../shop-fix/{}Run the first line alone to see the list before copying. If a file sits in a folder the worktree doesn't have yet, create the folder first.
Step 2: bring node_modules over#
You have two honest choices.
Install again. npm ci (or pnpm install, yarn install --frozen-lockfile) in the worktree gives you a clean, correct install. With pnpm it is quick, because packages come from one store on disk.
Clone the install you already have (macOS). On APFS, cp -c makes a copy-on-write clone: it takes almost no time and almost no disk until a file changes.
cp -cR node_modules ../shop-fix/On our test repo this took 0.03 seconds. Only do this when the worktree's lockfile matches the main checkout's. If the branch changes dependencies, install again.
Don't symlink node_modules between worktrees. It looks like a shortcut, but an install in one worktree then changes the packages under another agent mid-run, and some tools resolve the symlink back to the main checkout and load the wrong copy of your own code.
Step 3: give each worktree its own port#
Most dev servers read a PORT variable. Pick one per worktree and keep it there:
echo "PORT=3001" >> ../shop-fix/.env.localDo the same for anything else two copies of your app can't share: a local database name, a cache directory, a test server. Two agents migrating one local database break each other as surely as two agents editing one file.
Put it in one script#
Once the steps work, make them one command so neither you nor an agent skips one. Save this as scripts/wt.sh:
#!/bin/sh
# usage: scripts/wt.sh <branch> <port>
set -e
branch="${1:?give a branch name}"
port="${2:?give a port}"
dir="../$(basename "$PWD")-$branch"
git worktree add "$dir" -b "$branch"
git ls-files -z --others --ignored --exclude-from=.worktreeinclude \
| xargs -0 -I{} cp -p {} "$dir/{}"
(cd "$dir" && npm ci --silent)
echo "PORT=$port" >> "$dir/.env.local"
echo "$dir"Then tell the agent in your CLAUDE.md or AGENTS.md: "Start new work with scripts/wt.sh <branch> <port> and work in the folder it prints."
Step 4: clean up#
When the branch has landed:
git worktree remove ../shop-fix
git branch -d fix/cartIgnored files such as .env and node_modules don't block the removal; uncommitted changes to tracked files do, which is the safety you want.
Two things trip people up:
- "cannot remove a locked working tree": Claude Code locks the worktrees it creates. An interactive session cleans up when you exit, but a
claude -p --worktreerun leaves its lock in place. Once that session has ended, rungit worktree unlock <path>, thengit worktree remove <path>. - Folders you deleted by hand:
git worktree pruneforgets worktrees whose folder is gone.
git worktree list shows everything that is left.
Where Joxo fits#
Worktrees keep files apart on one computer. When the agents belong to different people, Joxo gives them one task board and tells an agent when a teammate's agent is already working on the same task or files. Setup is one line pasted into each agent. For running several sessions on one machine, see run parallel Claude Code sessions.
Frequently asked questions#
Why is my .env missing in a git worktree?#
A worktree only contains tracked files, and .env is gitignored. Copy it in: Claude Code does it for you when the file is listed in .worktreeinclude, and for other worktrees the git ls-files --others --ignored command above copies the same list.
Can git worktrees share node_modules?#
Not safely by symlink. Install in each worktree, or on macOS clone the install with cp -cR node_modules <worktree>/, which is near-instant and uses almost no disk until files change.
How do I remove a locked git worktree?#
Make sure no session is still using it, then run git worktree unlock <path> and git worktree remove <path>. Claude Code locks the worktrees it creates, and non-interactive runs leave the lock behind.