How I work on several changes at once without them getting tangled
Run two changes in the same checkout and sooner or later a branch carries both their commits, and a pull request stops matching anything that was verified. I found that out the slow way. It's why every change I make now gets its own branch, folder and pull request, and why cf, the Claude Code plugin I put every change through, enforces that rather than trusting me to remember.
I run changes side by side because a change spends most of its life waiting. On 31 October the featured articles change took 20 minutes to plan, five for the plan reviewer, 19 for the builder and just over six for verify. For most of that hour I was waiting for the next gate, and the obvious use of that wait is the next change. Starting it is how I get more commits and pull requests out of one project in a day, with every change still going through every gate. How those gates work is in the first article on cf.
The rule I hold every change to
Its own branch, its own folder, its own pull request.
The branch and the folder keep a change's commits and files apart, so a build in one change never sees half-written code from another.
The pull request is what I review and merge. If it carries anything that didn't pass verify, the verify result no longer describes what I'm merging. That's the standard I'd hold a team to, and the rest of cf's setup exists to keep it.
cf enforces the rule at ship and at push, refusing any branch that carries commits from another open change until I've separated them. A change stacked on top of another is fine, because the one underneath can still ship first.
What /cf:new sets up before any planning starts
/cf:new fetches main and creates a branch called cf/<change> from it, so every change starts from the latest base.
Then it adds a git worktree next to the project. A worktree is a second working copy of the same repository, checked out on its own branch and sharing one history with the first. A change called featured-articles gets marcallington.com-featured-articles beside marcallington.com. The new folder starts without .env files, because they're git-ignored, so cf copies mine across and tells me which folder to open.
I open it in its own VS Code window and run the change's stages there, so two windows means two builds running at once. All the folders share one .work/ folder, so /cf:status lists every open change, its stage and the next command, whichever window I run it in. A conversation started in a change's folder knows which change it's working on, and a stage run from the wrong folder stops and names the right one. Only a command I type can switch the active change.
When another change lands first
cf compares two things across every open change: the files each plan says it will touch, and what each branch has committed so far. It also flags any two changes that both add database migrations. I hear about an overlap at planning, again when I approve, and in /cf:status. An overlap doesn't stop me running both changes. I merge one first and rebase the other.
Whatever the check says, I keep one schema-altering change in flight per area. Two migrations can collide on their timestamps, or both alter the same table or policy, and I'd rather avoid that than untangle it.
When main moves underneath a change, /cf:status says so and gives me the rebase command to run in that change's folder.
After a rebase the change has to pass verify again. If the change's own diff is the same as before (git gives it the same patch id), verify still runs the checks but reviews only how the new commits on main interact with it, and it doesn't use up a fix round. If I had to resolve conflicts, it's a full verify.
After a pull request merges, cf tells me which overlapping changes need rebasing, and /cf:status reminds me to remove the merged change's folder and branch.
What it costs
Each worktree needs its own npm install. That's disk space and a few minutes at the start of every change.
Anything else running against the repo reads whichever checkout it was pointed at. On 31 October the Claude desktop app's article-admin MCP server was running from the main folder, so the desktop app only had the new featured-article tools once the change had merged and I'd pulled it into that folder.
How a morning with four changes would run
Picture the warehouse and delivery system for a haulage firm I'm working on. The warehouse side tracks stock by pallet location, and the transport side turns each day's orders into driver route sheets.
At 09:00 I start route-sheet-export, which generates each driver's route sheet as a PDF with drops in order and the load weight per drop. /cf:new gives it a branch and a folder, and its plan is ready by 09:20.
While the plan reviewer works, I start pallet-locations from the main window. It records which bay each pallet sits in and adds a migration for a locations table. When its plan lands at 09:40, cf flags an overlap: both plans touch lib/stock/movements.ts, because the route sheet needs a pick list for each drop and a pick is a movement out of a bay. I carry on with both, knowing whichever merges second will rebase.
At 10:00 I start stock-adjustments, which lets the warehouse write off damaged stock and adds its own migration. cf flags that migration against pallet-locations, so I let its plan go through review and hold its build.
The first two pass verify by 10:50. I merge route-sheet-export at 10:55, and /cf:status in the pallet window says main has moved. The rebase applies cleanly and the diff keeps its patch id, so verify runs the checks and reviews only how it sits with the new route sheet code. That takes six minutes and uses no fix round, and pallet-locations merges at 11:10.
/cf:status now says main has moved under stock-adjustments too. With nothing built on its branch yet the rebase is instant, and I start its build at 11:15. Meanwhile a demo on staging shows the load weight per drop is the whole order's weight, not what's left on the vehicle after each drop. The fix, route-sheet-weights, goes through the full lane on its own branch and merges at 12:30. That moves main under stock-adjustments, which passed verify at 11:45, so it rebases cleanly, gets the quick re-check and merges at 12:45.
Sources
git-worktree documentation, Git, https://git-scm.com/docs/git-worktree
git-patch-id documentation, Git, https://git-scm.com/docs/git-patch-id
Comments
Sign in or create an account to join the conversation.
Be the first to comment.
Enjoyed this? Get new articles by email.
Subscribe