I firmly believe that Git worktrees are one of the most underrated features of Git. I ignored them for years because I didn’t understand them or how to adapt them to my workflow.

Tree with multiple branches representing git worktrees

But that changed as I began using LLM coding tools to do more work in parallel. Being able to work on multiple branches simultaneously is a game changer.

Without git worktrees, working on multiple branches at the same time in the same repository is a pain. There’s the serial approach where you stash your changes, context switch, and pray you didn’t break anything. Or worse, just commit half-finished work with “WIP” messages (looking at you, past me). Or you can have multiple clones of the same repository, but that’s a pain to manage and can take up a lot of disk space.

Git worktrees solve this. They let you have multiple branches checked out simultaneously in different directories that all share the same git database (aka the .git directory). For me, this means I can work on a feature in one terminal, review a PR in another, have Claude Code work on another feature in another terminal, and have all of them share the same git history.

But here’s the thing: creating worktrees manually is tedious. You need to remember where you put them, what to name them, and clean them up later. Also, by default the git worktree is created in the root of the repository unless you specify a different directory.

I wanted something simpler. I wanted something that would work across any repository. No setup, no configuration files, just sensible defaults.

Enter tree-me

I built tree-me, a minimal wrapper around git’s native worktree commands. It adds organizational convention while letting git handle all the complexity.

Instead of this:

# Create worktree manually
mkdir -p ~/worktrees/my-project
git worktree add ~/worktrees/my-project/fix-bug -b haacked/fix-bug main
cd ~/worktrees/my-project/fix-bug
# Now repeat for every repo...

You do this:

tree-me create haacked/fix-bug
# Creates: ~/dev/worktrees/my-project/haacked/fix-bug
# And automatically cds into it

How it works

tree-me uses git-like subcommands and follows conventions so you don’t have to think:

  • Auto-detects repository name from your git remote
  • Auto-detects default branch (checks for origin/HEAD, falls back to main)
  • Organizes by repo: $WORKTREE_ROOT/<repo-name>/<branch-name>
  • Delegates to git for all validation, errors, and edge cases
  • PR support: Fetches GitHub PRs using git’s native PR refs (requires gh CLI)
  • Auto-CD: Automatically changes to the worktree directory after creation
  • Tab completion: Complete commands and branch names in bash/zsh

Commands:

tree-me create <branch> [base]        # Create new branch in worktree
tree-me checkout <branch>             # Checkout existing branch (alias: co)
tree-me pr <number|url>               # Checkout GitHub PR (uses gh CLI)
tree-me list                          # List all worktrees (alias: ls)
tree-me remove <branch>               # Remove a worktree (alias: rm)
tree-me prune                         # Prune stale worktree files
tree-me shellenv                      # Output shell function for auto-cd

Examples:

tree-me create haacked/fix-bug              # Create from main/master
tree-me create haacked/fix-bug develop      # Create from develop
tree-me co existing-feature                 # Checkout existing branch
tree-me pr 123                              # Checkout PR #123
tree-me pr https://github.com/org/repo/pull/456
tree-me ls                                  # Show all worktrees
tree-me rm haacked/fix-bug                  # Clean up (supports tab completion)

Conventions

tree-me is a minimal wrapper around git’s native commands. Works with any repo, any language, any setup. The only convention is where worktrees live and how they’re named.

Want worktrees in a different location? Set WORKTREE_ROOT. Need to branch from develop instead of main? Pass it as an argument: tree-me create my-feature develop. Conventions with escape hatches.

Setup

To enable auto-cd and tab completion, add this to your ~/.bashrc or ~/.zshrc:

source <(tree-me shellenv)

This makes tree-me create, tree-me checkout, and tree-me pr automatically cd into the worktree directory. It also enables tab completion for commands and branch names (try tree-me rm <TAB>).

View the full implementation at github.com/haacked/dotfiles/blob/main/bin/tree-me.

The PR workflow

Here’s where it shines. Someone asks you to review a PR while you’re deep in a feature:

tree-me pr 123                  # Fetches, checks out PR, and cds into it
# You're now in: ~/dev/worktrees/dotfiles/pr-123
# Review the code, test it, leave comments
# When done, switch back
tree-me co haacked/my-feature   # Checks out and cds back to your feature
# Back to your work, no stash needed

When you’re done reviewing:

tree-me rm pr-123               # Tab complete to see available branches

Gone. Clean. No accidentally committing review changes to your feature branch.

Note

tree-me uses the gh CLI to fetch PRs. If you don’t have it installed, you can install it with brew install gh.

Installation

Download tree-me and put it somewhere in your PATH:

# Example: copy to ~/bin or ~/.local/bin
curl -o ~/bin/tree-me https://raw.githubusercontent.com/haacked/dotfiles/main/bin/tree-me
chmod +x ~/bin/tree-me

Then enable auto-cd and tab completion (see Setup section above).

That’s it. No dependencies beyond git, bash, and optionally the gh CLI for PR checkout.

Why I built this

I work on multiple repos daily—PostHog, my blog, various open source projects. I was tired of remembering project-specific worktree scripts and hunting for that worktree I created last week.

The philosophy: Don’t recreate what git does well. Add only the minimal convention needed.

Git already handles worktrees perfectly. I just needed organized paths, sensible defaults, and a consistent interface across all my projects.

Directory structure

Everything is organized predictably based on the repository name and branch name:

~/dev/worktrees/<repo-name>/<branch-name>

For example:

~/dev/worktrees/
├── dotfiles/
│   ├── haacked/vim-improvements/
│   ├── haacked/git-tools/
│   └── main/
├── posthog/
│   ├── haacked/feature-flags/
│   ├── pr-789-contributor/
│   └── main/
└── spelungit/
    └── haacked/performance/

One glance and you know where everything is.

What it doesn’t do

tree-me doesn’t copy environment files, install dependencies, or set up project-specific tools. That’s deliberate. Those concerns belong in your project’s setup scripts, not in a generic git tool.

Want to automate environment setup? Add a script to your repo that runs after checkout. Want to copy .env files? Put it in your project’s onboarding docs. tree-me just handles the git worktree ceremony.

Try it

If you work with multiple branches regularly, give worktrees a try. If you work with multiple repos, give tree-me a try. If you hate it, at least you learned about git worktrees (and that’s probably worth more than the script).

Find tree-me at github.com/haacked/dotfiles/blob/main/bin/tree-me. It’s MIT licensed—copy it, modify it, improve it.