Git Bare Repository with Worktrees
Combining a bare clone with git worktree is a popular pattern that eliminates the concept of a “main” checkout directory. Every branch lives in its own worktree, and the bare repo is purely a shared object store.
What Is a Bare Repo?
A bare repository contains only the Git database — the objects, refs, and configuration — without a working tree. It is what you get on a Git server, and it is what git clone --bare creates locally.
# A normal clone has a working tree + .git directory
myapp/
|-- .git/ # Git database
|-- src/ # working tree
+-- README.md
# A bare clone has only the Git database
myapp.git/
|-- HEAD
|-- config
|-- objects/
|-- refs/
+-- ... # no working treeBecause a bare repo has no working tree, you cannot edit files in it directly. That is exactly the point — you add worktrees for every branch you want to work on.
Why Use Bare + Worktrees
With a regular clone, your main checkout is “special” — it holds the .git directory and all other worktrees depend on it. If you accidentally delete it, all linked worktrees break. The bare repo pattern avoids this asymmetry:
- No “main directory” confusion. The bare repo is clearly just infrastructure. Every branch you work on lives in an equally-weighted worktree.
- Clean mental model. You never accidentally edit files in the bare repo because there are no files to edit.
- Easy to reorganize.You can add, remove, and move worktrees without worrying about disrupting the “primary” checkout.
- Consistent directory structure. Every checkout follows the same pattern, making scripts and editor configurations simpler.
Setup Step-by-Step
Follow these steps to set up a bare repository with worktrees from scratch.
# 1. Create a parent directory for the project
mkdir ~/projects/myapp && cd ~/projects/myapp
# 2. Clone the repository as bare
git clone --bare [email protected]:you/myapp.git myapp.git
# 3. Configure the bare repo to fetch all remote branches
cd myapp.git
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch origin
# 4. Create worktrees for the branches you need
git worktree add ../main main
git worktree add ../develop develop
git worktree add -b feature/auth ../feature-authThe key step is number 3. By default, git clone --bare does not set up remote-tracking branches under refs/remotes/origin/*, so branches are not tracked in the conventional way. Setting remote.origin.fetch ensures you can fetch and track all remote branches.
# Verify the setup
$ git worktree list
/home/dev/projects/myapp/myapp.git (bare)
/home/dev/projects/myapp/main abc1234 [main]
/home/dev/projects/myapp/develop def5678 [develop]
/home/dev/projects/myapp/feature-auth ghi9012 [feature/auth]Pros and Cons
Pros
- No asymmetry between “main” and “linked” worktrees — all worktrees are equal.
- Accidentally deleting a worktree does not break the Git database.
- Cleaner directory structure with a clear separation between the repo and the checkouts.
- Ideal for developers who always work in worktrees and never need a default branch checkout.
Cons
- Slightly more setup than a regular clone — you need to configure the fetch refspec manually.
- Some tools and IDE integrations expect a regular clone and may not recognize the bare repo as a project root.
- You cannot run
git statusin the bare directory (it requires a working tree). Commands likegit logwork fine, but most workflows require a worktree. - Team members unfamiliar with the pattern may be confused by the directory layout initially.
Directory Structure Example
A fully set up project using the bare repo pattern looks like this:
~/projects/myapp/
|-- myapp.git/ # bare repository (Git database only)
| |-- HEAD
| |-- config
| |-- objects/
| |-- refs/
| +-- worktrees/ # metadata for linked worktrees
| |-- main/
| |-- develop/
| +-- feature-auth/
|-- main/ # worktree: main branch
| |-- .git # file pointing to myapp.git/worktrees/main
| |-- src/
| +-- package.json
|-- develop/ # worktree: develop branch
| |-- .git
| |-- src/
| +-- package.json
+-- feature-auth/ # worktree: feature/auth branch
|-- .git
|-- src/
+-- package.jsonWhen to Use This Pattern
The bare repo + worktrees pattern works best when:
- You routinely work on multiple branches simultaneously and want every branch to be a first-class worktree.
- You use a branching model like Git Flow where you always have main, develop, and feature branches active.
- You want a clean separation between the Git database and your working directories.
- You are setting up a new project from scratch and can adopt the pattern from day one.
If you already have a regular clone with linked worktrees and it works for you, there is no need to convert. The bare repo pattern is an alternative, not a requirement.