Git Flow with Worktrees
Git Flow is a popular branching model, but switching between main, develop, feature, release, and hotfix branches constantly is painful. Git worktree makes Git Flow practical by giving each branch type its own directory, eliminating the need to switch branches at all.
Git Flow Recap
Git Flow defines five types of branches, each with a specific role:
- main— always reflects production-ready code. Every commit is a release.
- develop— integration branch for features. Contains the latest delivered development changes.
- feature/*— branched from develop, merged back into develop when complete.
- release/*— branched from develop when preparing a release. Merged into both main and develop.
- hotfix/*— branched from main for urgent fixes. Merged into both main and develop.
The friction with Git Flow comes from constantly switching between these branches. You might be on a feature branch, need to review a release, then handle a hotfix, then return to your feature. With traditional Git Flow, that means three rounds of git stash or throwaway commits.
Mapping Git Flow Branches to Worktrees
The idea is simple: give each Git Flow branch its own worktree. The long-lived branches (main and develop) get permanent worktrees. Short-lived branches (features, releases, hotfixes) get temporary worktrees that are removed after merging.
# Permanent worktrees (always present)
~/projects/myapp/
├── myapp.git/ # bare repo (optional, but clean)
├── main/ # worktree: main branch
├── develop/ # worktree: develop branch
│
# Temporary worktrees (created as needed, removed after merge)
├── feature-auth/ # worktree: feature/auth
├── feature-payments/ # worktree: feature/payments
├── release-2.1/ # worktree: release/2.1
└── hotfix-login/ # worktree: hotfix/login-crashSetup Example
Here is a complete setup using a bare repository as the foundation:
# 1. Create the project directory and bare clone
mkdir ~/projects/myapp && cd ~/projects/myapp
git clone --bare [email protected]:you/myapp.git myapp.git
cd myapp.git
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch origin
# 2. Create permanent worktrees for long-lived branches
git worktree add ../main main
git worktree add ../develop develop
# 3. Start a feature (branch from develop)
cd ../develop
git worktree add -b feature/auth ../feature-auth
# 4. Start a release (branch from develop)
git worktree add -b release/2.1 ../release-2.1
# 5. Handle a hotfix (branch from main)
cd ../main
git worktree add -b hotfix/login-crash ../hotfix-loginMerging and cleaning up
# Merge a completed feature into develop
cd ../develop
git merge feature/auth
git worktree remove ../feature-auth
git branch -d feature/auth
# Merge a release into main and develop
cd ../main
git merge release/2.1
git tag -a v2.1.0 -m "Release 2.1.0"
cd ../develop
git merge release/2.1
git worktree remove ../release-2.1
git branch -d release/2.1
# Merge a hotfix into main and develop
cd ../main
git merge hotfix/login-crash
git tag -a v2.0.1 -m "Hotfix: login crash"
cd ../develop
git merge hotfix/login-crash
git worktree remove ../hotfix-login
git branch -d hotfix/login-crashBenefits Over Traditional Git Flow
- No branch switching. You never run
git checkoutorgit switchfor Git Flow operations. Navigate between branches by changing directories. - No stashing. Your feature work is always safe in its own directory. Handle hotfixes without touching your in-progress code.
- Parallel testing. Run the test suite on main, develop, and a feature branch simultaneously in separate terminal windows.
- Easy comparisons. Open main and develop in separate editor windows to compare behavior side by side.
- Shared object store. Despite having multiple checkouts, Git only stores each object once. Disk usage is far lower than multiple clones.
- Clean mental model. Each directory is one branch. You always know which branch you are on by looking at your current directory.
Practical Tips
- Use the bare repo pattern.It avoids the asymmetry of having one “special” worktree and makes the directory structure cleaner. See the bare repo guide.
- Name worktrees by branch purpose, not full branch name. Use
feature-authinstead offeature-auth-oauth2-migration-v2. Keep it short. - Remove worktrees immediately after merging. Git Flow already has many branches; do not let stale worktrees pile up. Run
git worktree listregularly. - Set up your IDE for multi-root workspaces. VS Code multi-root workspaces let you open main, develop, and feature worktrees in a single window, each in its own folder.
- Automate dependency installation. For JavaScript projects, each worktree needs its own
node_modules. Use a shell helper to handle this automatically. - Consider whether you need full Git Flow. Many teams find that trunk-based development with worktrees is simpler and achieves the same goals. Use Git Flow + worktrees if your release process genuinely requires the extra branches.