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 4d ago
2 unresolved threads · Jump to file
Selected thread: apps/server/src/vcs/GitVcsDriver.ts:825 — **<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> 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](https://github.com/pingdotgg/t3code/blob/c9f15114030de60ff4898433fa99c7dd7323807c/AGENTS.md#L145-L145) Useful? React with 👍 / 👎.
0 / 5 viewed5 files changedLine totals unavailable

apps/server/src/vcs/GitVcsDriver.ts · Earlier revision c9f15114 · right 825

Original diff
@@ -789,26 +802,62 @@ export const makeVcsDriverShape = Effect.fn("makeGitVcsDriverShape")(function* (
             yield* execute({
               operation,
               cwd: input.cwd,
-              args: ["-c", "core.fsmonitor=false", "read-tree", "--reset", "HEAD"],
+              args: [...indexConfig, "read-tree", "--reset", "HEAD"],
               env: commitEnv,
             });
             // read-tree can rewrite the index, so restore its racy timestamp afterward.
             yield* fileSystem.utimes(tempIndexPath, indexTime, indexTime);
-            const entries = yield* execute({
+            let specialFlags = false;
+            let recordStart = true;
+            yield* vcsProcess.run({
               operation,
+              command: "git",
               cwd: input.cwd,
-              args: ["ls-files", "-v"],
+              args: [...indexConfig, "ls-files", "--sparse", "-v", "-z"],
               env: commitEnv,
-              maxOutputBytes: WORKSPACE_FILES_MAX_OUTPUT_BYTES,
+              maxOutputBytes: 4_096,
+              outputMode: "truncate",
+              // Inspect every tag without retaining paths or stopping at the output cap.
+              onStdoutChunk: (chunk) => {
+                for (const byte of chunk) {
+                  if (
+                    recordStart &&
+                    ((byte >= 97 && byte <= 122) || (!sparseCheckout && byte === 83))
C

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 👍 / 👎.

V
vedprakash2302Sep 16, 2026

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.ts · Earlier revision c9f15114 · right 870

Original diff
@@ -817,14 +866,15 @@ export const makeVcsDriverShape = Effect.fn("makeGitVcsDriverShape")(function* (
         yield* execute({
           operation,
           cwd: input.cwd,
-          args: ["add", "-A", "--", "."],
+          // Preserve absent skipped entries, but capture present nonignored files outside the cone.
+          args: [...indexConfig, "add", ...(sparseCheckout ? ["--sparse"] : []), "-A", "--", "."],
C

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 👍 / 👎.

V
vedprakash2302Sep 16, 2026

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.