Git Worktree with .gitignore and .env Files
Understanding which files are shared between git worktrees and which are not is essential for a smooth workflow. This guide covers how .gitignore rules propagate across worktrees, why .env files need special attention, and strategies for managing both.
How .gitignore Works with Worktrees
The .gitignore file is a tracked file in your repository. Because it is tracked, it is automatically present in every worktree when the branch is checked out. All worktrees on the same branch share the same .gitignore rules.
# .gitignore is tracked — it appears in every worktree automatically
$ git worktree add ../myapp-feature feature/auth
$ cat ../myapp-feature/.gitignore
node_modules/
dist/
.env
.env.local
*.logIf different branches have different .gitignorefiles, each worktree will use the version from its checked-out branch. This is standard Git behavior — the ignore rules are part of the repository content, not worktree-specific configuration.
Nested .gitignore files in subdirectories also work exactly as expected. They are tracked files and follow their branch.
.env Files Are NOT Shared
Because .env files are listed in .gitignore, they are untracked. Git does not know about them, and they exist only in the working directory where they were created. When you add a new worktree, there is no .env file in it.
# Main worktree has .env
~/projects/myapp/
├── .env # exists (you created it manually)
├── .env.local # exists
├── .gitignore # tracked — lists .env
└── src/
# New worktree — NO .env files
~/projects/myapp-feature/
├── .gitignore # tracked — present automatically
├── src/
└── (no .env file) # you must create or copy itThis applies to all untracked and ignored files: .env, .env.local, .env.development, node_modules/, dist/, IDE configuration like .idea/, and any other files matching your .gitignore patterns.
Strategies for Managing .env
1. Keep a .env.example in the repo
Track a template file with placeholder values. In each new worktree, copy it and fill in the real values.
# .env.example (tracked in Git)
DATABASE_URL=postgres://user:password@localhost:5432/myapp
API_KEY=your-api-key-here
REDIS_URL=redis://localhost:6379
# In each new worktree:
cp .env.example .env
# Then edit .env with real values2. Copy from the main worktree
# After creating a worktree, copy .env from the main checkout
git worktree add ../myapp-feature feature/auth
cp .env ../myapp-feature/.env
cp .env.local ../myapp-feature/.env.local3. Use a secrets manager
For team environments, tools like dotenv-vault, 1Password CLI, or doppler can pull secrets from a central store. This eliminates the need to copy .env files entirely.
4. Symlink .env files
# Symlink to a single .env outside the worktrees
ln -s ~/secrets/myapp.env .env
# Or symlink to the main worktree's .env
cd ../myapp-feature
ln -s ../myapp/.env .env
# Caveat: changes in one worktree affect all symlinked copiesUsing .git/info/exclude for Per-Worktree Ignores
The .gitignore file applies to all worktrees because it is tracked. But sometimes you want to ignore a file in only one worktree without changing the shared .gitignore. That is what .git/info/exclude is for.
In a linked worktree, .gitis a file (not a directory) that points to the actual Git directory. The exclude file for a linked worktree lives inside the main repository’s .git/worktrees/<name>/info/exclude path.
# Find the actual Git directory for a worktree
$ cat ../myapp-feature/.git
gitdir: /home/dev/projects/myapp/.git/worktrees/myapp-feature
# Edit the exclude file for this worktree only
echo "my-local-test-data/" >> /home/dev/projects/myapp/.git/worktrees/myapp-feature/info/exclude
# Or use git rev-parse to find it programmatically
cd ../myapp-feature
GITDIR=$(git rev-parse --git-dir)
echo "my-local-test-data/" >> "$GITDIR/info/exclude"Rules in info/exclude work the same as .gitignore rules but are not tracked and not shared with other worktrees or other developers.
Should You .gitignore the Worktree Directory?
If you create worktrees inside the main repository directory (which is generally not recommended), those directories will show up as untracked content. You might be tempted to add them to .gitignore.
The better solution: create worktrees as sibling directories, not inside the main repo. This avoids the issue entirely.
# BAD: worktree inside the main repo (causes .gitignore headaches)
~/projects/myapp/
├── .git/
├── src/
├── worktrees/ # this would show as untracked
│ └── feature-auth/
└── .gitignore # you'd need to add "worktrees/"
# GOOD: worktrees as siblings (no .gitignore changes needed)
~/projects/
├── myapp/ # main worktree
├── myapp-feature-auth/ # linked worktree (sibling)
└── myapp-hotfix/ # linked worktree (sibling)If you must create worktrees inside the main repo (for example, due to tooling constraints), add the directory to .gitignore or to .git/info/exclude if you do not want to affect other developers.