GitWorktree.org logoGitWorktree.org

Git Worktree and node_modules

One of the most frequently asked questions about git worktree in JavaScript and TypeScript projects: do you need to run npm install in every worktree? The short answer is yes. Here is why, and what you can do about it.

Do You Need npm install in Each Worktree?

Yes. Each worktree has its own working directory, and node_modules is part of the working directory. When you run git worktree add, Git checks out the files tracked by Git — but node_modules is in .gitignore, so it is never checked out. You get an empty worktree with no dependencies installed.

node_modules is not shared
# Create a new worktree
git worktree add ../myapp-feature feature/auth

# Enter the worktree
cd ../myapp-feature

# Try to run the project — this will fail
npm start
# Error: Cannot find module 'react'

# You must install dependencies first
npm install   # or: yarn install / pnpm install

Why node_modules Is Not Shared

Worktrees share the Git object database (commits, blobs, trees) and refs (branches, tags). They do not share untracked or ignored files. Since node_modules is in .gitignore, it is invisible to Git and therefore per-worktree.

This is actually a good thing. Different branches may have different dependencies in their package.json and package-lock.json. Sharing node_modules across branches would cause version conflicts and subtle bugs.

The same applies to other ignored artifacts: build output (dist/, build/), virtual environments (.venv/), and compiled binaries. Each worktree needs its own copy.

Strategies for Managing node_modules

1. Use pnpm and its global store

pnpm uses a content-addressable store that deduplicates packages globally. Each worktree still gets its own node_modules, but packages are hard-linked from a shared store, so disk usage is dramatically lower and installs are faster.

pnpm global store
# Install pnpm if you haven't
npm install -g pnpm

# In each worktree, run:
pnpm install

# pnpm stores packages in ~/.local/share/pnpm/store
# Each worktree's node_modules uses hard links to that store
# 5 worktrees ≈ same disk usage as 1 with npm

2. Script it with npm ci

Use npm ci instead of npm install for faster, deterministic installs. It skips the dependency resolution step by using the lockfile directly.

Fast install with npm ci
# npm ci is faster than npm install for clean installs
# It deletes node_modules first and installs from package-lock.json
cd ../myapp-feature
npm ci

3. Symlinks (with caveats)

You can symlink node_modules from one worktree to another, but this is risky. It only works if both branches have identical dependencies. If they diverge, one worktree will have wrong versions, leading to hard-to-debug issues.

Symlinks (not recommended)
# Symlink node_modules (ONLY if branches have same dependencies)
cd ../myapp-feature
ln -s ../myapp/node_modules node_modules

# WARNING: If feature/auth changes package.json, this will break.
# The symlinked node_modules won't match the lockfile.
# Use pnpm instead for safe deduplication.

Handling .env Files

Like node_modules, .env files are typically in .gitignore and therefore not shared between worktrees. Each worktree needs its own .env file.

Copying .env files
# After creating a worktree, copy your .env file
cd ../myapp-feature
cp ../myapp/.env .env

# Or keep a .env.example in the repo (tracked by Git)
# and copy it in each worktree
cp .env.example .env

For a deeper dive into environment file management, see the .gitignore and .env files guide.

Shell Helper Script

Automate worktree creation with dependency installation by adding a helper function to your shell configuration.

Complete shell helper
# Add to ~/.bashrc or ~/.zshrc

# Create worktree + install deps + copy .env
gwt() {
  local dir="$1"
  shift
  git worktree add "$dir" "$@" || return 1
  cd "$dir" || return 1

  # Install dependencies based on lockfile type
  if [ -f "pnpm-lock.yaml" ]; then
    echo "Installing with pnpm..."
    pnpm install
  elif [ -f "yarn.lock" ]; then
    echo "Installing with yarn..."
    yarn install
  elif [ -f "package-lock.json" ]; then
    echo "Installing with npm ci..."
    npm ci
  fi

  # Copy .env if it exists in the parent worktree
  local main_dir
  main_dir="$(git worktree list --porcelain | head -1 | cut -d' ' -f2)"
  if [ -f "$main_dir/.env" ] && [ ! -f ".env" ]; then
    cp "$main_dir/.env" .env
    echo "Copied .env from main worktree"
  fi
}

# Usage:
# gwt ../myapp-feature feature/auth
# gwt ../myapp-hotfix -b hotfix/urgent

This function handles dependency installation for npm, yarn, and pnpm projects automatically, and copies your .env file from the main worktree.

Related Guides