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 sets the fetch refspec to only the default branch. Updating 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 logorgit statusin the bare directory — you mustcdinto a worktree first. - 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.