fix(server): keep large sparse checkouts on the fast checkpoint path#12154

merged3
main
GitHub fork: fix/sparse-checkpoint-capture
openedSep 16, 2026byVvedprakash2302
View on GitHubTwo-way sync enabledPR last imported 3d ago

Why

Fixes #12080.

Git already remembers enough about unchanged files to avoid reading them again. #10792 taught checkpoint capture to reuse that information, but two checks kept large sparse checkouts from benefiting:

  • The file listing could exceed 16 MiB, which made T3 throw away the copied index and start over.
  • Sparse checkout marks files it intentionally leaves off disk with skip-worktree. T3 treated those normal sparse-checkout flags as a reason to start over too.

So a checkpoint could reread a huge repository every turn and hit the 30-second timeout. Even if it finished, staging could fail just because an untracked file existed outside the selected sparse directories. Files left off disk could also look like deletions after the fallback.

What Changed

Keep the useful index information instead of throwing it away. The flag check now reads the complete output as a stream, so the buffer limit no longer decides whether reuse works. It can inspect sparse directory entries without expanding them into a full file listing.

Capture now accepts normal sparse-checkout flags and uses sparse-aware staging. Files intentionally absent stay in the snapshot. Present files, including nonignored additions outside the sparse rules, still get captured. The real index stays untouched, and a nested workspace still captures only its own changes.

The fallback rebuilds a sparse index for cone-mode checkouts. If it cannot safely rebuild a non-cone checkout's index, it fails rather than saving a snapshot with false deletions. The timestamp protections from #10792 stay in place.

Skipped file entries are checked against Git's sparse rules so a manually skipped in-cone deletion still gets captured. Git versions without add --sparse keep the previous capture path.

Results

On a generated 460,000-file repository with 230,000 files checked out, ten edits, and an 87.3 MB full listing:

Complete checkpoint captureBeforeAfter
Repository root9.82 s2.27 s
Nested workspace9.58 s2.27 s

That's about 4x faster. These are medians from three paired runs per workspace, alternating which version ran first. Every paired snapshot tree matched, and the real index stayed byte-for-byte identical. A separate capture with an out-of-cone untracked file succeeded in 12.03 s. These measurements include the fixes from automated review.

All measurements above use synthetic data on Linux/ext4. The 30-second subprocess timeout is unchanged. Large new or modified files still need to be read and hashed, so this does not fix every timeout covered by #3646.

70 focused tests pass, plus server typecheck, targeted lint, formatting, and diff checks. The regressions cover sparse snapshot contents, two consecutive captures, root and nested workspaces, safe fallback, and flags beyond the output cap. Clean-filter tests prove that truncation no longer causes unchanged files to be reread. Existing split-index and racy-timestamp tests still pass. A synthetic capture with a locally built Git 2.25.0 also passed and preserved the real index.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Implemented and verified with GPT-6 Astra through OpenCode, model github-copilot/gpt-6-astra.

Summary by CodeRabbit

  • New Features

    • Process output can now be streamed in full while buffered output remains capped, improving handling of large command results.
    • Large command results remain available through streamed byte chunks even when buffered output is truncated.
  • Bug Fixes

    • Improved checkpoint creation for sparse-checkout repositories, including more reliable index inspection and preservation of modified content.
    • Added compatibility handling for Git versions without sparse-index support.
    • Added clearer handling when checkpoints cannot be rebuilt for non-cone sparse checkouts.

Added 2 commits

  • fix(server): preserve sparse indexes during checkpoint capture6411198
  • fix(server): reject unsafe non-cone checkpoint fallbackc9f1511
Synced from GitHub

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

ReviewStatusCommitReview trigger
📝 Code Review✅ Completed 2026-09-16T23:30:44.375648Zc9f1511PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Synced from GitHub

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This server-side fix substantially changes the existing Git checkpoint pipeline, including sparse-index reuse, fallback behavior, and bounded process-output handling. The focused tests are valuable, but the cross-component runtime and Git-index semantics warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: c3e40d43-cc45-4d97-8ee3-a56b907f5fb4

📥 Commits

Reviewing files that changed from the base of the PR and between c9f15114030de60ff4898433fa99c7dd7323807c and 13c26084e1113a2027c464c3340efa8aa9bcf0fd.

📒 Files selected for processing (2)
  • apps/server/src/vcs/GitVcsDriver.test.ts
  • apps/server/src/vcs/GitVcsDriver.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

The change adds stdout chunk callbacks through the process and VCS layers. Checkpoint capture uses streaming index inspection and sparse-checkout-aware Git commands. Tests cover sparse indexes, non-cone checkout failures, index restoration, and output beyond buffer limits.

Changes

Checkpoint index capture

Layer / File(s)Summary
Stdout chunk streaming
apps/server/src/processRunner.ts, apps/server/src/vcs/VcsProcess.ts, apps/server/src/vcs/VcsProcess.test.ts
Process execution accepts an optional onStdoutChunk callback and forwards raw stdout chunks while retaining buffered output limits.
Sparse-checkout checkpoint handling
apps/server/src/vcs/GitVcsDriver.ts
Checkpoint capture detects sparse checkout, streams ls-files --sparse -v -z, applies shared index configuration, rejects non-cone rebuilds, and uses sparse-aware git add and read-tree commands.
Checkpoint and streaming validation
apps/server/src/vcs/GitVcsDriver.test.ts
Tests cover sparse and non-cone checkout states, index restoration, checkpoint content, truncated index output, and special index flags.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

mermaid
1sequenceDiagram
2 participant GitVcsDriver
3 participant VcsProcess
4 participant ProcessRunner
5 participant Git
6 GitVcsDriver->>VcsProcess: inspect checkpoint index
7 VcsProcess->>ProcessRunner: provide onStdoutChunk
8 ProcessRunner->>Git: run ls-files --sparse -v -z
9 Git-->>ProcessRunner: stream stdout chunks
10 ProcessRunner-->>VcsProcess: deliver chunks and buffered result
11 VcsProcess-->>GitVcsDriver: report index flags
12 GitVcsDriver->>Git: reuse or rebuild index
13 GitVcsDriver->>Git: add files and write tree

Suggested reviewers: im-kvijay

Merge Risk: ⚪ Minimal · up to 13c26

The sparse-checkout fallback remains able to capture checkpoints when index inspection options are unsupported, so no unresolved merge-blocking behavior is identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe PR addresses #12080. captureCheckpoint streams git ls-files --full-name --sparse -v -z output beyond the 16 MiB buffer and ignores normal sparse S records after checking them with `sparse-ch…
Out of Scope Changes check✅ PassedThe stdout chunk callbacks in ProcessRunner and VcsProcess directly enable the large-output checkpoint inspection required by #12080. The added tests validate the streaming behavior and checkpoint…
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 5…
Title check✅ PassedThe title clearly identifies the main change: preserving the fast checkpoint path for large sparse checkouts.
Description check✅ PassedThe description explains what changed and why, includes relevant validation results, and completes the applicable checklist items. UI-specific sections are not required because this PR does not change…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

Cchatgpt-codex-connector[bot]reviewed · 2 inline threadsSep 16, 2026
Synced from GitHub

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c9f1511403

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

apps/server/src/vcs/GitVcsDriver.tsline 825Outdated · c9f1511Open thread
……@@ -789,26 +802,62 @@ export const makeVcsDriverShape = Effect.fn("makeGitVcsDriverShape")(function* (
822+ for (const byte of chunk) {
823+ if (
824+ recordStart &&
825+ ((byte >= 97 && byte <= 122) || (!sparseCheckout && byte === 83))

P2 Badge Reset manual skip-worktree entries in sparse checkouts

When a sparse checkout also has a manually skip-worktree path inside the selected cone and that file is deleted, this condition treats its S record as an ordinary sparse exclusion. I reproduced this with git sparse-checkout set --cone included, git update-index --skip-worktree included/manual, and then deleting the file: git ls-files --sparse -v still reports S included/manual, while git add --sparse -A—whose -h text only says it permits updating entries outside the sparse-checkout cone—leaves the file in write-tree. The checkpoint therefore silently retains the deleted file and can resurrect it during restore; distinguish sparse-derived S entries from manual flags or reset the latter before reusing the index.

AGENTS.md reference: AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

Confirmed and fixed in 13c26084e. Added the reported manual skip-worktree deletion to the root and nested two-capture regressions; both failed before the fix. Capture now checks skipped file paths against Git sparse-checkout check-rules and falls back if a skipped file is inside the selected rules. Collapsed sparse directories do not need that extra check. The 460,000-file synthetic benchmark still shows about 4x faster captures.

apps/server/src/vcs/GitVcsDriver.tsline 870Outdated · c9f1511Open thread
……@@ -817,14 +866,15 @@ export const makeVcsDriverShape = Effect.fn("makeGitVcsDriverShape")(function* (
819868 cwd: input.cwd,
820- args: ["add", "-A", "--", "."],
869+ // Preserve absent skipped entries, but capture present nonignored files outside the cone.
870+ args: [...indexConfig, "add", ...(sparseCheckout ? ["--sparse"] : []), "-A", "--", "."],

P2 Badge Fall back when Git predates add --sparse

git add --sparse is unavailable in Git 2.25 even though that release supports git sparse-checkout; its git-add help does not list the option, while current help describes it as allowing updates outside the sparse-checkout cone. On such server or remote environments, the preceding unsupported ls-files --sparse invocation merely selects the fallback path, but this uncaught command then exits with unknown option 'sparse', so every sparse-checkout checkpoint fails. Since docs/user/source-control.md:8 requires Git without declaring a newer minimum, feature-detect this option or retain a compatible staging path.

AGENTS.md reference: AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

Confirmed and fixed in 13c26084e. Capture checks whether git add supports --sparse and retains the previous capture path if it does not. Added a regression that rejects unsupported sparse options. I also built Git 2.25.0 locally and ran a synthetic sparse-workspace capture through the driver: the edit was captured and the real index stayed byte-identical. This preserves compatibility with the old path; it does not backport modern sparse-index semantics to Git 2.25.

Added 1 commit

  • fix(server): handle manual sparse flags and older Git13c2608