Skip to content

Commit da4e6bc

Browse files
committed
fix(server): sparse worktrees read .gitmodules and t3.json from HEAD
Sparse patterns can leave .gitmodules and t3.json out of the new working tree. The submodule step checked both on disk, so it skipped submodules and ignored worktreeSubmodules. It now asks git for HEAD's copies, which are the same files in a full checkout. The docs drop the "keep t3.json inside the patterns" workaround and note the extensions.worktreeConfig change and the Git 2.35 floor.
1 parent 14a52f8 commit da4e6bc

3 files changed

Lines changed: 38 additions & 26 deletions

File tree

‎apps/server/src/vcs/GitVcsDriverCore.test.ts‎

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3398,29 +3398,32 @@ it.layer(layerTest)("GitVcsDriver core integration", (it) => {
33983398
true,
33993399
);
34003400

3401-
// Sparse patterns that leave out .gitmodules still check out the submodules.
3402-
yield* writeTextFile(cwd, "sparse-patterns.txt", "/shared/\n/t3.json\n");
3403-
yield* writeTextFile(
3404-
cwd,
3405-
"t3.json",
3406-
`{ "worktreeSparsePatternsPath": "sparse-patterns.txt" }`,
3407-
);
3408-
yield* git(cwd, ["add", "."]);
3409-
yield* git(cwd, ["commit", "-m", "sparse patterns"]);
3410-
const sparsePath = pathService.join(
3411-
pathService.dirname(worktreePath),
3412-
"sparse-submodule-worktree",
3413-
);
3414-
yield* driver.createWorktree({
3415-
cwd,
3416-
path: sparsePath,
3417-
refName: initialBranch,
3418-
newRefName: "feature/sparse-submodules",
3401+
// Sparse patterns that leave out .gitmodules and t3.json still honor both.
3402+
yield* writeTextFile(cwd, "sparse-patterns.txt", "/shared/\n");
3403+
const createSparse = Effect.fn(function* (projectFile: string, branch: string) {
3404+
yield* writeTextFile(cwd, "t3.json", projectFile);
3405+
yield* git(cwd, ["add", "."]);
3406+
yield* git(cwd, ["commit", "-m", branch]);
3407+
const sparsePath = pathService.join(pathService.dirname(worktreePath), branch);
3408+
yield* driver.createWorktree({
3409+
cwd,
3410+
path: sparsePath,
3411+
refName: initialBranch,
3412+
newRefName: branch,
3413+
});
3414+
assert.isFalse(yield* fileSystem.exists(pathService.join(sparsePath, ".gitmodules")));
3415+
assert.isFalse(yield* fileSystem.exists(pathService.join(sparsePath, "t3.json")));
3416+
return yield* fileSystem.exists(pathService.join(sparsePath, "shared", "SHARED.md"));
34193417
});
34203418

3421-
assert.isFalse(yield* fileSystem.exists(pathService.join(sparsePath, ".gitmodules")));
34223419
assert.isTrue(
3423-
yield* fileSystem.exists(pathService.join(sparsePath, "shared", "SHARED.md")),
3420+
yield* createSparse(`{ "worktreeSparsePatternsPath": "sparse-patterns.txt" }`, "sparse"),
3421+
);
3422+
assert.isFalse(
3423+
yield* createSparse(
3424+
`{ "worktreeSparsePatternsPath": "sparse-patterns.txt", "worktreeSubmodules": "none" }`,
3425+
"sparse-none",
3426+
),
34243427
);
34253428
}),
34263429
);

‎apps/server/src/vcs/GitVcsDriverCore.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3963,9 +3963,16 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*
39633963
options?.submodules ?? null,
39643964
options?.submodules != null
39653965
? null
3966-
: yield* fileSystem.readFileString(path.join(worktreePath, T3_PROJECT_FILE_NAME)).pipe(
3967-
Effect.flatMap((contents) => {
3968-
const file = parseT3ProjectFile(contents);
3966+
: // Read HEAD for the same reason: sparse patterns can leave t3.json out too.
3967+
yield* executeGit(
3968+
"GitVcsDriver.createWorktree.readProjectFile",
3969+
worktreePath,
3970+
["show", `HEAD:${T3_PROJECT_FILE_NAME}`],
3971+
{ allowNonZeroExit: true },
3972+
).pipe(
3973+
Effect.flatMap((result) => {
3974+
if (result.exitCode !== 0) return Effect.succeed(null);
3975+
const file = parseT3ProjectFile(result.stdout);
39693976
return file === null
39703977
? Effect.logWarning("t3.json is invalid; initializing submodules recursively", {
39713978
worktreePath,

‎docs/user/project-settings.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,11 @@ applies when the project and environment are both on **Inherit**.
171171
New worktrees check out every file. For a large repository, set `"worktreeSparsePatternsPath"` in
172172
`t3.json` to a repository-relative file of git sparse-checkout patterns in non-cone syntax. New
173173
worktrees then check out only the matching files, before any other file is written. Both files are
174-
read from the branch being checked out, so keep `t3.json` inside the patterns. If the pattern file
175-
is missing, creating the worktree fails instead of checking out everything. Submodules still follow
176-
**Submodules** above, not the patterns; set it to **Skip** to leave them empty.
174+
read from the branch being checked out, so the patterns can leave out `t3.json` and `.gitmodules`.
175+
If the pattern file is missing, creating the worktree fails instead of checking out everything.
176+
Submodules still follow **Submodules** above, also under excluded paths; set it to **Skip** to leave
177+
them empty. The first sparse worktree turns on Git's `extensions.worktreeConfig` for the repository.
178+
This needs Git 2.35 or newer.
177179

178180
## Worktree location
179181

0 commit comments

Comments
 (0)