Skip to content
berry.sh
Go back

Codex + git worktrees: avoiding index.lock in multi-agent workflows

Edit page

TL;DR

Git worktrees give you multiple working directories, but they still share one underlying repository. When multiple processes run write commands at the same time, they collide on Git lock files (.git/index.lock, ref locks, etc.).

Treat Git as a single-writer system:

Why worktrees + concurrent processes fail

Worktrees are lightweight because they share the .git/ directory with the main worktree. That shared directory contains:

Git protects these shared structures with lockfiles during updates. If two processes try to update at once, you get errors like:

Symptoms

If any of these are familiar, you’re dealing with contention:

What’s safe vs unsafe

Generally unsafe / needs serialization

These commands write to index/refs/objects and should not run concurrently across worktrees:

Generally safe

Read-only operations (usually safe to run concurrently):

Pattern 1: serialize Git writes with a mutex

Create a small wrapper script and point your agents at it.

# git-safe

set -euo pipefail

LOCK_FILE="${HOME}/.cache/git-safe/$(pwd | shasum | cut -d' ' -f1).lock"
mkdir -p "$(dirname "$LOCK_FILE")"

(
  flock -x 200
  git "$@"
) 200>"$LOCK_FILE"

Usage:

git-safe fetch origin
# ...
git-safe add .
git-safe commit -m "Update"
git-safe push

This keeps your agents fully parallel inside their worktrees, while letting Git writes happen one at a time.

Pattern 2: a single “sync” process

Instead of letting every agent do git fetch / git pull, run a single sync process that:

  1. periodically runs git-safe fetch origin,
  2. optionally updates tracking branches,
  3. optionally pulls the branch each worktree tracks.

Everyone else works locally, using the repo state that sync refreshed.

Pattern 3: clones per worker (when you truly need parallel Git writes)

If you want full isolation, don’t use shared .git/ at all.

git clone --filter=blob:none --no-checkout <URL> worker-1
git clone --filter=blob:none --no-checkout <URL> worker-2

If disk usage is a concern, keep the shared/central repository and use shallow clones or partial clone filters.

Pattern 4: bootstrap worktrees so the agent doesn’t choke on local state

Worktrees don’t share:

When you create a worktree, immediately hydrate it:

cp ../main/.env .env
pnpm install

Whatever your stack requires, codify it in a worktree-init script and run it every time.

Recovering from a stale lock

If you hit lock errors:

  1. Check for active Git processes (don’t delete locks while another Git command is running).
  2. If it’s truly stuck and no Git processes remain, delete the lock file and rerun.
rm -f .git/index.lock

Helpful commands

List worktrees:

git worktree list

Create a worktree on a branch:

git worktree add ../wt-feature feature/my-feature

Remove a worktree once done:

git worktree remove ../wt-feature

Closing thought

Worktrees are great for multi-branch workflows and agents, but they share one .git/. Git is fine with multiple read-only operations, but you must serialize writes or isolate them with clones. That’s the entire problem, and the entire fix.


Edit page
Share this post:

Next Post
Build & Maintain Sitemap for Sitepress