Git worktrees let you check out multiple branches simultaneously in separate directories. Combined with the VS Code Git Worktrees extension and its .worktree.yml lifecycle hooks, you can automate the entire setup — including spawning tmux sessions for Claude Code — every time you create, switch, or remove a worktree.
Why Git Worktrees?
Traditional branching requires stashing or committing before switching. Worktrees eliminate this friction:
# Instead of this painful dance:
git stash
git checkout feature-b
# work on feature-b...
git checkout feature-a
git stash pop
# You get parallel directories:
~/projects/myapp/ # main branch
~/projects/myapp-feature-a/ # feature-a branch
~/projects/myapp-feature-b/ # feature-b branch
Each worktree is an independent working directory with its own branch. You can run different tasks, different servers, different Claude Code sessions — all at the same time.
The VS Code Git Worktrees Extension
The Git Worktrees extension wraps git worktree commands into an interactive VS Code UI. I’ve forked and updated it to add .worktree.yml lifecycle hooks support — you can install the .vsix directly from the fork. Open the command palette (Ctrl+Shift+P) and search for Git Worktree::
- Add — Create a new worktree from a remote branch
- List — View all worktrees and switch between them (opens a new VS Code window)
- Remove — Delete a worktree (with force-delete for dirty trees)
No more terminal juggling. But the real power comes from lifecycle hooks.
.worktree.yml — Lifecycle Hooks
Drop a .worktree.yml file in your repository root. The extension reads it and executes commands automatically during worktree operations.
Supported Events
| Event | Trigger | Features |
|---|---|---|
add | After creating a new worktree | copy files + run commands |
switch | When switching to a worktree | Run commands |
remove | Before deleting a worktree | Run commands |
Available Variables
Use these in any command string:
| Variable | Resolves To | Example |
|---|---|---|
${userHome} | User home directory | /home/phathdt |
${workspaceFolder} | Full worktree path | /home/phathdt/projects/myapp-feat-x |
${workspaceFolderBasename} | Worktree folder name | myapp-feat-x |
${repositoryName} | Origin repo name | myapp |
Full Example: .worktree.yml
# .worktree.yml - Lifecycle hooks for git worktree operations
add:
copy:
- .env
- .env.local
commands:
- yarn install
- yarn db:generate
- tmux has-session -t ${repositoryName} 2>/dev/null && tmux new-window -t ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder} || tmux new-session -d -s ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder}
switch:
commands:
- tmux has-session -t ${repositoryName} 2>/dev/null && tmux new-window -t ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder} || tmux new-session -d -s ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder}
remove:
commands:
- tmux kill-window -t ${repositoryName}:${workspaceFolderBasename} 2>/dev/null || true
Let’s break down what happens.
The Workflow in Action
1. Creating a Worktree (add)
When you run Git Worktree: Add from the command palette:
- Extension creates the worktree via
git worktree add - Copies
.envand.env.localfrom the main repo to the new worktree — no manual setup - Runs
yarn installinside the new worktree - Runs
yarn db:generatefor database schemas - Creates or attaches a tmux session named after your repo, with a window named after the worktree folder
The tmux command is the key piece:
tmux has-session -t ${repositoryName} 2>/dev/null \
&& tmux new-window -t ${repositoryName} \
-n ${workspaceFolderBasename} \
-c ${workspaceFolder} \
|| tmux new-session -d \
-s ${repositoryName} \
-n ${workspaceFolderBasename} \
-c ${workspaceFolder}
This checks if a tmux session named myapp exists. If yes, it adds a new window. If not, it creates a new session. Either way, the window’s working directory is the worktree path and the window is named after the branch.
2. Switching Worktrees (switch)
When you switch via Git Worktree: List, the same tmux command runs — ensuring you always have a terminal window ready at the right directory for the worktree you’re switching to.
3. Removing a Worktree (remove)
Before deletion, the corresponding tmux window is killed:
tmux kill-window -t ${repositoryName}:${workspaceFolderBasename} 2>/dev/null || true
The || true ensures the removal proceeds even if the tmux window was already closed.
Claude Code + Worktrees = Parallel AI Development
Here’s where it gets powerful. Each tmux window is an isolated terminal session. You can run Claude Code (claude) in each one independently:
tmux session: myapp
├── window: main → claude (working on docs)
├── window: feat-auth → claude (implementing auth)
└── window: fix-bug-123 → claude (debugging issue)
Each Claude Code instance has its own context, its own working directory, its own branch. No conflicts. No context switching. You review one window while Claude works in another.
Typical Parallel Workflow
- Open VS Code on your main branch
Ctrl+Shift+P→Git Worktree: Add→ selectorigin/feature-auth→ extension creates worktree, installs deps, copies.env, opens tmux window- Repeat for
origin/fix-bug-123 - Attach tmux:
tmux attach -t myapp - In each window: run
claudeto start a Claude Code session - Switch between tmux windows with
Ctrl+Bthen window number - When done,
Git Worktree: Removecleans up the worktree and kills the tmux window
Project-Specific Customization
Adapt the add.commands to your stack:
# Node.js / pnpm project
add:
copy:
- .env
- .env.local
commands:
- pnpm install
- pnpm db:push
- tmux has-session -t ${repositoryName} 2>/dev/null && tmux new-window -t ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder} || tmux new-session -d -s ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder}
# Go project
add:
copy:
- .env
commands:
- go mod download
- tmux has-session -t ${repositoryName} 2>/dev/null && tmux new-window -t ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder} || tmux new-session -d -s ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder}
# Python project
add:
copy:
- .env
commands:
- python -m venv .venv
- .venv/bin/pip install -r requirements.txt
- tmux has-session -t ${repositoryName} 2>/dev/null && tmux new-window -t ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder} || tmux new-session -d -s ${repositoryName} -n ${workspaceFolderBasename} -c ${workspaceFolder}
Extension Settings Worth Knowing
Configure these in VS Code settings (Ctrl+,):
| Setting | Default | What it does |
|---|---|---|
vsCodeGitWorktrees.move.openNewVscodeWindow | true | Opens a new VS Code window when creating/switching worktrees |
vsCodeGitWorktrees.worktrees.dir.path | null | Custom directory for all worktrees (supports variables) |
vsCodeGitWorktrees.add.autoPush | true | Auto-push new worktree branch to remote |
vsCodeGitWorktrees.add.autoPull | true | Auto-pull after creating worktree branch |
vsCodeGitWorktrees.worktree.coloring | false | Color labels for worktree windows |
A useful pattern for worktrees.dir.path:
${userHome}/worktrees/${repositoryName}
This keeps all worktrees organized under a single directory instead of scattering them as siblings of your main repo.
Key Details
- Commands run sequentially in the worktree directory, not the source repo
- Failed commands show a warning but don’t block the operation — your worktree still gets created/removed
- Missing copy files are skipped with a warning, not an error
- Git >= 2.34.1 is required
.worktree.ymlgoes in your repo root and can be committed to share with your team
Summary
The combination of git worktrees, VS Code Git Worktrees extension, .worktree.yml lifecycle hooks, and tmux creates an automated parallel development environment. Each worktree gets its own VS Code window, its own terminal, its own dependencies, and its own Claude Code session — all set up with a single command palette action.
No manual cd. No manual npm install. No manual tmux session management. Just branch, work, and clean up.