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
The layout on diskA project directory contains a hidden bare repository holding all objects and refs, a .git file pointing at it, and one sibling directory per worktree. Main is a worktree like any other, so every working tree can be added and removed the same way.~/src/app/.bare/Bare repositoryobjects, refs, config.gitFilegitdir: ./.baremain/, feature-x/, …Worktreesall equalno tree is special β€” remove any of them with git worktree remove

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
Normal layout versus bare layoutIn the normal layout the main checkout holds the repository and is special, and extra worktrees sit beside it. In the bare layout the repository is hidden and every working tree, main included, is an equal sibling. The bare layout needs a fetch refspec fix and a little more setup, and pays off when many trees stay open.NormalBare + worktreesmain checkoutspecial, holds .gitjust another worktreeremove any treenot the main oneyessetupgit cloneclone --bare + refspec fixbest foroccasional extra treemany trees open alwaysthe refspec fix is the step most guides leave out

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"
Setting up the bare layoutClone bare into a hidden directory, add a .git file pointing at it, fix the fetch refspec and fetch, delete the copied local branches, and add the default branch as the first worktree with its upstream set.clone --bare.bare/.git filegitdir: ./.bareFix refspecthen fetchDrop copieslocal branchesAdd mainset upstreamscript it once; every repository then looks the same

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.