Post

Git Worktree: The Better Way to Handle Multiple Branches

Git Worktree: The Better Way to Handle Multiple Branches

When in the middle of some work and needed to quickly review a PR or fix a bug on another branch, you know the overhead. Stash your changes, switch branches, wait for your IDE to reindex, fix the issue, switch back, unstash, wait for reindexing again. There’s a better way: git worktree

I am frequently in the middle of working on an area in a codebase and need to quickly switch contexts to review a PR, test another thought, or start a refactor I noticed that needs to be done.

This is more common recently as I will notice something that needs to be fixed/refactored/improved and will write up a quick prompt to have an LLM agent iterate on in an isolated on its own branch.

Committing or stashing and switching branches has so much overhead.

I had heard about git worktrees but hadn’t looked too closely until recently.

What is Git Worktree?

Git worktree lets you have multiple branches checked out at the same time, each in its own directory. Instead of switching branches in your current directory, you cd to a different folder. Your IDE state, uncommitted changes, and running processes all stay intact.

1
2
3
4
5
6
7
8
9
10
11
12
# Traditional workflow (disruptive and slow)
git stash
git checkout feature-branch
# wait for IDE reindex...
# do work
git checkout main
git stash pop
# wait for IDE reindex again...

# Worktree workflow
cd ../repo_wt/feature-branch
# work already in progress, IDE already indexed

Reasons I am Using them more and more

PR Reviews: Keep your working state while reviewing code in a separate worktree.

Parallel Development: Working on multiple features? Have each in its own worktree with its own terminal session and IDE window.

Hot Fixes: Quickly jump to a clean directory without disrupting your current work.

Build Processes: Run a long build in one worktree while continuing development in another.

The Problem with Raw Git Worktree

Git’s built-in worktree commands work, but they’re verbose and require remembering paths:

1
2
3
4
5
6
7
8
# Create a worktree
git worktree add ../my-repo_wt/feature-branch feature-branch

# Where are my worktrees again?
git worktree list

# Navigate there
cd ../my-repo_wt/feature-branch

You also need to track which branches already have worktrees, handle remote tracking, and manage cleanup.

My Script: wt

I built a wrapper that makes worktrees a bit easier. Source it in your shell config and get two simple commands:

wt - List Everything

Running wt with no arguments shows:

1
2
3
4
5
6
7
8
9
10
Worktrees (most recent first):
Last Commit          Branch                        Path
2 hours ago         feature-login                 /Users/mark/dev/app_wt/feature-login
3 days ago          bugfix-auth                   /Users/mark/dev/app_wt/bugfix-auth
1 week ago          main                          /Users/mark/dev/app

Local branches without a worktree (most recent first):
Last Commit          Branch                        Upstream                      Ahead/Behind
4 hours ago         feature-settings              origin/feature-settings       +2/-0
2 days ago          refactor-api                  origin/refactor-api           +0/-5

Shows you exactly what’s active and what’s available, sorted by recent activity.

wt <branch> - Create or Jump

1
wt feature-login

This single command:

  • Creates a worktree if it doesn’t exist
  • Reuses the existing worktree if it does
  • Handles remote tracking automatically
  • Fetches and pulls latest changes
  • Changes to that directory
  • Shows git status
1
2
3
4
5
6
7
8
9
10
11
# First time - creates worktree
$ wt feature-dashboard
Preparing worktree (new branch 'feature-dashboard')
branch 'feature-dashboard' set up to track 'origin/feature-dashboard'
/Users/mark/dev/app_wt/feature-dashboard
## feature-dashboard...origin/feature-dashboard

# Later - just jumps to existing
$ wt feature-dashboard
/Users/mark/dev/app_wt/feature-dashboard
## feature-dashboard...origin/feature-dashboard [ahead 2]

How It Works

The script organizes worktrees under a sibling directory to your repo:

1
2
3
4
5
6
~/dev/
  app/                    # main repo
  app_wt/                 # worktrees live here
    feature-login/
    bugfix-auth/
    feature__settings/    # slashes become underscores

Key features:

Directory ‘safe’ Naming: Branch names with slashes (like feature/login) are sanitized to feature__login for safe directory names.

Automatic Tracking: Fetches from origin and sets up tracking branches automatically.

Reuse Logic: Jump to existing worktrees instead of creating duplicates.

Status Display: Always shows git status when entering a worktree.

Age Sorting: Lists worktrees and branches by most recent commit, so active work surfaces first.

Real-World Usage

My typical workflow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# In main worktree, working on feature A
$ pwd
/Users/mark/dev/blog

# PR review comes in
$ wt review-pr-123
# now in /Users/mark/dev/blog_wt/review-pr-123
# review, comment, done

# Back to feature A
$ cd ../blog
# all my changes still there, IDE state intact

# Later, check what's active
$ wt
Worktrees (most recent first):
Last Commit          Branch                        Path
10 minutes ago      review-pr-123                 /Users/mark/dev/blog_wt/review-pr-123
2 hours ago         main                          /Users/mark/dev/blog

Installation

Save the script somewhere (I keep it in ~/dotfiles/git-utils/wt.sh) and source it in your .zshrc or .bashrc:

1
source ~/dotfiles/git-utils/wt.sh

Now wt works in any git repository.

Tips

Clean Up Old Worktrees: Git doesn’t auto-delete worktrees. When you’re done:

1
2
3
4
git worktree remove feature-branch
# or just delete the directory
rm -rf ../repo_wt/feature-branch
git worktree prune

Terminal Sessions: I keep a terminal tab per active worktree. Makes context switching easy.

Branch Lifecycle: Create worktree → do work → PR → remove worktree. Keep your main repo clean.

When Worktrees Help

  • Code reviews: Dedicated environment per PR
  • Emergency fixes: Don’t disrupt in-progress work
  • Experiments: Try big changes without affecting in-progress focused work
  • Context preservation: Keep separate mental contexts separate

Script here: wt gist

This post is licensed under CC BY 4.0 by the author.