Bare repository and worktrees layout Jump to heading
The usual worktree setup has a main checkout with extra worktrees beside it, and the main checkout is special: it holds the .git directory, cannot be removed, and its branch is whatever you last left it on. An alternative layout removes the special case. Clone the repository bare into a hidden directory, and create every working tree β including main β as a linked worktree next to it. Every branch you work on is a sibling directory, all equal, all removable. The layout suits people who keep several branches open all the time. It needs one fix that catches almost everyone: bare clones have no fetch refspec, so remote-tracking branches never update until you add it. This page sets the layout up correctly, covers daily commands, and discusses when it is worth it, within Git worktrees for parallel development.
When to use this approach Jump to heading
- You keep several branches checked out at once, all the time.
- You want every working tree to be equal and removable.
- You script worktrees per task, as in scripting a worktree-per-ticket workflow.
- If you open a second worktree only occasionally, the normal layout is simpler.
Step 1 β Clone bare into a hidden directory Jump to heading
Create a project directory, clone bare into .bare inside it, and point a .git file at it so Git commands run from the project directory find the repository.
mkdir ~/src/app && cd ~/src/app
git clone --bare https://git.example.com/org/app.git .bare
echo "gitdir: ./.bare" > .git Step 2 β Fix the fetch refspec Jump to heading
A bare clone maps remote branches straight onto local branches and sets no fetch refspec, so git fetch does not create origin/* remote-tracking branches. Add the normal refspec and fetch.
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
git fetch origin
git branch -r | head
# origin/main
# origin/release/2.4 Without this, git status in worktrees cannot report ahead or behind, git pull behaves unexpectedly, and new remote branches never appear.
Step 3 β Remove the copied local branches Jump to heading
The bare clone created a local branch for every remote branch. You do not want hundreds of local branches; delete all except the default branch, which you will check out next.
git for-each-ref --format='%(refname:short)' refs/heads/ | grep -vx main | xargs -r git branch -D β οΈ SAFETY WARNING: Run this only right after the bare clone, when local branches are exact copies of the remote ones. On an existing repository it deletes local branches that may contain unpushed work.
Step 4 β Create worktrees for main and your branches Jump to heading
Add main as a worktree, set its upstream, and add other branches the same way.
git worktree add main main
git -C main branch --set-upstream-to=origin/main
git worktree add -b feature/rate-limits feature-rate-limits origin/main
git worktree list
# /home/me/src/app/.bare (bare)
# /home/me/src/app/main 1a2b3c4 [main]
# /home/me/src/app/feature-rate-limits 1a2b3c4 [feature/rate-limits] Step 5 β Daily commands Jump to heading
Run repository-wide commands from the project directory, and branch work inside each worktree.
cd ~/src/app
git fetch --all --prune # updates every worktree's remote-tracking refs
git worktree add review-1234 origin/pr-branch # quick review tree (detached)
cd main && git pull --ff-only
cd ../feature-rate-limits && git rebase origin/main Step 6 β Make tools find the repository Jump to heading
Most tools run from inside a worktree and find the repository through the worktreeβs .git file. Tools started from the project directory find it through the top-level .git file. A few tools expect a non-bare repository at the top; open them inside a worktree instead.
cd ~/src/app && git rev-parse --is-bare-repository # true (project directory)
cd main && git rev-parse --is-bare-repository # false (inside a worktree) Editor setup is covered in using worktrees with IDEs and language servers.
Step 7 β Script the setup Jump to heading
The setup is a handful of commands; script it so every repository follows the same layout.
#!/bin/sh
# usage: bare-clone <url> <dir>
set -eu
url=$1; dir=$2
mkdir -p "$dir" && cd "$dir"
git clone --bare "$url" .bare
echo "gitdir: ./.bare" > .git
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
git fetch origin
default=$(git symbolic-ref --short HEAD)
git for-each-ref --format='%(refname:short)' refs/heads/ | grep -vx "$default" | xargs -r git branch -D
git worktree add "$default" "$default"
git -C "$default" branch --set-upstream-to="origin/$default" Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
Why not just use the normal layout? Jump to heading
For one or two extra worktrees it is simpler. The bare layout helps when you always have several trees open and want none of them to be special.
Does pushing work normally? Jump to heading
Yes. Push from inside a worktree as usual. With the refspec fixed and upstreams set, git push and git pull behave as in a normal clone.
Can I convert an existing clone to this layout? Jump to heading
Clone again into the new layout, then move uncommitted work across with patches or by pushing branches. Converting in place is possible but error-prone.
Related Jump to heading
- Git Worktrees for Parallel Development β the parent topic.
- Worktrees vs Multiple Clones β choosing between the approaches.
- Cleaning Up Stale Worktrees Safely β removing trees you no longer need.
- Per-Worktree Config and Shared Hooks β settings in a many-worktree layout.