Daksh Pareek

Welcome to my personal portfolio website, showcasing my projects and blogs.

Refining My Git Worktree Workflow: From Chaos to Clarity

2025-12-17


The Context Switching Problem

Context switching has always been a part of development work. Urgent production bugs need immediate attention, teammates need code reviews, and you often need to check how something was implemented in another part of the codebase. Each of these interruptions meant stashing changes, switching branches, and then trying to get back to where you were.

I thought git worktrees would solve this. They did, but only after I stopped using them wrong.


How I Was Doing It Wrong

Initially, I set up a bare repo and created worktrees inside my bare repo based on the feature I was working on. Like below:

my-project/
├── .bare/                  ← Bare repository (no working files)
│   ├── objects/            ← All commits and history
│   ├── refs/               ← All branches and tags
│   ├── config              ← Repository settings
│   ├── feature/abc         ← Worktree for feature abc
│   ├── fix/xyz             ← Worktree for fix xyz

Because of this approach:


Perspective Shift

One fine day, I came upon Matklad’s blog post on Git Worktrees. I got understanding that I need to have a perspective shift in how I was thinking about worktrees.

Instead of creating feature specific directory/worktree, I should create mindset based worktree. The idea is to organize worktrees by the type of work you’re doing, not by what you’re building. So when you need to do something, you already know which worktree to use.

So I created below worktrees:

WorktreePurpose
developFor quickly comparing changes, checking out new branches, and testing small fixes
workThis is the place I am actively building features and working on tasks
reviewFor preparing code for reviews, cleaning up commits, and ensuring everything is polished
scratchA playground for experimenting with ideas, testing out new libraries, or prototyping features without affecting my main work

Now my structure looks like below:

my-project/
├── .bare/                  ← Bare repository (no working files)
│   ├── objects/            ← All commits and history
│   ├── refs/               ← All branches and tags
│   ├── develop/            ← Worktree for quick experiments
│   ├── work/               ← Worktree for active development
│   ├── review/             ← Worktree for code reviews
│   └── scratch/            ← Worktree for prototyping

I used DETACHED HEAD trick to create these worktrees without being tied to any specific branch. So I never get error of “worktree already exists for this branch”.


Scripts to Simplify My Workflow

Since I now have permanent folders, I don’t need scripts to create directories anymore. Instead, I use scripts to reset them to the state I need. This solves the dependency hell—since the node_modules folder stays put, switching tasks is nearly instant because I rarely need a full clean install.

I rely on two main scripts:

1. start-feature.sh

I use this exclusively in my work directory. It fetches the latest changes, handles the branch creation logic, and ensures my dependencies are up to date.

#!/bin/bash
# Usage: ./start-feature.sh feature/new-login
BRANCH_NAME=$1

cd work || exit
git fetch origin
# Reset 'work' to origin/develop and create the new branch
git switch -C "$BRANCH_NAME" origin/develop
pnpm install
echo "Ready to code in 'work' on $BRANCH_NAME"

2. sync-worktree.sh

I use this for my review and scratch folders. It safely “teleports” these folders to any branch or PR I need to inspect, using Detached HEAD to avoid conflicts with my main work.

#!/bin/bash
# Usage: ./sync-worktree.sh review pull/320/head
FOLDER=$1
TARGET=$2

cd "$FOLDER" || exit
git fetch origin
# Switch using detached HEAD so we don't lock the branch name
git checkout --detach "$TARGET"

# Re-link .env files if they don't exist
ln -sf ../shared-config/core.env .env
pnpm install

The Scenarios that I Encounter on Daily Basis and How I Handle Them

Here is how this setup handles real-world chaos without breaking my flow.

Scenario 1: The Urgent Production Bug

Old Way: Stash changes in my current branch (hoping I don’t forget them), switch to main, fix bug, commit, switch back, unstash, resolve weird conflicts.

New Way:

Scenario 2: The “Can you review this?” Request

Old Way: “Sure, give me 10 mins to finish what I’m doing so I can switch branches.”

New Way:

Scenario 3: “I need to check a reference in the old code”

Old Way: Browse GitHub in the browser, which is slow and lacks “Go to Definition.”

New Way:


Getting Started

If you want to try this setup, here’s how to create the initial structure:

# Create a bare repo
git clone --bare [email protected]:you/project.git project/.bare
cd project/.bare

# Create your permanent worktrees
git worktree add --detach develop
git worktree add --detach work
git worktree add --detach review  
git worktree add --detach scratch

# In each folder, check out what you need
cd work && git switch -c your-feature-branch
cd ../develop && git switch main

You can start with just work and develop folders, then add review and scratch later as needed.