
Git Worktree
- 99 installs
- 6 repo stars
- Updated July 22, 2026
- julianobarbosa/claude-code-skills
Master advanced Git workflows for history management, debugging, and collaboration.
About
Git worktree management with tmux and iTerm2 integration. Use when creating isolated dev environments, managing parallel feature branches, switching contexts without stashing.
- Isolated feature development without branch switching
- Run multiple Claude Code instances in parallel
Git Worktree by the numbers
- 99 all-time installs (skills.sh)
- +2 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #227 of 733 Git & Pull Requests skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianobarbosa/claude-code-skills --skill git-worktreeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 99 |
|---|---|
| repo stars | ★ 6 |
| Last updated | July 22, 2026 |
| Repository | julianobarbosa/claude-code-skills ↗ |
What it does
Master advanced Git workflows for history management, debugging, and collaboration.
Files
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:
# Create worktree with tmux window
wt add-new-feature
# This will:
# 1. Create git worktree named 'add-new-feature'
# 2. Create new tmux window in current session
# 3. Rename window to 'add-new-feature'
# 4. Change directory to the worktreetmux Integration
Workflow: Create Worktree + tmux Window
# Function for ~/.zshrc or ~/.bashrc
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"
# Validate we're in a git repo
if [[ -z "$repo_root" ]]; then
echo "Error: Not in a git repository"
return 1
fi
# Create worktree directory
mkdir -p "$repo_root/.worktrees"
# Create worktree with new branch
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
# tmux integration
if [[ -n "$TMUX" ]]; then
# Create new window with worktree name
tmux new-window -n "$name" -c "$worktree_path"
echo "Created tmux window: $name"
else
# Not in tmux, just cd
cd "$worktree_path"
echo "Changed to: $worktree_path"
fi
}
# Remove worktree and tmux window
wt-rm() {
local name="$1"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
# Remove git worktree
git worktree remove "$worktree_path" --force 2>/dev/null
# Close tmux window if exists
if [[ -n "$TMUX" ]]; then
tmux kill-window -t "$name" 2>/dev/null
fi
# Optionally delete branch
git branch -d "$name" 2>/dev/null
echo "Removed worktree: $name"
}
# List all worktrees
wt-ls() {
git worktree list
}tmux Session Management
# Create dedicated tmux session per project
wt-session() {
local name="$1"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
# Create worktree first
wt "$name"
# Create new tmux session (or attach if exists)
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
# Function for ~/.zshrc
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"
# Create worktree
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
# Open in new iTerm2 tab
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"
}
# Open worktree in new iTerm2 window
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"
# Create worktree
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
# Open in new iTerm2 window
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
# Add to ~/.zshrc or ~/.bashrc
# Preferred terminal for worktree operations (auto-detected if not set)
export WORKTREE_TERMINAL="tmux" # or "iterm2" or "auto"
# Auto-install dependencies after creating worktree
export WORKTREE_AUTO_INSTALL=trueWorktree 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):
# Functions location: ~/.zsh/functions.zsh
# Aliases
alias wt='wt'
alias wtl='wt-ls'
alias wtr='wt-rm'
alias wts='wt-session'
# Completion for wt commands
_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-rmGit Worktree Commands Reference
Creating Worktrees
# Create worktree with new branch from current HEAD
git worktree add ../feature-x -b feature-x
# Create worktree from specific branch
git worktree add ../hotfix hotfix-branch
# Create worktree from remote branch
git worktree add ../upstream upstream/main
# Create worktree at specific commit
git worktree add ../review abc123Managing Worktrees
# List all worktrees
git worktree list
# Show worktree details (porcelain format)
git worktree list --porcelain
# Lock worktree (prevent pruning)
git worktree lock ../feature-x --reason "WIP"
# Unlock worktree
git worktree unlock ../feature-x
# Remove worktree
git worktree remove ../feature-x
# Force remove (discards changes)
git worktree remove ../feature-x --force
# Prune stale worktrees
git worktree pruneAdvanced Operations
# Move worktree to new location
git worktree move ../old-path ../new-path
# Repair worktree after manual move
git worktree repair ../moved-worktreeParallel Claude Development
Running Multiple Instances
# Create worktrees for parallel development
wt feature-auth
wt feature-api
wt bugfix-login
# Each worktree gets its own:
# - tmux window / iTerm2 tab
# - Git index and working directory
# - Port allocation (if configured)
# - Claude Code instancePort Management
# Configure port pools per worktree
export WORKTREE_PORT_BASE=8100
export WORKTREE_PORTS_PER_TREE=2
# Calculate ports for worktree
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
# After PR merge, clean up worktree
wt-cleanup() {
local name="$1"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
# Switch to main worktree
cd "$repo_root"
# Update main
git fetch origin
git pull origin main
# Remove worktree
git worktree remove "$worktree_path" --force
# Delete branch if merged
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
# Close tmux window
[[ -n "$TMUX" ]] && tmux kill-window -t "$name" 2>/dev/null
}Bulk Cleanup
# Remove all worktrees with merged branches
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:
# List existing worktrees
git worktree list
# Remove stale entry
git worktree pruneBranch already checked out:
# Error: 'branch' is already checked out at '/path'
# Solution: Use a different branch name or remove existing worktree
git worktree remove /path/to/existingtmux window naming conflicts:
# Rename existing window first
tmux rename-window -t old-name new-name
# Or kill conflicting window
tmux kill-window -t conflicting-nameiTerm2 AppleScript errors:
# Ensure iTerm2 is running
open -a iTerm
# Grant automation permissions
# System Preferences > Security & Privacy > Privacy > AutomationReferences
- references/tmux-config.md - tmux configuration for worktrees
- references/iterm2-config.md - iTerm2 profile setup
- Shell functions:
~/.zsh/functions.zsh
External Links
- Git Worktree Documentation: https://git-scm.com/docs/git-worktree
- tmux Manual: https://man.openbsd.org/tmux
- iTerm2 Documentation: https://iterm2.com/documentation.html
---
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 lockonly blocksgit worktree remove;rm -rfstill works. - macOS case-insensitive fs + worktrees: two worktrees with case-only-different paths cause unpredictable git behavior.
#!/usr/bin/env bash
# Git Worktree Shell Functions
# Source this file in ~/.zshrc or ~/.bashrc
# Usage: source ~/.config/git-worktree/functions.sh
# Configuration
export WORKTREE_TERMINAL="${WORKTREE_TERMINAL:-auto}" # auto, tmux, iterm2, basic
# Detect terminal environment
_wt_detect_terminal() {
if [[ "$WORKTREE_TERMINAL" != "auto" ]]; then
echo "$WORKTREE_TERMINAL"
elif [[ -n "$TMUX" ]]; then
echo "tmux"
elif [[ "$TERM_PROGRAM" == "iTerm.app" ]]; then
echo "iterm2"
else
echo "basic"
fi
}
# Get repository root and name
_wt_repo_info() {
local root
root=$(git rev-parse --show-toplevel 2>/dev/null) || return 1
echo "$root"
}
_wt_repo_name() {
local root
root=$(_wt_repo_info) || return 1
basename "$root"
}
# Main worktree creation function
wt() {
local name="$1"
local base_branch="${2:-main}"
if [[ -z "$name" ]]; then
echo "Usage: wt <worktree-name> [base-branch]"
echo " wt feature-auth main"
return 1
fi
local repo_root
repo_root=$(_wt_repo_info) || {
echo "Error: Not in a git repository"
return 1
}
local worktree_path="$repo_root/.worktrees/$name"
# Create worktree directory
mkdir -p "$repo_root/.worktrees"
# Create worktree with new branch
if git worktree add -b "$name" "$worktree_path" "$base_branch" 2>/dev/null; then
echo "Created worktree with new branch: $name"
elif git worktree add "$worktree_path" "$name" 2>/dev/null; then
echo "Attached to existing branch: $name"
else
echo "Error: Failed to create worktree '$name'"
echo "Hint: Check if branch exists or worktree path is in use"
return 1
fi
# Terminal-specific handling
case "$(_wt_detect_terminal)" in
tmux)
_wt_tmux_window "$name" "$worktree_path"
;;
iterm2)
_wt_iterm2_tab "$name" "$worktree_path"
;;
basic)
cd "$worktree_path" || return 1
echo "Changed to: $worktree_path"
;;
esac
}
# tmux window creation
_wt_tmux_window() {
local name="$1"
local path="$2"
# Create new window with worktree name
tmux new-window -n "$name" -c "$path"
echo "Created tmux window: $name"
}
# iTerm2 tab creation
_wt_iterm2_tab() {
local name="$1"
local path="$2"
osascript - "$path" "$name" <<'APPLESCRIPT'
on run argv
set worktreePath to item 1 of argv
set worktreeName to item 2 of argv
tell application "iTerm2"
tell current window
create tab with default profile
tell current session
set name to worktreeName
write text "cd '" & worktreePath & "' && clear"
end tell
end tell
end tell
end run
APPLESCRIPT
echo "Created iTerm2 tab: $name"
}
# Remove worktree
wt-rm() {
local name="$1"
if [[ -z "$name" ]]; then
echo "Usage: wt-rm <worktree-name>"
return 1
fi
local repo_root
repo_root=$(_wt_repo_info) || {
echo "Error: Not in a git repository"
return 1
}
local worktree_path="$repo_root/.worktrees/$name"
# Check if worktree exists
if [[ ! -d "$worktree_path" ]]; then
echo "Error: Worktree '$name' not found at $worktree_path"
return 1
fi
# Remove git worktree
git worktree remove "$worktree_path" --force 2>/dev/null || {
echo "Warning: Failed to remove worktree, forcing prune"
rm -rf "$worktree_path"
git worktree prune
}
# Terminal-specific cleanup
case "$(_wt_detect_terminal)" in
tmux)
tmux kill-window -t "$name" 2>/dev/null
echo "Closed tmux window: $name"
;;
iterm2)
_wt_iterm2_close_tab "$name"
echo "Closed iTerm2 tab: $name"
;;
esac
# Optionally delete branch
git branch -d "$name" 2>/dev/null && echo "Deleted branch: $name"
echo "Removed worktree: $name"
}
# Close iTerm2 tab by name
_wt_iterm2_close_tab() {
local name="$1"
osascript - "$name" <<'APPLESCRIPT'
on run argv
set tabName to item 1 of argv
tell application "iTerm2"
tell current window
repeat with t in tabs
tell t
repeat with s in sessions
if name of s is tabName then
close s
return
end if
end repeat
end tell
end repeat
end tell
end tell
end run
APPLESCRIPT
}
# List worktrees
wt-ls() {
local repo_root
repo_root=$(_wt_repo_info) || {
echo "Error: Not in a git repository"
return 1
}
echo "Git Worktrees:"
echo "=============="
git worktree list
echo ""
echo "Worktree directory: $repo_root/.worktrees"
}
# Switch to worktree
wt-cd() {
local name="$1"
if [[ -z "$name" ]]; then
echo "Usage: wt-cd <worktree-name>"
return 1
fi
local repo_root
repo_root=$(_wt_repo_info) || {
echo "Error: Not in a git repository"
return 1
}
local worktree_path="$repo_root/.worktrees/$name"
if [[ -d "$worktree_path" ]]; then
cd "$worktree_path" || return 1
echo "Changed to: $worktree_path"
else
echo "Error: Worktree '$name' not found"
return 1
fi
}
# Create tmux session for worktree
wt-session() {
local name="$1"
local base_branch="${2:-main}"
if [[ -z "$name" ]]; then
echo "Usage: wt-session <session-name> [base-branch]"
return 1
fi
# Create worktree first (without terminal integration)
local original_terminal="$WORKTREE_TERMINAL"
export WORKTREE_TERMINAL="basic"
wt "$name" "$base_branch"
export WORKTREE_TERMINAL="$original_terminal"
local repo_root
repo_root=$(_wt_repo_info)
local worktree_path="$repo_root/.worktrees/$name"
# Create or attach to tmux session
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
}
# Cleanup merged worktrees
wt-cleanup() {
local name="$1"
if [[ -z "$name" ]]; then
echo "Usage: wt-cleanup <worktree-name>"
return 1
fi
local repo_root
repo_root=$(_wt_repo_info) || {
echo "Error: Not in a git repository"
return 1
}
# Switch to main worktree
cd "$repo_root" || return 1
# Update main
git fetch origin
git pull origin main 2>/dev/null || git pull origin master 2>/dev/null
# Remove worktree
wt-rm "$name"
}
# Cleanup all merged worktrees
wt-cleanup-merged() {
local main_dir
main_dir=$(git worktree list | head -1 | awk '{print $1}')
echo "Scanning for merged worktrees..."
git worktree list | tail -n +2 | while read -r line; do
local wt_path wt_branch
wt_path=$(echo "$line" | awk '{print $1}')
wt_branch=$(echo "$line" | awk '{print $3}' | tr -d '[]')
if git branch --merged main 2>/dev/null | grep -q "$wt_branch"; then
echo "Removing merged worktree: $wt_branch"
git worktree remove "$wt_path" --force 2>/dev/null
git branch -d "$wt_branch" 2>/dev/null
fi
done
echo "Cleanup complete"
}
# Prune stale worktrees
wt-prune() {
echo "Pruning stale worktrees..."
git worktree prune -v
echo "Done"
}
# Status of all worktrees
wt-status() {
echo "Worktree Status:"
echo "================"
git worktree list | while read -r line; do
local wt_path wt_branch
wt_path=$(echo "$line" | awk '{print $1}')
wt_branch=$(echo "$line" | awk '{print $3}' | tr -d '[]')
echo ""
echo "📁 $wt_branch ($wt_path)"
(
cd "$wt_path" 2>/dev/null || exit
local status
status=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
if [[ "$status" -gt 0 ]]; then
echo " ⚠️ $status uncommitted changes"
else
echo " ✅ Clean"
fi
local ahead behind
ahead=$(git rev-list --count HEAD@{upstream}..HEAD 2>/dev/null || echo "0")
behind=$(git rev-list --count HEAD..HEAD@{upstream} 2>/dev/null || echo "0")
if [[ "$ahead" -gt 0 ]] || [[ "$behind" -gt 0 ]]; then
echo " 📊 ↑$ahead ↓$behind"
fi
)
done
}
# Shell completions for zsh
if [[ -n "$ZSH_VERSION" ]]; then
_wt_completion() {
local branches worktrees
branches=$(git branch --format='%(refname:short)' 2>/dev/null)
worktrees=$(git worktree list --porcelain 2>/dev/null | grep '^worktree' | cut -d' ' -f2 | xargs -I{} basename {} 2>/dev/null)
_alternative \
"branches:branch:($branches)" \
"worktrees:worktree:($worktrees)"
}
compdef _wt_completion wt wt-rm wt-cd wt-cleanup
fi
# Shell completions for bash
if [[ -n "$BASH_VERSION" ]]; then
_wt_completion_bash() {
local cur="${COMP_WORDS[COMP_CWORD]}"
local branches worktrees
branches=$(git branch --format='%(refname:short)' 2>/dev/null)
worktrees=$(git worktree list --porcelain 2>/dev/null | grep '^worktree' | cut -d' ' -f2 | xargs -I{} basename {} 2>/dev/null)
COMPREPLY=($(compgen -W "$branches $worktrees" -- "$cur"))
}
complete -F _wt_completion_bash wt wt-rm wt-cd wt-cleanup
fi
# Aliases
alias wtl='wt-ls'
alias wtr='wt-rm'
alias wtc='wt-cd'
alias wts='wt-status'
alias wtp='wt-prune'
echo "Git worktree functions loaded. Type 'wt --help' or 'wt-ls' to get started."
iTerm2 Configuration for Git Worktrees
iTerm2 setup for seamless worktree management with tabs, profiles, and automation.
Profile Setup
Create Worktree Profile
1. Open iTerm2 Preferences (Cmd + ,) 2. Go to Profiles > + (Add new profile) 3. Configure:
Name: Worktree
Badge: WT: \(session.name)
Working Directory: Advanced Configuration
- Working Directory for New Tabs: Reuse previous session's directory
Title: \(session.name) - \(path)Profile JSON Export
Save to ~/.config/iterm2/worktree-profile.json:
{
"Name": "Worktree",
"Guid": "worktree-profile-guid",
"Badge Text": "WT: \\(session.name)",
"Custom Directory": "Recycle",
"Working Directory": ".worktrees",
"Title Components": 2,
"Custom Window Title": "Worktree",
"Use Custom Window Title": true,
"Terminal Type": "xterm-256color",
"Scrollback Lines": 10000,
"Unlimited Scrollback": false,
"Close Sessions On End": true,
"Jobs to Ignore": ["rlogin", "ssh", "slogin", "telnet"],
"Triggers": [
{
"partial": true,
"regex": "^\\[WORKTREE\\]",
"action": "HighlightTextTrigger",
"parameter": {
"textColor": "#00FF00"
}
}
]
}AppleScript Functions
Create Tab for Worktree
Save to ~/.config/iterm2/scripts/new-worktree-tab.applescript:
on run argv
set worktreePath to item 1 of argv
set worktreeName to item 2 of argv
tell application "iTerm2"
tell current window
create tab with profile "Worktree"
tell current session
set name to worktreeName
write text "cd '" & worktreePath & "' && clear && echo '[WORKTREE] " & worktreeName & " ready'"
end tell
end tell
end tell
end runCreate Window for Worktree
Save to ~/.config/iterm2/scripts/new-worktree-window.applescript:
on run argv
set worktreePath to item 1 of argv
set worktreeName to item 2 of argv
tell application "iTerm2"
create window with profile "Worktree"
tell current session of current window
set name to worktreeName
write text "cd '" & worktreePath & "' && clear && echo '[WORKTREE] " & worktreeName & " ready'"
end tell
end tell
end runSplit Pane for Worktree
on run argv
set worktreePath to item 1 of argv
set direction to item 2 of argv -- "vertical" or "horizontal"
tell application "iTerm2"
tell current session of current window
if direction is "vertical" then
set newSession to split vertically with profile "Worktree"
else
set newSession to split horizontally with profile "Worktree"
end if
tell newSession
write text "cd '" & worktreePath & "'"
end tell
end tell
end tell
end runShell Functions
Add to ~/.zshrc:
# iTerm2 worktree functions
if [[ "$TERM_PROGRAM" == "iTerm.app" ]]; then
# Create worktree with iTerm2 tab
wt() {
local name="$1"
local base_branch="${2:-main}"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
if [[ -z "$repo_root" ]]; then
echo "Error: Not in a git repository"
return 1
fi
local worktree_path="$repo_root/.worktrees/$name"
# Create worktree
mkdir -p "$repo_root/.worktrees"
if ! git worktree add -b "$name" "$worktree_path" "$base_branch" 2>/dev/null; then
if ! git worktree add "$worktree_path" "$name" 2>/dev/null; then
echo "Error: Failed to create worktree"
return 1
fi
fi
# Create iTerm2 tab
osascript - "$worktree_path" "$name" <<'EOF'
on run argv
set worktreePath to item 1 of argv
set worktreeName to item 2 of argv
tell application "iTerm2"
tell current window
create tab with default profile
tell current session
set name to worktreeName
write text "cd '" & worktreePath & "' && clear"
end tell
end tell
end tell
end run
EOF
echo "Created worktree: $name"
}
# Remove worktree and close tab
wt-rm() {
local name="$1"
local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
local worktree_path="$repo_root/.worktrees/$name"
# Remove git worktree
git worktree remove "$worktree_path" --force 2>/dev/null
# Close iTerm2 tab with matching name
osascript - "$name" <<'EOF'
on run argv
set tabName to item 1 of argv
tell application "iTerm2"
tell current window
repeat with t in tabs
tell t
repeat with s in sessions
if name of s is tabName then
close s
return
end if
end repeat
end tell
end repeat
end tell
end tell
end run
EOF
git branch -d "$name" 2>/dev/null
echo "Removed worktree: $name"
}
fiKeyboard Shortcuts
Configure in iTerm2 Preferences > Keys > Key Bindings:
| Shortcut | Action | Command |
|---|---|---|
Cmd+Shift+W | New Worktree Tab | Run AppleScript: new-worktree-tab.applescript |
Cmd+Shift+N | New Worktree Window | Run AppleScript: new-worktree-window.applescript |
Cmd+Shift+L | List Worktrees | Send text: git worktree list\n |
Tab Colors
Distinguish worktree tabs by color:
# Set iTerm2 tab color based on worktree
iterm2_set_tab_color() {
local r=$1 g=$2 b=$3
printf "\033]6;1;bg;red;brightness;%d\a" "$r"
printf "\033]6;1;bg;green;brightness;%d" "$g"
printf "\033]6;1;bg;blue;brightness;%d\a" "$b"
}
# Color coding for worktree types
wt-color() {
local name=$(basename "$PWD")
case "$name" in
feature-*) iterm2_set_tab_color 50 150 50 ;; # Green for features
bugfix-*) iterm2_set_tab_color 200 100 50 ;; # Orange for bugfixes
hotfix-*) iterm2_set_tab_color 200 50 50 ;; # Red for hotfixes
*) iterm2_set_tab_color 100 100 200 ;; # Blue for others
esac
}
# Auto-color on directory change
chpwd_functions+=(wt-color)Badge Configuration
Show worktree info in tab badge:
# Update iTerm2 badge with worktree info
iterm2_set_badge() {
printf "\033]1337;SetBadgeFormat=%s\007" "$(echo -n "$1" | base64)"
}
# Set badge when entering worktree
wt-badge() {
if git rev-parse --git-dir > /dev/null 2>&1; then
local branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
local wt=$(basename "$PWD")
iterm2_set_badge "WT: $wt\n$branch"
fi
}
# Auto-update badge on directory change
chpwd_functions+=(wt-badge)Triggers
Add triggers to highlight worktree-related output:
In iTerm2 Preferences > Profiles > Worktree > Advanced > Triggers:
| Regex | Action | Parameter |
|---|---|---|
^\[WORKTREE\] | Highlight Text | Green |
^Created worktree: | Post Notification | Worktree Created |
^Removed worktree: | Post Notification | Worktree Removed |
fatal:.*worktree | Highlight Text | Red |
Integration with tmux
When running tmux inside iTerm2:
# Detect environment and use appropriate method
wt() {
if [[ -n "$TMUX" ]]; then
# Use tmux window
wt-tmux "$@"
elif [[ "$TERM_PROGRAM" == "iTerm.app" ]]; then
# Use iTerm2 tab
wt-iterm "$@"
else
# Fallback: just create worktree and cd
wt-basic "$@"
fi
}tmux Configuration for Git Worktrees
Optimal tmux settings for managing multiple worktree environments.
Session Configuration
Add to ~/.tmux.conf:
# Window naming
set-option -g automatic-rename off
set-option -g allow-rename off
# Start windows at 1 instead of 0
set -g base-index 1
setw -g pane-base-index 1
# Renumber windows when one is closed
set -g renumber-windows on
# Status bar showing worktree info
set -g status-right '#[fg=cyan]#{pane_current_path} #[fg=white]| #[fg=yellow]%H:%M'
set -g status-right-length 100
# Window status format
setw -g window-status-format '#I:#W'
setw -g window-status-current-format '#[fg=yellow,bold]#I:#W#[fg=default]'
# Quick window switching
bind -n M-1 select-window -t 1
bind -n M-2 select-window -t 2
bind -n M-3 select-window -t 3
bind -n M-4 select-window -t 4
bind -n M-5 select-window -t 5
bind -n M-6 select-window -t 6
bind -n M-7 select-window -t 7
bind -n M-8 select-window -t 8
bind -n M-9 select-window -t 9Worktree-Specific Bindings
# Create new worktree window (prompts for name)
bind W command-prompt -p "Worktree name:" "run-shell 'wt %%'"
# List worktrees in a popup
bind G display-popup -E "git worktree list | less"
# Quick switch to main worktree
bind M run-shell "tmux select-window -t main 2>/dev/null || tmux display 'No main window'"
# Kill worktree window and cleanup
bind X confirm-before -p "Remove worktree #W? (y/n)" "run-shell 'wt-rm #W'"Session Layout Script
Create ~/.config/tmux/worktree-session.sh:
#!/usr/bin/env bash
# Usage: tmux-worktree-session.sh <project-path>
PROJECT_PATH="$1"
PROJECT_NAME=$(basename "$PROJECT_PATH")
# Create or attach to session
tmux has-session -t "$PROJECT_NAME" 2>/dev/null
if [ $? != 0 ]; then
# Create session with main window
tmux new-session -d -s "$PROJECT_NAME" -n "main" -c "$PROJECT_PATH"
# Get existing worktrees and create windows
cd "$PROJECT_PATH"
git worktree list --porcelain | grep '^worktree' | while read -r line; do
wt_path=$(echo "$line" | cut -d' ' -f2)
wt_name=$(basename "$wt_path")
# Skip main worktree
if [[ "$wt_path" == "$PROJECT_PATH" ]]; then
continue
fi
# Create window for each worktree
tmux new-window -t "$PROJECT_NAME" -n "$wt_name" -c "$wt_path"
done
# Select main window
tmux select-window -t "$PROJECT_NAME:main"
fi
# Attach to session
tmux attach-session -t "$PROJECT_NAME"Window Hooks
Auto-configure windows when created for worktrees:
# Hook: when window is created for a worktree
set-hook -g after-new-window 'if-shell "git rev-parse --git-dir 2>/dev/null" "setenv -g WORKTREE_ACTIVE 1"'
# Hook: cleanup when window is destroyed
set-hook -g window-closed 'run-shell "echo Window closed: #W >> /tmp/tmux-worktree.log"'Status Bar Integration
Show worktree info in status bar:
# Left status: session and window
set -g status-left '#[fg=green]#S #[fg=white]| '
set -g status-left-length 40
# Right status: git branch and worktree path
set -g status-right '#[fg=cyan]#(cd #{pane_current_path}; git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "no git") #[fg=white]| #[fg=yellow]#{pane_current_path}'
set -g status-right-length 120
# Update status every 5 seconds
set -g status-interval 5Pane Layouts for Worktrees
# Preset layout: editor + terminal
bind L select-layout main-vertical \; \
resize-pane -t 1 -x 60%
# Preset layout: equal split for code review
bind E select-layout even-horizontal
# Split and cd to same worktree
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"fzf Integration
Quick worktree switching with fzf:
# Switch to worktree window using fzf
bind f display-popup -E "tmux list-windows -F '#W' | fzf --reverse | xargs tmux select-window -t"
# Switch to worktree directory using fzf
bind F display-popup -E "git worktree list | fzf --reverse | awk '{print \$1}' | xargs -I{} tmux send-keys 'cd {}' Enter"Environment Variables
Pass worktree info to new windows:
# Set environment for worktree windows
set-environment -g WORKTREE_TERMINAL "tmux"
# Update environment on attach
set -g update-environment "WORKTREE_TERMINAL"