Git Worktrees#
All repositories are set up as bare clones with worktrees. This enables parallel work — multiple agents (or a human and an agent) can work on different issues in the same repository simultaneously without conflicts, stashing, or context-switching.
Why worktrees#
| Problem with traditional clones | How worktrees solve it |
|---|---|
| Only one branch checked out at a time | Each issue gets its own worktree — parallel by default |
| Switching branches requires clean state | Worktrees are independent — no stashing or committing WIP |
| Agent work blocks human work on same repo | Different worktrees, no interference |
| Default branch gets dirty during development | <default>/ worktree is always a clean reference |
Repository layout#
<repo-root>/
├── .bare/ # bare git data (the actual repository)
├── .git # file containing: gitdir: ./.bare
├── <default>/ # worktree: default branch (always clean, never worked in directly)
├── 42-add-pagination/ # worktree: issue #42 in progress
└── 99-fix-null-ref/ # worktree: issue #99 in progress
.bare/— the shared git object store. All worktrees share this..git— a file (not a directory) that points git tooling to.bare/.<default>/— the default branch worktree (e.g.mainormaster). Kept as a clean reference. Used for diffing, reading docs, running comparisons. Never directly committed to.<N>-<slug>/— one worktree per issue in flight. Named by issue number and a short slug. Branch name matches the folder name.
Remotes#
Every repository has exactly two remotes (or one, if it is not a fork):
| Remote | Points to | Required | Purpose |
|---|---|---|---|
origin |
Our copy on the server | Always | Push branches, open PRs, CI runs against this. |
upstream |
The parent repo (forks only) | Forks | Track upstream changes, sync the default branch. |
No other remotes are added. This keeps the model simple and predictable for both humans and agents.
How it works in practice#
- Non-fork repos — only
originexists. Branches are pushed toorigin, PRs are opened againstorigin. - Forked repos —
originis our fork,upstreamis the original repository. The default branch tracksupstreamfor syncing; feature branches are pushed tooriginand PRs are opened fromoriginintoupstream.
Fetch configuration#
Both remotes are configured with full refspecs so git fetch --all --prune keeps everything current:
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
[remote "upstream"] # forks only
fetch = +refs/heads/*:refs/remotes/upstream/*
Setup (one-time per repository)#
# Clone as bare into .bare/
git clone --bare https://github.com/<owner>/<repo>.git .bare
# Create the .git pointer file
Set-Content .git "gitdir: ./.bare" -NoNewline
# Configure fetch refspec (bare clones don't set this automatically)
git -C .bare config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
# Fetch remote branches
git -C .bare fetch origin
# Determine the default branch
$defaultBranch = git -C .bare symbolic-ref HEAD | ForEach-Object { $_ -replace 'refs/heads/', '' }
# Create the default branch worktree
git -C .bare worktree add "../$defaultBranch" $defaultBranch
# Set upstream tracking (prevents "Publish Branch" prompt in VS Code)
git -C .bare config "branch.$defaultBranch.remote" origin
git -C .bare config "branch.$defaultBranch.merge" "refs/heads/$defaultBranch"
The Checkout-GitHubRepo script automates this for all repositories.
Working on an issue#
# From the repo root (where .bare/ lives)
$defaultBranch = git -C .bare symbolic-ref HEAD | ForEach-Object { $_ -replace 'refs/heads/', '' }
git -C .bare worktree add ../42-add-pagination -b 42-add-pagination $defaultBranch
# Set upstream tracking (prevents "Publish Branch" prompt in VS Code)
git -C .bare config branch.42-add-pagination.remote origin
git -C .bare config branch.42-add-pagination.merge refs/heads/42-add-pagination
# Open in VS Code
code 42-add-pagination
Then follow the normal Implement flow: initial commit → push → draft PR → build → finalize.
Cleanup after merge#
# Remove the worktree
git -C .bare worktree remove 42-add-pagination
# Delete the local branch ref
git -C .bare branch -D 42-add-pagination
# Prune if needed (removes stale worktree references)
git -C .bare worktree prune