Git Worktree with tmux
Worktrees give you parallel branches; tmux gives you parallel terminals. Together they are the closest thing to a free productivity upgrade for terminal-first developers. This guide wires them up cleanly — one tmux window per git worktree, consistent naming, surviving disconnects, and a pattern that plays well with Claude Code.
The Pipeline at a Glance
- Pick a project. Open one tmux session per project (
tmux new -s <project>). - For each branch you want to work on, create a worktree and an attached tmux window.
- Each window splits into two panes: editor on top, terminal on bottom. Or AI agent on top, shell on bottom.
- Detach with
Ctrl-b dwhen you switch context. Reattach later withtmux a -t <project>. - Cleanup means closing the window and git worktree remove in lockstep.
Step 1: One tmux Session Per Project
A common mistake is mixing projects inside one tmux session. Don't. The session is your project boundary. Pick a name that matches your repo:
# Start a session named after the project
cd ~/projects/acme-api
tmux new -s acme-api
# Or, if you already have a session for it
tmux a -t acme-apiWhen you see tmux ls later, the first column is your project map. That alone makes context-switching feel calmer.
Step 2: New Worktree, New tmux Window
The pair-up is what creates the magic. Don't open a worktree without spawning a window for it, and don't open a window for the same worktree twice. The pattern:
# In your main tmux session, create a new window
# named after the branch you're about to create
WT=fix-auth-redirect
git worktree add ../wt/${WT} -b ${WT}
tmux new-window -n ${WT} -c "$PWD/../wt/${WT}"
# Verify
git worktree list
tmux list-windowsThe -c flag tells tmux to start the window in the worktree's directory, not your current one. Skip it and you'll find yourself editing files in the wrong checkout. Common mistake.
Step 3: Standard Pane Layout
Once you are in a worktree's window, split it into two panes. The layout that emerged from heavy use:
+--------------------------------------+ | | | Top pane: editor (nvim) OR agent | | (claude, codex) | | | +--------------------------------------+ | | | Bottom pane: shell — runs tests, | | git commands, server | | | +--------------------------------------+
Bind it to a key in your ~/.tmux.conf so every new window opens this way:
# In ~/.tmux.conf — split new windows into 2 panes
bind C-w split-window -v -p 30 -c "#{pane_current_path}"
# Then in the worktree window, just hit Ctrl-b Ctrl-wStep 4: Running an AI Agent Per Worktree
This is the killer pattern. You can leave a long-running Claude Code or Codex session in each window, detach, and come back later. The agents do not interfere with each other because their worktrees do not share a working directory.
# In window "fix-auth-redirect"
claude --worktree=../wt/fix-auth-redirect
# Detach the whole tmux session: Ctrl-b d
# Walk away. Get coffee. Take a meeting.
# Reattach later
tmux a -t acme-api
# The agent is still running in its windowFor the full pattern with multiple parallel agents, see running parallel AI agents with git worktree.
Step 5: The Cleanup Two-Step
Don't leave dangling worktrees or dangling tmux windows. They both accumulate. Pair the cleanup:
# Inside the worktree's tmux window:
git push # or whatever finishes the work
cd .. # leave the worktree dir
git worktree remove ../wt/fix-auth-redirect
# Close the tmux window
exit
# Or: Ctrl-b & (kill window)A weekly review: run git worktree list and tmux list-windows side-by-side. They should match. If they don't, you have drift. Fix it with git worktree prune on the Git side and tmux kill-window -t <name> on the tmux side.
Two Recipes Worth Keeping
Recipe A: The wt-new function
Add this to your ~/.zshrc or ~/.bashrc:
wt-new() {
local slug=$1
[[ -z "$slug" ]] && { echo "usage: wt-new <slug>"; return 1; }
local path="../wt/$slug"
git worktree add "$path" -b "$slug" || return $?
tmux new-window -n "$slug" -c "$(cd "$path" && pwd)"
}Now wt-new fix-auth-redirect creates the worktree AND opens its tmux window.
Recipe B: The wt-rm cleanup function
wt-rm() {
local slug=$1
[[ -z "$slug" ]] && { echo "usage: wt-rm <slug>"; return 1; }
git worktree remove "../wt/$slug" || return $?
tmux kill-window -t "$slug" 2>/dev/null
}Common Pitfalls
- Different shell in different panes. If your top pane is
nvimand bottom iszsh, don't expect aliases or env vars to match. Set them in your shell init, not interactively. - Forgetting
-con new-window. New tmux windows default to your shell's startup directory, not the worktree. Always pass-c. - Killing the wrong window. Use named windows (the
-nflag) sotmux list-windowsis human-readable. - Trying to share a branch.Each branch can be checked out in only one worktree at a time. If you try a second checkout you'll get the “branch already checked out” error.
FAQ
Why tmux over screen or kitty tabs?
The killer feature is the detach/reattach cycle: a Claude session you started Monday morning is still running Friday afternoon. Screen does this too — pick whichever you already know. Kitty tabs don't survive a terminal restart.
Does this work on Windows?
Use WSL. Native Windows tmux isn't there yet (May 2026). Inside WSL2, the setup is identical to macOS/Linux.
What if I prefer separate terminal windows?
The window-per-worktree idea still applies — replace “tmux window” with “iTerm tab” or “Warp split” and the rest of this guide carries over.
How does this compare with a managed setup like a Codex cloud sandbox?
Cloud sandboxes (see Codex + Docker) take the worktree-per-agent pattern and isolate further with containers. Local tmux + worktree is faster and cheaper; cloud sandboxes win when you need true isolation from your host filesystem.
Can I open a worktree as a full VS Code window from inside tmux?
Yes — run code . from inside the worktree directory. The tmux window stays available for the shell; VS Code opens externally. See VS Code with git worktree for the integration details.