Skip to content

Cloud agent env clones: use --filter=blob:none instead of --filter=tree:0 - #15281

Open
warp-agent-staging[bot] wants to merge 2 commits into
masterfrom
factory/app-5509-blobless-clone-filter
Open

Cloud agent env clones: use --filter=blob:none instead of --filter=tree:0#15281
warp-agent-staging[bot] wants to merge 2 commits into
masterfrom
factory/app-5509-blobless-clone-filter

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Description

Cloud agent environment prep clones every source repo as a treeless partial clone (git clone --filter=tree:0). --filter=tree:0 omits tree objects, not just blobs, so any history walk against a path (git log -- <path>, git log -p/--stat/--follow, git blame, git show) lazily refetches trees from the promisor remote once per commit visited. Each lazy fetch reprints a full clone-style progress banner (remote: Enumerating objects… Receiving objects…), which is indistinguishable from a repo being cloned in a loop, and the command effectively never finishes.

This was reported in Slack after a factory run appeared to hang with continuous "cloning" output while running git log. Root-caused and tracked in APP-5509.

This PR switches the clone and the matching pinned-ref fetch from --filter=tree:0 to --filter=blob:none. Blobless clones still omit file contents (most of the setup-time win is preserved), but they carry all trees, so path-limited history, git blame, and git show --stat are fully local and no longer trigger lazy refetches.

Changes

  • app/src/ai/agent_sdk/driver/environment.rs: switch all four --filter=tree:0 sites (parallel clone, parallel pinned-ref fetch, single-repo clone, single-repo pinned-ref fetch in checkout_command_for) to --filter=blob:none.
  • Updated the comments/doc comments that named the filter or reasoned about treeless behavior to stay accurate. (Revised after review: the original wording claimed a partial clone "only fetches the default branch", which is false — a plain git clone fetches all remote branches by default. The real reason the pinned-ref fetch is needed is that the requested SHA, branch, or tag may not have existed yet, or may have moved, by the time the clone ran.)
  • Updated app/src/ai/agent_sdk/driver/environment_tests.rs assertions and the real-git fixture helper (partial_clone) to match the new filter string.
  • Added blobless_clone_walks_path_limited_history_without_network: a regression test that clones with blob:none, repoints origin at an unreachable URL, and asserts a path-limited git log still completes locally. It fails fast if the filter regresses to tree:0 (verified manually).

Rollout shape: the clone path skips repos whose directory already exists ("Repository directory already exists, skipping clone..."). Any already-provisioned or snapshotted environment will keep its existing treeless clone until it is re-created from scratch — this change only affects newly-created environments.

Linked Issue

Testing

  • ./script/format --check and cargo clippy -p warp --all-targets --tests -- -D warnings pass.
  • cargo nextest run -p warp -E 'test(parallel_clone) or test(checkout_command) or test(checkout_result) or test(single_repo_checkout) or test(blobless_clone) or test(no_checkout_ref)': 13/13 pass, including the real-git fixture tests that exercise the actual fetch-then-checkout commands and the new regression test.

Filter tradeoff measurements

Fresh clones of both repos, run back-to-back from this environment (network conditions vary run to run, but the relative deltas are representative):

Repo Filter Clone wall time On-disk size (working tree) .git size
warpdotdev/warp tree:0 6.4s 527M 137M
warpdotdev/warp blob:none 20.5s 534M 144M
warpdotdev/warp-server tree:0 16.7s 229M 77M
warpdotdev/warp-server blob:none 9.9s 250M 97M

.git size grows modestly (+5% for warp, +26% for warp-server) since trees are now retained. Clone wall time is noisier — network variance dominated the small absolute times here — but the on-disk cost of carrying trees is small relative to the multi-hundred-MB working tree either way.

Demonstrating the fix

Before (in a --filter=tree:0 clone of warp-server):

$ timeout 30 script -qec "git --no-pager log --oneline -2 -- go.mod" before.log
Session terminated, killing shell... ...killed.   # exit 124, timed out
$ grep -c "remote: Enumerating" before.log
56

30 seconds, killed by timeout, 56 "cloning-looking" refetch banners for a query that should return instantly.

After (in a --filter=blob:none clone of the same repo):

$ time git --no-pager log --oneline -2 -- go.mod
0f08b5bca Server-owned Factory file schemas, registry and validation (REMOTE-2868) (#15209)
1177bee97 Wire per-category charged usage breakdown into RequestCharges/ConversationUsageMetadata (#14922)

real    0m0.005s
$ grep -c "remote: Enumerating" after.log
0

5 milliseconds, correct output, zero refetch chatter.

I also reproduced the original symptom directly: this very /workspace/warp checkout is itself a tree:0 clone (remote.origin.partialclonefilter=tree:0), and running git log -- app/src/ai/agent_sdk/driver/environment.rs against it produced the same runaway refetch output described in the report.

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

CHANGELOG-BUG-FIX: Cloud agent environments no longer hang with runaway "cloning" output when running git log, git blame, or other path-limited history commands.

…ee:0

Treeless partial clones (--filter=tree:0) omit tree objects, so any
history walk against a path (git log -- <path>, git log -p/--stat/
--follow, git blame, git show) lazily refetches trees from the
promisor remote once per commit visited. Each fetch reprints a full
clone-style progress banner, which looks like repos being cloned in
a loop, and the command effectively never finishes.

Switch the clone and the matching pinned-ref fetch to
--filter=blob:none. Blobless clones still omit file contents, so
setup keeps most of its speed benefit, but they carry all trees,
which makes path-limited history, git blame, and git show --stat
fully local.

Fixes APP-5509.
@warp-agent-staging

Copy link
Copy Markdown
Contributor Author

This PR was generated with Warp.

Comment @warp-factory on this PR to send it follow-up work.

View run View conversation

@danielpeng2
danielpeng2 marked this pull request as ready for review August 18, 2026 19:51
@danielpeng2
danielpeng2 enabled auto-merge (squash) August 18, 2026 19:52

@warp-agent-staging warp-agent-staging Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

Switches cloud agent environment clones from --filter=tree:0 to --filter=blob:none so path-limited history stops refetching trees per commit. Two further findings (an inaccurate doc-comment rationale and missing regression coverage) are already back with the author for revision; the one below needs your decision, so this review is neither an approval nor a rejection.

Concerns

  • The clone step skips repo directories that already exist, so every already-provisioned or snapshotted environment keeps remote.origin.partialclonefilter=tree:0 indefinitely — including the environment whose run triggered this fix. Only newly created environments get the fix, and the PR records that gap without a migration or a tracked follow-up. Decide whether environment recreation is an acceptable rollout boundary: if it is, this needs a communicated operational follow-up, and if it is not, the clone path needs safe detection and reprovisioning of existing treeless clones without disturbing user-owned working trees.

Verdict

Checks: build pass, tests pass (author-run; the reviewer's local cargo nextest was killed by a runner memory limit), CI pending (15 passed, 10 skipped, 6 pending, none failing), visual proof n/a

Found: 0 critical, 0 important, 0 suggestions, 0 nits, 1 question

Responding as wilson: Open session · View factory task

@danielpeng2
danielpeng2 disabled auto-merge August 18, 2026 20:01
Review found two issues in the initial blob:none change:

- The doc comment on checkout_command_for (and the matching comment
  in clone_repo) claimed a partial clone 'only fetches the default
  branch', which is false: a plain git clone fetches all remote
  branches by default. The real reason the pinned-ref fetch is
  needed is that the requested SHA, branch, or tag may not have
  existed yet, or may have moved, by the time the clone ran.

- None of the existing tests would catch a regression back to
  --filter=tree:0: the string-matching assertions just pin the
  literal filter value, and the real-git fixture tests only exercise
  fetch-then-checkout, which passes under either filter. Added
  blobless_clone_walks_path_limited_history_without_network, which
  clones with blob:none, repoints origin at an unreachable URL, and
  asserts a path-limited git log still completes locally. Verified
  manually that flipping the test's clone filter back to tree:0 makes
  it fail (git tries to fetch a missing tree from the now-unreachable
  promisor remote and errors out immediately).
@danielpeng2
danielpeng2 enabled auto-merge (squash) August 18, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant