On October 22, Claude Code 2.1.309 shipped a line that is easy to scroll past. It changed worktree creation and reuse "so git no longer fetches objects on demand, in ordinary clones and in partial clones with a Bash sandbox configured; such a partial clone fetches them first."

Git fetching objects on demand is a feature most developers never notice. In a blobless clone it happens every time you check out a commit whose files you haven't downloaded yet. Git opens a network connection in the middle of what looks like a local command, gets the files, and carries on.

That is fine on your laptop. It is a problem for an agent in a sandbox with no network.

What a blobless clone leaves out

Git stores three main kinds of objects: commits, trees (directory listings) and blobs (file contents). A blobless clone, git clone --filter=blob:none, downloads every reachable commit and tree but only the blobs needed for the checkout. In GitHub's guide to partial clone, Derrick Stolee calls blobless clones "the most widely-used partial clone option" and says they "are best for developers and build environments that span multiple builds."

The clone keeps a promise on record. Per the partial clone docs, a remote counts as a promisor when the config sets remote.<name>.promisor = true, remote.<name>.partialCloneFilter, or extensions.partialClone. The packs it downloaded get a .promisor file next to them. Try it on a real repository:

git clone --filter=blob:none \ https://github.com/git/git.git cd git git config --get-regexp \ 'remote\.origin\.(promisor|partialclonefilter)' 

When a lookup can't find an object, the docs say "Git invokes promisor_remote_get_direct() to try to get the object from a promisor remote and then retry the object lookup." Objects are "faulted in," and "no check as to whether the missing object is actually a promisor object is performed." Any missing object becomes a network request.

A tall stack of folders with outlined, empty file slots, each tied by a dotted line to a distant server on the horizon
A blobless clone has every folder and none of the history's files. Each empty slot is a promise the remote has to keep.

Count what you don't have

git rev-list --missing=print walks the object graph and lists missing objects with a ? prefix instead of fetching them. The partial clone docs describe exactly this use: rev-list prints the missing objects so other commands can prefetch them in bulk.

# every missing object in the repo git rev-list --objects --all --missing=print \ | grep -c '^?' # missing objects in HEAD's history git rev-list --objects --missing=print HEAD \ | grep -c '^?' 

Your numbers depend on the day you clone, so I won't quote any. Expect the first count to be large: every historical version of every file in the repo, minus the versions your checkout needed.

Watch git reach for the network

Which commands trigger a fetch? The docs say checkout, and any other command built on unpack-trees, "has been taught to bulk pre-fetch all required missing blobs in a single batch." The git backfill docs add that commands which change HEAD, like checkout or merge, may need missing blobs, and that git blame can "become very slow" because it downloads them "in single-blob requests."

git worktree add checks out files too. Point it at a tag far behind HEAD, so many of its blobs aren't local, and turn on Trace2's performance format:

GIT_TRACE2_PERF=1 git worktree add --detach \ ../git-old v2.20.0 2>&1 | grep child_start 

Among the child processes, look for a git fetch. The arguments come from promisor-remote.c, which builds this command:

git -c fetch.negotiationAlgorithm=noop fetch origin --no-tags --no-write-fetch-head --recurse-submodules=no --filter=blob:none --stdin 

The object IDs go in on stdin. Nothing on your screen said "downloading." You asked for a worktree, and git went to GitHub for the files.

Now run the same thing with lazy fetching turned off. Git 2.45 added git --no-lazy-fetch, which the git manual says is "equivalent to setting the GIT_NO_LAZY_FETCH environment variable to 1." Pick a different old tag, since the first run already downloaded blobs for v2.20.0:

GIT_NO_LAZY_FETCH=1 git worktree add --detach \ ../git-offline v2.10.0 

The same source file shows what happens next. Git warns "lazy fetching disabled; some objects may not be available", skips the fetch, and dies with "could not fetch %s from promisor remote" for the first missing blob, before the worktree is usable. An agent with no network hits the same wall, except a real sandbox doesn't skip the fetch. It tries and gets blocked.

Why sandboxes and lazy fetch don't mix

Look at how Claude Code's sandbox handles the network. A sandboxed command has "no direct route out." Connections go through a proxy that checks each host against your allowed domains, "which start empty." What happens to a host that isn't on the list depends on your permission mode: a prompt in manual, acceptEdits and plan modes, a refusal in dontAsk mode. On macOS, git fetch against an SSH remote fails inside the sandbox even when the host is allowed.

Lazy fetch is a network call hidden inside a read. An agent running git checkout or git blame didn't ask for the network, so you get a permission prompt or a failure in the middle of a command that looked local.

Worktree creation makes this more likely than you'd think. Claude Code's worktree docs say new worktrees branch from the remote default branch by default, and if the repo "hasn't been fetched in the last 24 hours, it fetches the default branch." In a blobless clone, as the backfill docs put it, "later fetches download reachable commits and trees but no blobs." So the new worktree's starting commit is the one most likely to have files you don't have.

A sandboxed terminal window inside a walled box, with a fetch arrow bouncing off the wall before it reaches a server outside
Inside a sandbox with no network, every missing blob is a fetch that can't leave the box.

That is what 2.1.309 fixes: in a partial clone with a Bash sandbox configured, it fetches the objects first and then turns off fetching on demand. If the fetch can't complete, the docs list an error containing This repository is a partial clone that means Claude Code "couldn't fetch every git object the worktree's starting commit needs." The changelog doesn't say what happens in a partial clone with no sandbox configured.

The change also applies to ordinary clones, which have no promisor to fetch from unless their config says so. The same release made Claude Code's worktree checkouts ignore git replace refs. An earlier release stopped running filter drivers from the repository's own config during worktree creation, because "anything that can write to the repository, including Claude, could have put one there." Promisor settings live in that same .git/config. My reading, which Anthropic hasn't stated: turning off on-demand fetching closes one more path by which repository config can make git do something nobody asked for.

Prefetch before you hand the worktree over

If you start agents in your own worktrees, do the network work up front, while you still have network:

# 1. create the worktree outside the sandbox git worktree add -b agent/task ../task origin/main cd ../task # 2. only if the agent will read history git backfill # 3. after step 2, this should print 0 git rev-list --objects --missing=print HEAD \ | grep -c '^?' 

Step 1 is often enough. Checkout downloads the blobs for the new tip in one batch, so an agent that only edits files and runs tests has everything it needs.

Step 2 is for agents that run git blame, git log -p or diffs against old commits. git backfill arrived in Git 2.49, whose notes say lazy loading "tends to be one-blob-at-a-time" and that backfill helps "bulk-download necessary files beforehand." By default it downloads every blob reachable from HEAD, grouped by path, in batches of at least 50,000. --sparse limits it to your sparse-checkout. It is marked experimental, and it brings back most of what you skipped by cloning blobless, so run it only when the agent needs history. Step 3 confirms the backfill worked. Skip it if you skipped step 2, because without a backfill the count stays large.

Then start the agent with GIT_NO_LAZY_FETCH=1 in its environment. A missing object now fails right away with a clear message, instead of waiting on a proxy prompt nobody is watching. The alternative is to add your git host to the sandbox's allowedDomains, as the docs' own example does with github.com. That works, but every sandboxed command can then reach that host. Make that choice on purpose.

Doing it per task in Hermes IDE

I build Hermes IDE, so weigh this section with that in mind. Hermes runs each agent's own CLI in a real terminal, and ⌘N (Ctrl+Shift+N on Windows and Linux) starts every task on its own hermes/<name> branch in its own worktree.

A worktree recipe is where the prefetch goes. Hermes reads .hermes/worktree.toml and runs its setup commands in the new worktree, in order, before the session starts. For a blobless repo that needs history:

# .hermes/worktree.toml setup = ["git backfill", "npm ci"] 

The first command that fails stops the rest, and the failure goes to the inbox, so a backfill that couldn't reach the remote shows up in your inbox. Hermes shows you the commands the first time it sees the recipe and every time it changes, and nothing runs until you choose Run setup.

The Review Desk's Worktrees tab in Hermes IDE, listing one worktree each for orders-api, web-app and orders-types with branch, task, age and disk size
The Review Desk's Worktrees tab: one Hermes worktree each for orders-api, web-app and orders-types under ~/code, with each worktree's hermes/ branch, task, age and disk size, and the free disk space.

This matters most for the agents you run next to Claude Code. Since 2.1.309, Claude Code's own worktrees handle a partial clone when a sandbox is configured. I haven't checked how other agents' worktree features treat partial clones, and I haven't run this recipe against a large blobless repo myself.

If this was useful

Lazy fetch is lazy loading: fast to start, and only as available as the remote behind it. Prefetching trades a bigger download up front for reads that never touch the network. My System Design Pocket Guide: Fundamentals covers caching, latency and availability, the building blocks behind trade-offs like this one.

Hermes IDE is free, source-available under BSL 1.1, and runs on macOS, Windows and Linux. Download it or star it on GitHub. It isn't affiliated with Nous Research, Anthropic, OpenAI or any agent vendor.

System Design Pocket Guide: Fundamentals — Core Building Blocks for Scalable Systems