Revert "Carry local files and shared state into new worktrees (#2419)"

This reverts commit cbbf6c1684.
This commit is contained in:
Mohamed Boudra
2026-07-30 00:37:52 +02:00
parent 20e46e3e3f
commit c6df5ffd4d
13 changed files with 145 additions and 2725 deletions
@@ -1,635 +0,0 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
existsSync,
chmodSync,
lstatSync,
linkSync,
mkdirSync,
mkdtempSync,
readdirSync,
readFileSync,
readlinkSync,
realpathSync,
rmSync,
symlinkSync,
writeFileSync,
} from "fs";
import { tmpdir } from "os";
import { dirname, join, relative } from "path";
import { isPlatform } from "../test-utils/platform.js";
import { materializeWorktreeIncludePlan, readWorktreeIncludePlan } from "./worktree-include.js";
describe("worktree include planning", () => {
let tempDir: string;
let sourceRoot: string;
beforeEach(() => {
tempDir = mkdtempSync(join(tmpdir(), "worktree-include-test-"));
sourceRoot = join(tempDir, "source");
mkdirSync(sourceRoot);
});
afterEach(() => {
rmSync(tempDir, { recursive: true, force: true });
});
it("copies bare entries and accepts explicit copy and symlink modes", async () => {
writeFileSync(
join(sourceRoot, ".worktreeinclude"),
[".env.local", ".cache/**", "symlink shared-state", "copy packages/*/.runtime.env", ""].join(
"\n",
),
);
writeFileSync(join(sourceRoot, ".env.local"), "source\n");
mkdirSync(join(sourceRoot, ".cache"), { recursive: true });
writeFileSync(join(sourceRoot, ".cache", "state.txt"), "cache\n");
mkdirSync(join(sourceRoot, "shared-state"), { recursive: true });
writeFileSync(join(sourceRoot, "shared-state", "state.txt"), "shared\n");
mkdirSync(join(sourceRoot, "packages", "api"), { recursive: true });
writeFileSync(join(sourceRoot, "packages", "api", ".runtime.env"), "api\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toHaveLength(4);
expect(plan.materializations).toEqual(
expect.arrayContaining([
expect.objectContaining({ mode: "copy", relativePath: ".env.local", sourceKind: "file" }),
expect.objectContaining({ mode: "copy", relativePath: ".cache", sourceKind: "directory" }),
expect.objectContaining({
mode: "symlink",
relativePath: "shared-state",
sourceKind: "directory",
}),
expect.objectContaining({
mode: "copy",
relativePath: "packages/api/.runtime.env",
sourceKind: "file",
}),
]),
);
});
it("skips invalid and conflicting entries while retaining safe entries", async () => {
writeFileSync(join(sourceRoot, "shared"), "source\n");
writeFileSync(join(sourceRoot, ".env"), "source\n");
writeFileSync(
join(sourceRoot, ".worktreeinclude"),
["../outside", "symlink", "shared", "symlink shared", ".env", ""].join("\n"),
);
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([
expect.objectContaining({ mode: "copy", relativePath: ".env", sourceKind: "file" }),
]);
expect(plan.skipped).toEqual(
expect.arrayContaining([
expect.objectContaining({ lineNumber: 1, raw: "../outside", reason: "invalid" }),
expect.objectContaining({ lineNumber: 2, raw: "symlink", reason: "invalid" }),
expect.objectContaining({ lineNumber: 3, raw: "shared", reason: "conflict" }),
expect.objectContaining({ lineNumber: 4, raw: "symlink shared", reason: "conflict" }),
]),
);
});
it("skips missing entries while retaining existing literal and glob matches", async () => {
writeFileSync(
join(sourceRoot, ".worktreeinclude"),
[".env", ".env.local", "config/*.env", "missing/**", ""].join("\n"),
);
writeFileSync(join(sourceRoot, ".env"), "source\n");
mkdirSync(join(sourceRoot, "config"), { recursive: true });
writeFileSync(join(sourceRoot, "config", "runtime.env"), "runtime\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([
expect.objectContaining({ mode: "copy", relativePath: ".env", sourceKind: "file" }),
expect.objectContaining({
mode: "copy",
relativePath: "config/runtime.env",
sourceKind: "file",
}),
]);
expect(plan.skipped).toEqual(
expect.arrayContaining([
expect.objectContaining({ raw: ".env.local", reason: "missing" }),
expect.objectContaining({ raw: "missing/**", reason: "missing" }),
]),
);
});
it("requires the optimized trailing recursive form to resolve to a directory", async () => {
writeFileSync(join(sourceRoot, "cache"), "not-a-directory\n");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "cache/**\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([]);
expect(plan.skipped).toEqual([expect.objectContaining({ raw: "cache/**", reason: "unsafe" })]);
});
it("skips directory copies that overlap protected worktree paths", async () => {
const protectedWorktreeRoot = join(sourceRoot, ".dev", "paseo-home", "worktrees", "project");
mkdirSync(protectedWorktreeRoot, { recursive: true });
writeFileSync(join(sourceRoot, ".worktreeinclude"), ".dev/**\n");
const plan = await readWorktreeIncludePlan({
sourceRoot,
excludedSourceRoots: [protectedWorktreeRoot],
});
expect(plan.materializations).toEqual([]);
expect(plan.skipped).toEqual([expect.objectContaining({ raw: ".dev/**", reason: "unsafe" })]);
});
it("protects every managed project beneath checkout-local worktree storage", async () => {
const managedWorktreesRoot = join(sourceRoot, ".dev", "paseo-home", "worktrees");
const siblingWorktree = join(managedWorktreesRoot, "other-project", "sibling");
mkdirSync(siblingWorktree, { recursive: true });
writeFileSync(join(siblingWorktree, ".env"), "secret\n");
writeFileSync(
join(sourceRoot, ".worktreeinclude"),
".dev/paseo-home/worktrees/other-project/sibling/.env\n",
);
const plan = await readWorktreeIncludePlan({
sourceRoot,
excludedSourceRoots: [managedWorktreesRoot],
});
expect(plan.materializations).toEqual([]);
expect(plan.skipped).toEqual([expect.objectContaining({ reason: "unsafe" })]);
});
it("allows includes from a source worktree inside the protected worktree root", async () => {
const managedWorktreesRoot = join(tempDir, "paseo-home", "worktrees", "project");
sourceRoot = join(managedWorktreesRoot, "source-worktree");
mkdirSync(sourceRoot, { recursive: true });
writeFileSync(join(sourceRoot, ".worktreeinclude"), ".env\n");
writeFileSync(join(sourceRoot, ".env"), "source\n");
const plan = await readWorktreeIncludePlan({
sourceRoot,
excludedSourceRoots: [managedWorktreesRoot],
});
expect(plan.materializations).toEqual([
expect.objectContaining({ relativePath: ".env", sourceKind: "file" }),
]);
expect(plan.skipped).toEqual([]);
});
it.skipIf(isPlatform("win32"))(
"skips protected paths reached through a symlink alias",
async () => {
const sourceAlias = join(tempDir, "source-alias");
symlinkSync(sourceRoot, sourceAlias, "dir");
mkdirSync(join(sourceRoot, ".dev", "paseo-home", "worktrees", "project"), {
recursive: true,
});
writeFileSync(join(sourceRoot, ".worktreeinclude"), ".dev/**\n");
const plan = await readWorktreeIncludePlan({
sourceRoot,
excludedSourceRoots: [join(sourceAlias, ".dev", "paseo-home", "worktrees", "project")],
});
expect(plan.materializations).toEqual([]);
expect(plan.skipped).toEqual([expect.objectContaining({ raw: ".dev/**", reason: "unsafe" })]);
},
);
it.skipIf(isPlatform("win32"))(
"skips external source links while retaining safe entries",
async () => {
const outsidePath = join(tempDir, "outside.txt");
writeFileSync(outsidePath, "outside\n");
writeFileSync(join(sourceRoot, "safe.txt"), "safe\n");
symlinkSync(outsidePath, join(sourceRoot, "linked.txt"));
writeFileSync(
join(sourceRoot, ".worktreeinclude"),
["safe.txt", "linked.txt", ""].join("\n"),
);
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([
expect.objectContaining({ relativePath: "safe.txt", sourceKind: "file" }),
]);
expect(plan.skipped).toEqual([
expect.objectContaining({ raw: "linked.txt", reason: "unsafe" }),
]);
},
);
it.skipIf(isPlatform("win32"))("matches globs beneath a safe symlinked directory", async () => {
mkdirSync(join(sourceRoot, "actual-config"));
writeFileSync(join(sourceRoot, "actual-config", "runtime.env"), "runtime\n");
symlinkSync(join(sourceRoot, "actual-config"), join(sourceRoot, "config"), "dir");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "config/*.env\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([
expect.objectContaining({ relativePath: "config/runtime.env", sourceKind: "file" }),
]);
expect(plan.skipped).toEqual([]);
});
it.skipIf(isPlatform("win32"))(
"does not scan unrelated directories for bounded globs",
async () => {
writeFileSync(join(sourceRoot, ".worktreeinclude"), "packages/*/.runtime.env\n");
mkdirSync(join(sourceRoot, "packages", "api"), { recursive: true });
writeFileSync(join(sourceRoot, "packages", "api", ".runtime.env"), "api\n");
const unreadableDirectory = join(sourceRoot, "unrelated");
mkdirSync(unreadableDirectory);
chmodSync(unreadableDirectory, 0o000);
try {
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([
expect.objectContaining({ relativePath: "packages/api/.runtime.env" }),
]);
} finally {
chmodSync(unreadableDirectory, 0o700);
}
},
);
it.skipIf(isPlatform("win32") || process.getuid?.() === 0)(
"skips inaccessible entries while retaining safe paths",
async () => {
const inaccessibleDirectory = join(sourceRoot, "private");
mkdirSync(inaccessibleDirectory);
writeFileSync(join(inaccessibleDirectory, "secret.txt"), "secret\n");
writeFileSync(join(sourceRoot, "safe.txt"), "safe\n");
writeFileSync(
join(sourceRoot, ".worktreeinclude"),
["private/**", "private/*", "safe.txt", ""].join("\n"),
);
chmodSync(inaccessibleDirectory, 0o000);
try {
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([
expect.objectContaining({ relativePath: "safe.txt", sourceKind: "file" }),
]);
expect(plan.skipped).toEqual(
expect.arrayContaining([
expect.objectContaining({ raw: "private/**", reason: "materialization" }),
expect.objectContaining({ raw: "private/*", reason: "materialization" }),
]),
);
} finally {
chmodSync(inaccessibleDirectory, 0o700);
}
},
);
it.skipIf(isPlatform("win32") || process.getuid?.() === 0)(
"retains valid glob matches when a viable sibling is unreadable",
async () => {
mkdirSync(join(sourceRoot, "packages", "good"), { recursive: true });
mkdirSync(join(sourceRoot, "packages", "private"), { recursive: true });
writeFileSync(join(sourceRoot, "packages", "good", ".runtime.env"), "good\n");
writeFileSync(join(sourceRoot, "packages", "private", ".runtime.env"), "private\n");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "packages/*/.runtime.env\n");
chmodSync(join(sourceRoot, "packages", "private"), 0o000);
try {
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([
expect.objectContaining({ relativePath: "packages/good/.runtime.env" }),
]);
expect(plan.skipped).toEqual([
expect.objectContaining({ raw: "packages/*/.runtime.env", reason: "materialization" }),
]);
} finally {
chmodSync(join(sourceRoot, "packages", "private"), 0o700);
}
},
);
});
describe.skipIf(isPlatform("win32"))("worktree include materialization", () => {
let tempDir: string;
let sourceRoot: string;
let worktreeRoot: string;
beforeEach(() => {
tempDir = mkdtempSync(join(tmpdir(), "worktree-include-materialize-test-"));
sourceRoot = join(tempDir, "source");
worktreeRoot = join(tempDir, "worktree");
mkdirSync(sourceRoot);
mkdirSync(worktreeRoot);
});
afterEach(() => {
rmSync(tempDir, { recursive: true, force: true });
});
it("copies snapshots, links shared paths, and is idempotent", async () => {
writeFileSync(
join(sourceRoot, ".worktreeinclude"),
["copy.txt", "copy-dir/**", "symlink linked.txt", "symlink linked-dir"].join("\n"),
);
writeFileSync(join(sourceRoot, "copy.txt"), "copy-v1\n");
mkdirSync(join(sourceRoot, "copy-dir"), { recursive: true });
writeFileSync(join(sourceRoot, "copy-dir", "state.txt"), "copy-dir-v1\n");
writeFileSync(join(sourceRoot, "linked.txt"), "linked-v1\n");
mkdirSync(join(sourceRoot, "linked-dir"), { recursive: true });
writeFileSync(join(sourceRoot, "linked-dir", "state.txt"), "linked-dir-v1\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(lstatSync(join(worktreeRoot, "copy.txt")).isSymbolicLink()).toBe(false);
expect(lstatSync(join(worktreeRoot, "copy-dir")).isSymbolicLink()).toBe(false);
expect(lstatSync(join(worktreeRoot, "linked.txt")).isSymbolicLink()).toBe(true);
expect(lstatSync(join(worktreeRoot, "linked-dir")).isSymbolicLink()).toBe(true);
writeFileSync(join(sourceRoot, "copy.txt"), "copy-v2\n");
writeFileSync(join(sourceRoot, "copy-dir", "state.txt"), "copy-dir-v2\n");
writeFileSync(join(sourceRoot, "linked.txt"), "linked-v2\n");
writeFileSync(join(sourceRoot, "linked-dir", "state.txt"), "linked-dir-v2\n");
expect(readFileSync(join(worktreeRoot, "copy.txt"), "utf8")).toBe("copy-v1\n");
expect(readFileSync(join(worktreeRoot, "copy-dir", "state.txt"), "utf8")).toBe("copy-dir-v1\n");
expect(readFileSync(join(worktreeRoot, "linked.txt"), "utf8")).toBe("linked-v2\n");
expect(readFileSync(join(worktreeRoot, "linked-dir", "state.txt"), "utf8")).toBe(
"linked-dir-v2\n",
);
await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(readFileSync(join(worktreeRoot, "copy.txt"), "utf8")).toBe("copy-v2\n");
expect(readFileSync(join(worktreeRoot, "copy-dir", "state.txt"), "utf8")).toBe("copy-dir-v2\n");
});
it("replaces an existing directory snapshot without retaining destination-only files", async () => {
mkdirSync(join(sourceRoot, "cache"));
writeFileSync(join(sourceRoot, "cache", "current.txt"), "current\n");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "cache/**\n");
mkdirSync(join(worktreeRoot, "cache"));
writeFileSync(join(worktreeRoot, "cache", "stale.txt"), "stale\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
const result = await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(result).toMatchObject({ materialized: 1, skipped: [] });
expect(readFileSync(join(worktreeRoot, "cache", "current.txt"), "utf8")).toBe("current\n");
expect(existsSync(join(worktreeRoot, "cache", "stale.txt"))).toBe(false);
});
it("retains an explicit descendant when an overlapping directory copy is skipped", async () => {
mkdirSync(join(sourceRoot, "config", "nested"), { recursive: true });
writeFileSync(join(sourceRoot, "config", "local.env"), "local\n");
writeFileSync(join(sourceRoot, "config", "nested", "state.txt"), "state\n");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "config/**\nconfig/local.env\n");
mkdirSync(join(worktreeRoot, "config"));
writeFileSync(join(worktreeRoot, "config", "nested"), "conflict\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
const result = await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(result).toMatchObject({ materialized: 1 });
expect(result.skipped).toEqual([
expect.objectContaining({ raw: "config/**", reason: "conflict" }),
]);
expect(readFileSync(join(worktreeRoot, "config", "local.env"), "utf8")).toBe("local\n");
});
it("skips a destination parent symlink without writing through it", async () => {
mkdirSync(join(sourceRoot, "config"), { recursive: true });
writeFileSync(join(sourceRoot, "config", "local.json"), "{}\n");
writeFileSync(join(sourceRoot, "safe.txt"), "safe\n");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "config/local.json\nsafe.txt\n");
const outsideRoot = join(tempDir, "outside");
mkdirSync(outsideRoot);
symlinkSync(outsideRoot, join(worktreeRoot, "config"));
const plan = await readWorktreeIncludePlan({ sourceRoot });
const result = await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(existsSync(join(outsideRoot, "local.json"))).toBe(false);
expect(readFileSync(join(worktreeRoot, "safe.txt"), "utf8")).toBe("safe\n");
expect(result).toMatchObject({ materialized: 1 });
expect(result.skipped).toEqual([
expect.objectContaining({ raw: "config/local.json", reason: "conflict" }),
]);
expect(readdirSync(worktreeRoot)).not.toContain(
expect.stringMatching(/^\.paseo-worktreeinclude-/),
);
});
it("copies resolved source links and creates direct live links", async () => {
const targetPath = join(sourceRoot, "target.txt");
const targetDirectoryPath = join(sourceRoot, "target-directory");
writeFileSync(targetPath, "v1\n");
mkdirSync(targetDirectoryPath);
writeFileSync(join(targetDirectoryPath, "state.txt"), "v1\n");
symlinkSync(targetPath, join(sourceRoot, "copy-link.txt"));
symlinkSync(targetPath, join(sourceRoot, "live-link.txt"));
symlinkSync(targetDirectoryPath, join(sourceRoot, "copy-directory-link"), "dir");
symlinkSync(targetDirectoryPath, join(sourceRoot, "live-directory-link"), "dir");
writeFileSync(
join(sourceRoot, ".worktreeinclude"),
[
"copy-link.txt",
"symlink live-link.txt",
"copy-directory-link",
"symlink live-directory-link",
"",
].join("\n"),
);
const plan = await readWorktreeIncludePlan({ sourceRoot });
const result = await materializeWorktreeIncludePlan({ plan, worktreeRoot });
const copiedPath = join(worktreeRoot, "copy-link.txt");
const linkedPath = join(worktreeRoot, "live-link.txt");
const copiedDirectoryPath = join(worktreeRoot, "copy-directory-link");
const linkedDirectoryPath = join(worktreeRoot, "live-directory-link");
const canonicalWorktreeRoot = realpathSync(worktreeRoot);
expect(result).toMatchObject({ materialized: 4, skipped: [] });
expect(lstatSync(copiedPath).isSymbolicLink()).toBe(false);
expect(lstatSync(linkedPath).isSymbolicLink()).toBe(true);
expect(lstatSync(copiedDirectoryPath).isSymbolicLink()).toBe(false);
expect(lstatSync(linkedDirectoryPath).isSymbolicLink()).toBe(true);
expect(readlinkSync(linkedPath)).toBe(
relative(dirname(join(canonicalWorktreeRoot, "live-link.txt")), realpathSync(targetPath)),
);
expect(readlinkSync(linkedDirectoryPath)).toBe(
relative(
dirname(join(canonicalWorktreeRoot, "live-directory-link")),
realpathSync(targetDirectoryPath),
),
);
expect(realpathSync(linkedPath)).toBe(realpathSync(targetPath));
expect(realpathSync(linkedDirectoryPath)).toBe(realpathSync(targetDirectoryPath));
writeFileSync(targetPath, "v2\n");
writeFileSync(join(targetDirectoryPath, "state.txt"), "v2\n");
expect(readFileSync(copiedPath, "utf8")).toBe("v1\n");
expect(readFileSync(linkedPath, "utf8")).toBe("v2\n");
expect(readFileSync(join(copiedDirectoryPath, "state.txt"), "utf8")).toBe("v1\n");
expect(readFileSync(join(linkedDirectoryPath, "state.txt"), "utf8")).toBe("v2\n");
});
it("rejects source links that alias Git metadata", async () => {
mkdirSync(join(sourceRoot, ".git"));
writeFileSync(join(sourceRoot, ".git", "config"), "secret\n");
symlinkSync(join(sourceRoot, ".git"), join(sourceRoot, "metadata"), "dir");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "metadata\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([]);
expect(plan.skipped).toEqual([expect.objectContaining({ raw: "metadata", reason: "unsafe" })]);
});
it("treats hard links as ordinary files", async () => {
const targetPath = join(sourceRoot, "target.txt");
const hardLinkPath = join(sourceRoot, "hard-link.txt");
writeFileSync(targetPath, "v1\n");
linkSync(targetPath, hardLinkPath);
writeFileSync(join(sourceRoot, ".worktreeinclude"), "hard-link.txt\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
await materializeWorktreeIncludePlan({ plan, worktreeRoot });
const copiedPath = join(worktreeRoot, "hard-link.txt");
expect(lstatSync(copiedPath).isSymbolicLink()).toBe(false);
expect(readFileSync(copiedPath, "utf8")).toBe("v1\n");
});
it("skips a source link retargeted outside the checkout after planning", async () => {
const insidePath = join(sourceRoot, "inside.txt");
const linkedPath = join(sourceRoot, "linked.txt");
const outsidePath = join(tempDir, "outside.txt");
writeFileSync(insidePath, "inside\n");
writeFileSync(outsidePath, "outside\n");
symlinkSync(insidePath, linkedPath);
writeFileSync(join(sourceRoot, ".worktreeinclude"), "symlink linked.txt\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
rmSync(linkedPath);
symlinkSync(outsidePath, linkedPath);
const result = await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(existsSync(join(worktreeRoot, "linked.txt"))).toBe(false);
expect(result.skipped).toEqual([
expect.objectContaining({ raw: "symlink linked.txt", reason: "unsafe" }),
]);
});
it("skips a linked directory that exposes an external nested link", async () => {
const sharedPath = join(sourceRoot, "shared");
const outsidePath = join(tempDir, "outside.txt");
mkdirSync(sharedPath);
writeFileSync(outsidePath, "outside\n");
symlinkSync(outsidePath, join(sharedPath, "outside.txt"));
writeFileSync(join(sourceRoot, ".worktreeinclude"), "symlink shared\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
expect(plan.materializations).toEqual([]);
expect(plan.skipped).toEqual([
expect.objectContaining({ raw: "symlink shared", reason: "unsafe" }),
]);
});
it("allows a linked directory with internal nested links", async () => {
const sharedPath = join(sourceRoot, "shared");
const targetPath = join(sourceRoot, "shared-target");
mkdirSync(sharedPath);
mkdirSync(targetPath);
writeFileSync(join(targetPath, "state.txt"), "source\n");
symlinkSync(targetPath, join(sharedPath, "target"), "dir");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "symlink shared\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
const result = await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(result).toMatchObject({ materialized: 1, skipped: [] });
expect(readFileSync(join(worktreeRoot, "shared", "target", "state.txt"), "utf8")).toBe(
"source\n",
);
});
it("skips a source removed after planning", async () => {
writeFileSync(join(sourceRoot, ".worktreeinclude"), "runtime.env\n");
const sourcePath = join(sourceRoot, "runtime.env");
writeFileSync(sourcePath, "source\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
rmSync(sourcePath);
const result = await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(existsSync(join(worktreeRoot, "runtime.env"))).toBe(false);
expect(result.skipped).toEqual([
expect.objectContaining({ raw: "runtime.env", reason: "missing" }),
]);
});
it.skipIf(process.getuid?.() === 0)(
"skips ordinary filesystem failures during materialization revalidation",
async () => {
mkdirSync(join(sourceRoot, "private"));
writeFileSync(join(sourceRoot, "private", "state.txt"), "private\n");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "private/**\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
chmodSync(join(sourceRoot, "private"), 0o000);
try {
const result = await materializeWorktreeIncludePlan({ plan, worktreeRoot });
expect(result).toMatchObject({ materialized: 0 });
expect(result.skipped).toEqual([
expect.objectContaining({ raw: "private/**", reason: "materialization" }),
]);
} finally {
chmodSync(join(sourceRoot, "private"), 0o700);
}
},
);
});
describe.skipIf(!isPlatform("win32"))("worktree include Windows directory links", () => {
let tempDir: string;
let sourceRoot: string;
let worktreeRoot: string;
beforeEach(() => {
tempDir = mkdtempSync(join(tmpdir(), "worktree-include-windows-test-"));
sourceRoot = join(tempDir, "source");
worktreeRoot = join(tempDir, "worktree");
mkdirSync(sourceRoot);
mkdirSync(worktreeRoot);
});
afterEach(() => {
rmSync(tempDir, { recursive: true, force: true });
});
it("uses a live directory link and removes it without touching the source", async () => {
mkdirSync(join(sourceRoot, "shared-state"));
writeFileSync(join(sourceRoot, "shared-state", "state.txt"), "source-v1\n");
writeFileSync(join(sourceRoot, ".worktreeinclude"), "symlink shared-state\n");
const plan = await readWorktreeIncludePlan({ sourceRoot });
await materializeWorktreeIncludePlan({ plan, worktreeRoot });
writeFileSync(join(sourceRoot, "shared-state", "state.txt"), "source-v2\n");
expect(readFileSync(join(worktreeRoot, "shared-state", "state.txt"), "utf8")).toBe(
"source-v2\n",
);
rmSync(worktreeRoot, { recursive: true, force: true });
expect(readFileSync(join(sourceRoot, "shared-state", "state.txt"), "utf8")).toBe("source-v2\n");
});
});