| name | git-worktree |
| description | Git worktree management with tmux and iTerm2 integration. Use when creating isolated dev environments, managing parallel feature branches, switching contexts without stashing, or running multiple Claude instances. Covers worktree creation, tmux window management, iTerm2 tabs, and cleanup workflows. |
Git Worktree Skill
Manage parallel development environments using git worktrees with seamless terminal integration.
Overview
Git worktrees enable multiple working directories from a single repository:
- Isolated feature development without branch switching
- Run multiple Claude Code instances in parallel
- Context switching without stashing uncommitted changes
- Clean separation of experimental work
Quick Commands
wt - Worktree Manager
The wt command creates worktrees with automatic terminal integration:
wt add-new-feature
tmux Integration
Workflow: Create Worktree + tmux Window
wt() {
local name="$1"
local base_branch="${2:-main}"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
if [[ -z "$repo_root" ]]; then
echo "Error: Not in a git repository"
return 1
fi
mkdir -p "$repo_root/.worktrees"
if git worktree add -b "$name" "$worktree_path" "$base_branch" 2>/dev/null; then
echo "Created worktree: $worktree_path"
elif git worktree add "$worktree_path" "$name" 2>/dev/null; then
echo "Attached to existing branch: $name"
else
echo "Error: Failed to create worktree"
return 1
fi
if [[ -n "$TMUX" ]]; then
tmux new-window -n "$name" -c "$worktree_path"
echo "Created tmux window: $name"
else
cd "$worktree_path"
echo "Changed to: $worktree_path"
fi
}
wt-rm() {
local name="$1"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
git worktree remove "$worktree_path" --force 2>/dev/null
if [[ -n "$TMUX" ]]; then
tmux kill-window -t "$name" 2>/dev/null
fi
git branch -d "$name" 2>/dev/null
echo "Removed worktree: $name"
}
wt-ls() {
git worktree list
}
tmux Session Management
wt-session() {
local name="$1"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
wt "$name"
if tmux has-session -t "$name" 2>/dev/null; then
tmux attach-session -t "$name"
else
tmux new-session -d -s "$name" -c "$worktree_path"
tmux attach-session -t "$name"
fi
}
iTerm2 Integration
AppleScript for iTerm2 Tabs
wt-iterm() {
local name="$1"
local base_branch="${2:-main}"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
mkdir -p "$repo_root/.worktrees"
git worktree add -b "$name" "$worktree_path" "$base_branch" 2>/dev/null || \
git worktree add "$worktree_path" "$name" 2>/dev/null
osascript <<EOF
tell application "iTerm2"
tell current window
create tab with default profile
tell current session
write text "cd '$worktree_path' && clear"
end tell
end tell
end tell
EOF
echo "Created worktree with iTerm2 tab: $name"
}
wt-iterm-window() {
local name="$1"
local base_branch="${2:-main}"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
mkdir -p "$repo_root/.worktrees"
git worktree add -b "$name" "$worktree_path" "$base_branch" 2>/dev/null || \
git worktree add "$worktree_path" "$name" 2>/dev/null
osascript <<EOF
tell application "iTerm2"
create window with default profile
tell current session of current window
write text "cd '$worktree_path' && clear"
end tell
end tell
EOF
echo "Created worktree with iTerm2 window: $name"
}
iTerm2 Profile Integration
Create a dedicated iTerm2 profile for worktrees:
{
"Name": "Worktree",
"Badge Text": "WT: \\(session.name)",
"Working Directory": "$HOME/.worktrees",
"Custom Directory": "Yes"
}
Configuration
Environment Variables
export WORKTREE_TERMINAL="tmux"
export WORKTREE_AUTO_INSTALL=true
Worktree Location
Worktrees are stored inside the project directory:
~/Repos/github/my-project/
├── .worktrees/
│ ├── feature-auth/ # worktree for feature-auth branch
│ ├── bugfix-login/ # worktree for bugfix-login branch
│ └── add-new-skill/ # worktree for add-new-skill branch
├── src/
├── package.json
└── ...
Shell Configuration
Functions are defined in ~/.zsh/functions.zsh (already loaded by your zshrc):
alias wt='wt'
alias wtl='wt-ls'
alias wtr='wt-rm'
alias wts='wt-session'
_wt_completion() {
local branches=$(git branch --format='%(refname:short)' 2>/dev/null)
local worktrees=$(git worktree list --porcelain 2>/dev/null | grep '^worktree' | cut -d' ' -f2 | xargs -I{} basename {})
_alternative \
"branches:branch:($branches)" \
"worktrees:worktree:($worktrees)"
}
compdef _wt_completion wt wt-rm
Git Worktree Commands Reference
Creating Worktrees
git worktree add ../feature-x -b feature-x
git worktree add ../hotfix hotfix-branch
git worktree add ../upstream upstream/main
git worktree add ../review abc123
Managing Worktrees
git worktree list
git worktree list --porcelain
git worktree lock ../feature-x --reason "WIP"
git worktree unlock ../feature-x
git worktree remove ../feature-x
git worktree remove ../feature-x --force
git worktree prune
Advanced Operations
git worktree move ../old-path ../new-path
git worktree repair ../moved-worktree
Parallel Claude Development
Running Multiple Instances
wt feature-auth
wt feature-api
wt bugfix-login
Port Management
export WORKTREE_PORT_BASE=8100
export WORKTREE_PORTS_PER_TREE=2
get_worktree_ports() {
local index=$(git worktree list | grep -n "$PWD" | cut -d: -f1)
local base=$((WORKTREE_PORT_BASE + (index - 1) * WORKTREE_PORTS_PER_TREE))
echo "Dev server: $base, API: $((base + 1))"
}
Cleanup Workflows
Merge and Cleanup
wt-cleanup() {
local name="$1"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
cd "$repo_root"
git fetch origin
git pull origin main
git worktree remove "$worktree_path" --force
if git branch --merged | grep -q "$name"; then
git branch -d "$name"
echo "Branch $name was merged and deleted"
else
echo "Branch $name not yet merged, kept locally"
fi
[[ -n "$TMUX" ]] && tmux kill-window -t "$name" 2>/dev/null
}
Bulk Cleanup
wt-cleanup-merged() {
local main_dir=$(git worktree list | head -1 | awk '{print $1}')
git worktree list | tail -n +2 | while read -r line; do
local wt_path=$(echo "$line" | awk '{print $1}')
local wt_branch=$(echo "$line" | awk '{print $3}' | tr -d '[]')
if git branch --merged main | grep -q "$wt_branch"; then
echo "Removing merged worktree: $wt_branch"
git worktree remove "$wt_path" --force
git branch -d "$wt_branch"
fi
done
}
Troubleshooting
Common Issues
Worktree already exists:
git worktree list
git worktree prune
Branch already checked out:
git worktree remove /path/to/existing
tmux window naming conflicts:
tmux rename-window -t old-name new-name
tmux kill-window -t conflicting-name
iTerm2 AppleScript errors:
open -a iTerm
References
External Links
Gotchas
- Worktree paths inside the main repo's working tree confuse the outer
git status — files in the inner worktree show as untracked in the parent.
- A branch checked out in any worktree refuses
git branch -d — must remove the worktree first.
- Worktrees share
.git/config — per-worktree config needs explicit git config --worktree.
git worktree prune removes orphaned records but does NOT delete the directory — clean up the dir manually after pruning.
- Locking a worktree doesn't prevent fs operations —
git worktree lock only blocks git worktree remove; rm -rf still works.
- macOS case-insensitive fs + worktrees: two worktrees with case-only-different paths cause unpredictable git behavior.