Monorepo Release Management with Worktrees
How a platform team maintains two active release branches and a development mainline — simultaneously — using git worktree.
The Problem
The Acme platform is a monorepo with 200+ packages. The team ships new features on main, while maintaining two long-lived release branches: release/v2 for enterprise customers still on v2, and release/v3 for the current stable release.
Every bug fix needs to land on main and then be cherry-picked to both release branches. Tests must pass on all three. Without worktrees, this means constant branch switching, rebuilding node_modules, and waiting for incremental builds to catch up — or maintaining three separate clones of a 4 GB repo.
The Worktree Setup
Instead of cloning three times, the team creates one clone and two linked worktrees:
# Main worktree is the original clone on the main branch
cd ~/projects/acme # already on main
# Create worktrees for each release branch
git worktree add ../acme-v2 release/v2
git worktree add ../acme-v3 release/v3
# Install dependencies in each
(cd ../acme-v2 && npm install) &
(cd ../acme-v3 && npm install) &
waitThe Git object database is shared, so this setup uses roughly 4 GB + working files instead of 12 GB for three full clones.
Daily Workflow
A typical bug fix flows through all three worktrees:
1. Fix the bug on main
cd ~/projects/acme
# Fix the bug
git add -A && git commit -m "fix: prevent duplicate order events"
git push origin main2. Cherry-pick to v3
cd ~/projects/acme-v3
git cherry-pick main
npm test # run tests against v3
git push origin release/v33. Cherry-pick to v2
cd ~/projects/acme-v2
git cherry-pick main
npm test # run tests against v2
git push origin release/v2All three branches are updated and tested within minutes. There is no branch switching, no stashing, and no waiting for rebuilds — each worktree keeps its own build cache and node_modules.
Directory Structure
~/projects/
├── acme/ # main worktree — main branch
│ ├── .git/ # the actual Git repository (shared)
│ ├── packages/
│ ├── node_modules/
│ └── ...
│
├── acme-v3/ # linked worktree — release/v3
│ ├── .git # file pointing to acme/.git
│ ├── packages/
│ ├── node_modules/ # v3-specific dependencies
│ └── ...
│
└── acme-v2/ # linked worktree — release/v2
├── .git # file pointing to acme/.git
├── packages/
├── node_modules/ # v2-specific dependencies
└── ...Key Takeaways
- Disk efficiency. The Git object database is stored once. Only the working files and build artifacts are duplicated per worktree.
- Instant context switching. Switching between main, v3, and v2 is just a
cdcommand. Each worktree has its own build state, so there is no rebuild penalty. - Parallel CI. You can run test suites in all three worktrees simultaneously. A bug fix that takes 10 minutes to test serially takes 4 minutes in parallel.
- Long-lived worktrees are fine. Unlike feature worktrees that come and go, release worktrees can persist for months. Just keep them updated with
git pullin each worktree.