10 min readJust now
–
Press enter or click to view image in full size
Git Worktree for Claude Code
It was 2 AM. Claude Code was halfway through refactoring our payment processing module — a 3-hour job that had already consumed two. Files scattered across 12 directories. Uncommitted changes everywhere. The AI was in the zone.
Then Slack lit up.
“Production is down. Payment gateway throwing 500s. Need a fix NOW.”
I stared at my terminal. Claude had touched 47 files. The payment module — the exact code I needed to debug — was mid-surgery. Uncommitted. Unstaged. A beautiful mess of half-finished brilliance.
My options?
Stash everything and pray I could reconstruct Claude’s context later. Or clone the entire repo again, wasting 20 minutes on a 4GB download while produc…
10 min readJust now
–
Press enter or click to view image in full size
Git Worktree for Claude Code
It was 2 AM. Claude Code was halfway through refactoring our payment processing module — a 3-hour job that had already consumed two. Files scattered across 12 directories. Uncommitted changes everywhere. The AI was in the zone.
Then Slack lit up.
“Production is down. Payment gateway throwing 500s. Need a fix NOW.”
I stared at my terminal. Claude had touched 47 files. The payment module — the exact code I needed to debug — was mid-surgery. Uncommitted. Unstaged. A beautiful mess of half-finished brilliance.
My options?
Stash everything and pray I could reconstruct Claude’s context later. Or clone the entire repo again, wasting 20 minutes on a 4GB download while production burned. Or — and this is what I actually did — tell my team lead I needed “a few more minutes” while frantically trying to commit half-working code just to switch branches.
None of these are good options. All of them are what developers do every single day.
I’d been using Claude Code for months at this point. It had become my coding partner, my rubber duck, my 3 AM companion. But this moment exposed a fundamental flaw — not in Claude, but in how I was working with it.
The AI doesn’t care about your git workflow. It doesn’t know you’ll need to context-switch in 10 minutes. It just codes. Brilliantly, relentlessly, and across as many files as the task requires.
The next morning, after the production fire was extinguished (it was a config issue, naturally), I sat down with a coffee and typed two words into Google: “git worktree.”
That search changed everything.
The Real Problem: You Can’t Be in Two Places at Once
Here’s the scenario every developer knows:
You’re deep into Feature A. Claude is humming along, generating tests, refactoring components, building something beautiful. Then Feature B becomes urgent. Or a bug appears. Or code review comments come back on last week’s PR.
Traditional git gives you one working directory. One set of files. One context.
You can switch branches, sure. But what about those uncommitted changes? What about the mental state of your AI session? What about the terminal history, the test runs, the half-written debugging notes?
Gone. All of it.
Most developers I know went through three distinct phases trying to solve this:
Phase 1: The “Just Clone Another Copy” Era
The brute force approach. Need isolation? Clone the entire repo again. Have five features? Clone five times.
Your disk weeps. Your .git folders multiply like rabbits. And syncing changes between clones? Nightmare fuel.
Phase 2: The “Stash Everything” Dance
You become a git stash virtuoso. git stash, git stash pop, git stash apply stash@{3}. You lose track. You lose code. You lose sleep.
Phase 3: The Enlightenment — Git Worktree
And then someone whispers those two magic words.
What Git Worktree Actually Does (Without the Jargon)
Think of your git repository as a library. Traditional git is like having one reading room where you can only open one book at a time. Want to read another book? Close the first one.
Git worktree gives you multiple reading rooms. Same library. Same books. But you can have “Chapter 5 of Authentication” open in one room while “Appendix B: Bug Fixes” stays open in another.
Technically, git worktree lets you check out multiple branches from the same repository into separate directories—simultaneously. One .git folder. Multiple working directories. Zero duplication.
# Create a worktree for a new featuregit worktree add ../feature-auth feature-auth# Now you have two directories:# /your-repo (main branch)# /feature-auth (feature-auth branch)
Each directory is fully functional. You can run builds. Start dev servers. Let Claude Code loose.
But here’s the catch.
The Technical Advantages That Changed My Workflow
Instant Context Switching
No stashing. No committing half-baked code. Just cd ../other-worktree. You’re on a different branch with a different Claude session. Your terminal history stays intact. Your mental context switches cleanly.
Shared Git History
Unlike cloning, worktrees share the same .git database. Fetch once, available everywhere. Commit in one worktree, cherry-pick from another instantly. The efficiency is staggering.
Disk Space Sanity
A cloned repo duplicates everything — the entire .git folder, all objects, all history. A worktree? Just the working files. For a 2GB repository, the difference between five clones (10GB) and five worktrees (2.5GB) is your laptop thanking you.
Perfect for AI Coding Sessions
This is where it gets interesting for Claude Code users.
Each worktree becomes an isolated sandbox. Claude can experiment wildly in gwt/experiment-1 while your stable code sits untouched in main. When the AI produces gold, you merge. When it produces chaos, you delete the worktree and walk away.
No archaeology required. No git reset --hard. Just surgical removal.
The Dark Side: Disadvantages You Need to Know
Let me be honest. Git worktree isn’t perfect.
Mental Overhead
You now have multiple directories to track. Which worktree was I working on the payment feature? Was it gwt/fix-payments or gwt/payment-refactor? Without discipline, you’ll drown in your own organization system.
Shared State Gotchas
All worktrees share the same .git folder. Run git fetch in one, and refs update everywhere. Usually good. Sometimes confusing. Always worth knowing.
Branch Locking
You cannot check out the same branch in multiple worktrees. Makes sense when you think about it — git can’t track two sets of changes to the same branch. But it catches people off guard.
Tool Compatibility
Some IDE integrations get confused by worktrees. VS Code handles them well. Other editors… less gracefully. Test your toolchain before committing to this workflow.
Submodule Complexity
If your repo uses submodules, worktrees add another layer of complexity. Each worktree needs its own submodule checkout. Not impossible, but not automatic either.
The Monorepo Evolution: Sparse Checkout + Worktree
Here’s where my workflow leveled up again.
I work on a monorepo. Frontend, backend, shared libraries, infrastructure — all in one repository. When Claude needs to fix an API endpoint, why should it see 50,000 frontend components?
Enter sparse checkout.
Sparse checkout lets you check out only specific folders from a repository. Combine it with worktree, and you get laser-focused AI environments.
# Create a worktree with NO files initiallygit worktree add --no-checkout gwt/api-fix api-fix# Navigate and configure sparse checkoutcd gwt/api-fixgit sparse-checkout init --conegit sparse-checkout set api/# Now only the api/ folder exists in this worktree
The benefits compound:
- Reduced AI Context — Claude Code reads fewer files. Its responses become more focused. Less hallucination. More precision.
- Faster Operations —
git status,git diff, even file watchers run faster when there are fewer files to track. - Clearer Boundaries — When Claude only sees the
api/folder, it can’t accidentally suggest frontend changes for a backend task.
This is the workflow I use now. Not just worktrees — scoped worktrees.
Alternatives to Git Worktree (And Why I Still Choose It)
Fair question: what else exists?
Multiple Clones
The original solution. Still works. But the disk space and synchronization overhead make it painful at scale. If you have unlimited storage and patience, go for it.
Git Stash
Great for quick context switches. Terrible for long-running AI sessions. You’ll lose track of stashes. Trust me.
Git Branch + Manual Cleanup
Work on a branch, switch when needed, deal with uncommitted changes manually. This is what most people do. It’s fine until it isn’t.
Docker Development Containers
Isolate entire development environments. Powerful but heavyweight. Overkill for simple branch isolation. Perfect for complex environment requirements.
GitHub Codespaces / Gitpod
Cloud-based isolated environments. Excellent tooling. But you’re paying for compute, dealing with latency, and losing offline capability. For some teams, the trade-off is worth it.
Nix Flakes / Devbox
Reproducible development environments. Solves different problems — environment consistency rather than branch isolation. Complementary to worktrees, not a replacement.
Why I Choose Worktree
Simplicity. Git worktree is built into git. No external tools. No cloud dependencies. No Docker overhead. Just pure git, doing exactly what you need.
For Claude Code development specifically, the lightweight isolation is perfect. I can spin up a worktree in seconds, let the AI work, and tear it down when done. The cognitive load stays manageable.
A Ready-Made Script for Your Workflow
I’ve been using a helper script to manage this workflow. It handles worktree creation, sparse checkout configuration, environment file copying, and cleanup. After months of refinement, here it is:
#!/bin/bash# ==============================================================================# Claude Code Worktree Manager (cw)# A helper to create isolated, context-aware git environments.# ==============================================================================# Colors (Fixed with ' for proper shell interpretation)GREEN='\033[0;32m'BLUE='\033[0;34m'CYAN='\033[0;36m'RED='\033[0;31m'YELLOW='\033[0;33m'BOLD='\033[1m'NC='\033[0m' # No Color# ------------------------------------------------------------------------------# Help Documentation# ------------------------------------------------------------------------------function show_help { cat << EOF${BOLD}Claude Code Worktree Manager (cw)${NC}A tool to create isolated git environments for AI coding sessions.It keeps your main branch clean and creates focused folders for Claude Code.${BOLD}USAGE:${NC} cw ${BLUE}<command>${NC} ${GREEN}[arguments]${NC}${BOLD}COMMANDS:${NC} ${BLUE}setup${NC} ${GREEN}<branch> [scope]${NC} Create a new worktree. - ${GREEN}<branch>${NC}: The name of the git branch. - ${GREEN}[scope]${NC}: (Optional) The specific folder to check out (e.g., 'api', 'web'). Use this for monorepos to save space and reduce AI context noise. ${BLUE}cleanup${NC} ${GREEN}<branch>${NC} Remove a worktree and optionally delete the branch. - Automatically removes the folder from 'gwt/'. - Prunes git worktree data. ${BLUE}list${NC} Show all active worktrees. ${BLUE}--help, -h${NC} Show this help message.${BOLD}EXAMPLES:${NC} ${CYAN}1. Standard Setup (Full Repo):${NC} $ cw setup feature-login ${NC}Creates 'gwt/feature-login' containing all files.${NC} ${CYAN}2. Monorepo Setup (Sparse Checkout):${NC} $ cw setup fix-api-endpoint api ${NC}Creates 'gwt/fix-api-endpoint' containing ONLY the 'api/' folder.${NC} ${CYAN}3. Cleanup:${NC} $ cw cleanup feature-login ${NC}Deletes the folder and removes git worktree entry.${NC}${BOLD}AUTOMATION FEATURES:${NC} - ${YELLOW}.env Sync:${NC} Copies .env from root (and scope folder) to worktree. - ${YELLOW}Context Sync:${NC} Copies .claude/ config folder to worktree. - ${YELLOW}Safety:${NC} Adds 'gwt/' to .gitignore automatically.EOF}# ------------------------------------------------------------------------------# Core Logic# ------------------------------------------------------------------------------COMMAND=$1BRANCH_NAME=$2SCOPE=$3function check_git_root { if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then echo -e "${RED}Error: Not inside a git repository.${NC}" exit 1 fi REPO_ROOT=$(git rev-parse --show-toplevel)}function check_gitignore { GITIGNORE_PATH="$REPO_ROOT/.gitignore" if [ ! -f "$GITIGNORE_PATH" ]; then touch "$GITIGNORE_PATH"; fi # Ensure gwt/ is ignored if ! grep -q "^/gwt/" "$GITIGNORE_PATH" && ! grep -q "^gwt/" "$GITIGNORE_PATH"; then echo -e "${YELLOW}Adding 'gwt/' to .gitignore...${NC}" # Ensure newline before appending tail -c1 "$GITIGNORE_PATH" | read -r _ || echo >> "$GITIGNORE_PATH" echo "gwt/" >> "$GITIGNORE_PATH" fi}function setup_worktree { if [ -z "$BRANCH_NAME" ]; then echo -e "${RED}Error: Missing branch name.${NC}" echo -e "Usage: cw setup <branch-name> [scope]" exit 1 fi check_git_root GWT_DIR="$REPO_ROOT/gwt" WORKTREE_PATH="$GWT_DIR/$BRANCH_NAME" mkdir -p "$GWT_DIR" check_gitignore if [ -d "$WORKTREE_PATH" ]; then echo -e "${RED}Error: Folder $WORKTREE_PATH already exists.${NC}" exit 1 fi echo -e "${BLUE}Setting up worktree: $BRANCH_NAME...${NC}" # 1. Create the worktree (initially checks out everything or nothing based on strategy) if [ -n "$SCOPE" ]; then # Check if scope exists in main repo if [ ! -d "$REPO_ROOT/$SCOPE" ]; then echo -e "${RED}Error: Folder '$SCOPE' does not exist in the repository.${NC}" exit 1 fi if git rev-parse --verify "$BRANCH_NAME" >/dev/null 2>&1; then git worktree add --no-checkout "$WORKTREE_PATH" "$BRANCH_NAME" else git worktree add --no-checkout -b "$BRANCH_NAME" "$WORKTREE_PATH" fi else # Standard full checkout if git rev-parse --verify "$BRANCH_NAME" >/dev/null 2>&1; then git worktree add "$WORKTREE_PATH" "$BRANCH_NAME" else git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" fi fi # 2. Configure Sparse Checkout if SCOPE is set if [ -n "$SCOPE" ]; then echo -e "${BLUE}Configuring sparse checkout for folder: $SCOPE...${NC}" cd "$WORKTREE_PATH" || exit git sparse-checkout init --cone git sparse-checkout set "$SCOPE" git checkout "$BRANCH_NAME" cd - > /dev/null fi # 3. Copy Context Files (.env / .claude) # Root .env if [ -f "$REPO_ROOT/.env" ]; then echo -e "${BLUE}Copying root .env...${NC}" cp "$REPO_ROOT/.env" "$WORKTREE_PATH/.env" fi # Scoped .env (e.g., api/.env -> gwt/branch/api/.env) if [ -n "$SCOPE" ] && [ -f "$REPO_ROOT/$SCOPE/.env" ]; then echo -e "${BLUE}Copying $SCOPE/.env...${NC}" cp "$REPO_ROOT/$SCOPE/.env" "$WORKTREE_PATH/$SCOPE/.env" fi # Claude Config if [ -d "$REPO_ROOT/.claude" ]; then echo -e "${BLUE}Copying .claude config...${NC}" cp -r "$REPO_ROOT/.claude" "$WORKTREE_PATH/.claude" fi echo -e "${GREEN}Worktree Ready!${NC}" echo -e " Run: ${BOLD}cd gwt/$BRANCH_NAME && claude${NC}"}function cleanup_worktree { if [ -z "$BRANCH_NAME" ]; then echo -e "${RED}Error: Missing branch name.${NC}" echo -e "Usage: cw cleanup <branch-name>" exit 1 fi check_git_root WORKTREE_PATH="$REPO_ROOT/gwt/$BRANCH_NAME" if [ ! -d "$WORKTREE_PATH" ]; then echo -e "${RED}Error: Worktree '$WORKTREE_PATH' not found.${NC}" exit 1 fi echo -e "${BLUE}Removing worktree: $BRANCH_NAME...${NC}" # Force remove works better if git status is messy inside worktree git worktree remove "$WORKTREE_PATH" --force git worktree prune read -p "Delete branch '$BRANCH_NAME'? (y/n) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then git branch -d "$BRANCH_NAME" || echo -e "${RED}Branch not merged. Use 'git branch -D $BRANCH_NAME' to force.${NC}" fi echo -e "${GREEN}Cleanup complete.${NC}"}function list_worktrees { check_git_root echo -e "${BLUE}Active Worktrees:${NC}" git worktree list}# ------------------------------------------------------------------------------# Main Switch# ------------------------------------------------------------------------------case "$COMMAND" in setup) setup_worktree ;; cleanup) cleanup_worktree ;; list) list_worktrees ;; help|--help|-h) show_help ;; *) if [ -z "$COMMAND" ]; then show_help else echo -e "${RED}Unknown command: $COMMAND${NC}" echo "Try 'cw --help' for usage information." exit 1 fi ;;esac
Save this as cw (Claude Worktree) somewhere in your PATH. Make it executable with chmod +x cw.
nano cw# copy-paste the above scriptchmod +x cwsudo mv cw /usr/local/bin/cw
Usage is simple:
# Full repo worktreecw setup feature-new-login# Monorepo scoped worktree (only checks out 'api' folder)cw setup fix-api-bug api# See all worktreescw list# Clean up when donecw cleanup feature-new-login
The script automatically copies your .env files and .claude configuration to each worktree. No manual setup. No forgotten environment variables.
The Workflow That Works
Here’s my daily routine now:
- Start in main repo. Pull latest changes.
- Run
cw setup ticket-1234for the feature I’m working on. cd gwt/ticket-1234 && claude- Let Claude work. Review. Iterate.
- When satisfied, commit, push, create PR.
cw cleanup ticket-1234
Rinse. Repeat.
My main branch stays pristine. Claude experiments freely. And when the AI produces something brilliant, merging is trivial.
The Bottom Line
Git worktree isn’t new. It’s been in git since 2015. But the rise of AI coding assistants like Claude Code gives it new purpose.
The pattern is clear: isolate AI experiments, preserve your stable code, merge only the wins.
Whether you use the script I’ve shared or build your own workflow, the principle remains. Give Claude Code a sandbox. Protect your main branch. Sleep better at night.
That 47-file deletion I mentioned at the start? It was actually good code. I merged it. But now I merge by choice, not by accident.
That’s the difference a worktree makes.