I had a lot of work to get through, and for once I didn't want to crawl through it one ticket at a time.
I knew Claude Code could run a few sessions in parallel, so my first thought was just to turn a couple of agents loose on different things at once.
But then I hit my actual hangup: I don't merge code I haven't read.
I like going through diffs properly, lately with git-lrc.
So the question became, how do I let a bunch of sessions work at the same time without all their changes piling up into one unreviewable mess on a single branch?
Because in a single checkout, everything is sequential by definition: work → review → commit → push → cut a new branch → start the whole thing over.
One task can't start until the last one is done.
And if something interrupts you halfway through, you're back to the old muscle memory: stash, checkout main, branch, fix, switch back, pop the stash, and hope nothing re-ran a build while you weren't looking.
Then I remembered git worktrees. And it turned out Claude had a genuinely good doc on them.
The idea clicked right away.
One folder per task, each on its own branch, each with its own Claude session.
My one rule was dead simple: branch name = session name = whatever it's for.
That way I could jump to any of them, or come back hours later, and not have to squint to remember which was which.
So I sat down and actually learned it on my little TUI file browser, peektea.
Here's the small write-up. Let me pour you a cup xD
What a worktree actually is
A normal clone gives you one working directory tied to one branch.
A worktree is a second working directory for the same repo, parked on a different branch.
Both folders share one .git (file) same history, same remote, but their files are completely independent.
Edit, build, or run in one, and the other never even notices.
That isolation is the entire point.
Claude can be wiring up one feature in Terminal A while you fix something unrelated in Terminal B, and neither session can clobber the other's files. No stashing. No tea spilled.
The one rule that decides everything
A worktree belongs to exactly one repository, and a branch can live in exactly one worktree at a time. So the math is simple: one worktree per parallel feature. Two features in peektea → two worktrees → two terminals → two sessions.
Our two "tickets"
I created two issues in peektea that may not touch each other, ideal for steeping in parallel:
| Feature | Issue | What it is |
|---|---|---|
| A | #2 |
Copy shortcuts: y copies the highlighted path, Y copies the file's contents |
| B | #3 |
Move a file: x cuts the entry, v drops it into the current directory |
Both branch off master.
Both add keybindings, but they live in different code paths, exactly the kind of "could be one PR each, done at the same time" work worktrees were made for.
My main checkout lives at ~/pers/peektea.
The worktrees will sit right next to it.
Go: one terminal each
You run git worktree add from any existing checkout of the repo, you don't have to be "inside" a worktree to make one.
The command creates the folder and the branch in a single shot and wires them together.
Terminal A · the copy-shortcuts feature
cd ~/pers/peektea
git fetch origin master # refresh the base
# new folder + new branch, off master
git worktree add \
-b copy-path-and-contents \
~/pers/peektea-copy \
master
cd ~/pers/peektea-copy
claude # fresh session, right here
Terminal B · the move-file feature
cd ~/pers/peektea
git worktree add \
-b move-to-dir \
~/pers/peektea-move \
master
cd ~/pers/peektea-move
claude # second session, fully independent
That's it. Two checkouts, two branches, two Claude sessions and your original ~/pers/peektea is sitting there untouched, exactly how you left it.
Name the session after the branch
Inside each session, run /rename to match the branch.
Costs a second now, saves you squinting at an unlabelled session list later:
/rename copy-path-and-contents
Because claude --resume only lists sessions for the folder you launch it from, the branch-named one is the obvious cup waiting in each worktree:
cd ~/pers/peektea-move
claude --resume # pick the named session
# or, fastest:
claude --continue # reopen the most recent one here
Living in two checkouts at once
Switching is just… switching terminals.
The sessions don't share state, so there's nothing to reconcile.
status and diff work exactly how you already know them, and you can peek at either tree from anywhere with git -C instead of cd-ing around:
# inside a worktree
git status
git diff # unstaged
git diff --staged # staged
# or peek without leaving your current folder
git -C ~/pers/peektea-move status
Here's the part I genuinely didn't appreciate until I tried it.
Because each worktree is its branch, a commit can only ever land on that branch.
The classic "ugh, I committed the bugfix onto the feature branch" mistake isn't something you have to be careful about, it's structurally impossible.
Visually, the two features steep in their own cups and only meet when you merge:
Committing and pushing is the usual ceremony, the first push just sets the upstream:
cd ~/pers/peektea-copy
git add -A
git commit -m "feat: y/Y to copy path and file contents (#2)"
git push -u origin copy-path-and-contents # first push sets upstream
Where the app actually runs
This is where a Go TUI is a delight compared to a web stack.
peektea is a single binary, no frontend, no backend, no ports to fight over.
You build inside the worktree and run the local binary, because you're testing your edited code, not the main tree's:
cd ~/pers/peektea-move
make build # builds ./peektea right here in the worktree
./peektea # run the version with YOUR changes
Want live reload while you iterate with Claude? make start rebuilds on every .go save:
make start # air watches and rebuilds ./peektea
And because there's no server, you can happily run make start in both worktrees at once, no port collision, no proxy juggling, nothing to stop and restart.
The TUI just reads the terminal it's launched in.
Anyways.
Cleaning up
When a feature's merged, remove its worktree from a different checkout, not from inside the folder you're deleting:
cd ~/pers/peektea
git worktree list # see them all
git worktree remove ~/pers/peektea-move
# refuses if the worktree is dirty; add --force to discard changes
git branch -d move-to-dir # -D to force-delete if unmerged
git worktree prune # tidy up stale metadata
Note that git worktree remove leaves the branch behind on purpose, so you can't accidentally throw away unmerged work by deleting a folder.
Branches get deleted separately, deliberately. Polite to the last drop.
"But Claude Code has --worktree…"
It does! claude --worktree feature-x spins up a worktree and drops you straight into a session, perfect for a quick spike.
For real tickets I still reach for the manual git worktree add, for two reasons:
- It names the branch
worktree-feature-x, not the exact name I want (copy-path-and-contents). - It branches from
origin/HEAD, which here ismasteranyway, but on repos where your trunk isdevordevelop, that's the wrong base.
When the branch name and base both need to be exactly right, plain git worktree add wins.
For a throwaway experiment, --worktree is the faster pour.
Cheat sheet :D
| Command | What it does |
|---|---|
git worktree add -b <branch> <dir> <base> |
new worktree on a brand-new branch |
git worktree add <dir> <existing-branch> |
worktree from an existing branch |
git worktree list |
show every worktree + its branch |
git -C <dir> status |
inspect a worktree without cd-ing |
git worktree remove <dir> |
delete it (--force if dirty) |
git worktree prune |
clear stale worktree metadata |
/rename <name> |
name the Claude session (= the branch) |
claude --resume / --continue
|
reopen a session in this folder |
The takeaway
Worktrees turn "I can only hold one branch in my hands at a time" into "I have as many hands as I have terminals."
Pair that with a named Claude Code session per checkout, and parallel work stops feeling like juggling and starts feeling like… letting two cups steep at once.
No stash dance. No wrong-branch commits. No tea spilled.
Disclaimer: This article was written by me; AI was used to fix grammar and improve readability.
AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs — without telling you. You often find out in production.
git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.
Any feedback or contributors are welcome! It's online, source-available, and ready for anyone to use.
⭐ Star it on GitHub:
HexmosTech
/
git-lrc
Free, Micro AI Code Reviews That Run on Git Commit
| 🇩🇰 Dansk | 🇪🇸 Español | 🇮🇷 Farsi | 🇫🇮 Suomi | 🇯🇵 日本語 | 🇳🇴 Norsk | 🇵🇹 Português | 🇷🇺 Русский | 🇦🇱 Shqip | 🇨🇳 中文 | 🇮🇳 हिन्दी |
git-lrc
Free, Micro AI Code Reviews That Run on Commit
GenAI today is a race car without brakes. It accelerates fast -- you describe something, and large blocks of code appear instantly. But AI agents silently break things: they remove logic, relax constraints, introduce expensive cloud calls, leak credentials, and change behavior -- without telling you. You often find out in production.
git-lrc is your braking system. It hooks into git commit and runs an AI review on every diff before it lands. 60-second setup. Completely free.
In short, git-lrc helps Prevent Outages, Breaches, and Technical Debt Before They Happen
At a glance: 10 risk categories · 100+ failure patterns tracked · every commit…









Top comments (4)
Worktrees isolate your tracked files, but not the stuff living around them, and that's the gotcha I'd flag for anyone coming from a web stack. Two sessions still share one local database and the same :3000, plus whatever lives in .env, so the moment both run migrations the isolation you thought you had is gone. Your single Go binary is exactly why this reads so smoothly, no ports to fight over is a real luxury. For a Node or Rails reader I'd add a note about separate env files per worktree and separate DB names, otherwise the two cups end up steeping in the same pot. Really like the rule of naming the session after the branch though, that one's going straight into my habits.
Yes that's true. If its too much hassle to use seperate db etc, after pushing the code, you can checkout to that branch from the main dir and test. So that's always there 😉
The clean-isolation story holds right up until your two "unrelated" features both touch a shared file — and with agents in parallel, that happens more than you'd think. Your copy and move tickets both add keybindings, and even if the handlers live in different code paths, they likely register in the same dispatch table or key-map switch. Worktrees don't remove that collision; they defer it to merge time, where you now hit a conflict on code neither Claude session can see the other half of. The isolation that prevents wrong-branch commits also means each agent is optimizing against a stale
master, blind to what the other one is doing.The failure mode I'd flag specifically: an agent that hits a compile error because it references a symbol it thinks should exist (say, a shared helper the other worktree is mid-refactor on) will happily invent its own version rather than tell you it's blocked. Two independent inventions of the same helper is a merge conflict that looks like a semantic bug. The worktree hygiene here is great, but the real discipline is scoping the tickets so the agents genuinely can't overlap — and "both add keybindings" is exactly the kind of thing that reads as parallel but isn't.
One small correction worth noting:
--worktreenaming and base behavior in Claude Code has shifted across versions, so it's worth checking current behavior rather than treating theworktree-prefix as fixed.Got it, thanks for the suggestion, I agree the scope of work matters a lot.