mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-13 23:39:21 +02:00
fix: preserve staged untracked files in all-file workspaces
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
a44ae3d789
commit
0a34c6abe4
2 files changed
+33
-3
No files matched your search
@@ -1232,7 +1232,8 @@ export async function prepareSandboxManagedRuntime(input: {
|
||||
}
|
||||
ignoredPaths = managedIgnored.paths("ignored");
|
||||
}
|
||||
const deletedPaths = gitSnapshot?.deletedPaths ?? managedIgnored?.paths("deleted") ?? [];
|
||||
let deletedPaths = gitSnapshot?.deletedPaths ?? managedIgnored?.paths("deleted") ?? [];
|
||||
let filesystemDeleted: Awaited<ReturnType<typeof createWorkspaceManifest>> | null = null;
|
||||
managedIgnored?.close();
|
||||
let baselineSnapshot: DirectorySnapshot | null = null;
|
||||
const cleanupWorkspaceSnapshot = async () => {
|
||||
@@ -1241,6 +1242,7 @@ export async function prepareSandboxManagedRuntime(input: {
|
||||
await disposeGitWorkspaceSnapshot(gitSnapshot);
|
||||
if (!gitSnapshot) for (const repo of repositories) await disposeGitWorkspaceSnapshot(repo.snapshot);
|
||||
if (managedIgnored) await fs.rm(path.dirname(managedIgnored.filePath), { recursive: true, force: true });
|
||||
if (filesystemDeleted) await fs.rm(path.dirname(filesystemDeleted.filePath), { recursive: true, force: true });
|
||||
};
|
||||
try {
|
||||
// A selected subfolder has no cloneable Git snapshot, but its parent
|
||||
@@ -1291,6 +1293,18 @@ export async function prepareSandboxManagedRuntime(input: {
|
||||
ignoredPaths: input.workspaceBaseline.ignoredPaths,
|
||||
});
|
||||
}
|
||||
if (input.workspaceFileMode === "all" && baselineSnapshot
|
||||
&& (isPathManifest(deletedPaths) ? deletedPaths.count : deletedPaths.length) > 0) {
|
||||
// The filesystem is authoritative in all-files mode. Git can report a
|
||||
// staged untracking as deleted while its file remains in the admitted tree.
|
||||
filesystemDeleted = await createWorkspaceManifest();
|
||||
try {
|
||||
for (const relative of workspacePaths(deletedPaths)) {
|
||||
if (!baselineSnapshot.entries.has(relative)) filesystemDeleted.add("deleted", relative);
|
||||
}
|
||||
deletedPaths = filesystemDeleted.paths("deleted");
|
||||
} finally { filesystemDeleted.close(); }
|
||||
}
|
||||
|
||||
const seedGeneration = input.workspaceSeedCacheDirectory && baselineSnapshot && workspaceInboundMode !== "durable_seed"
|
||||
? workspaceSeedGeneration(baselineSnapshot, gitSnapshot, repositories) : null;
|
||||
|
||||
@@ -24,6 +24,10 @@ it.each([undefined, "all"] as const)("keeps nested Git metadata sanitized in col
|
||||
await git(repository, "init", "-b", "main");
|
||||
await fs.writeFile(path.join(repository, ".gitignore"), "ignored.txt\n");
|
||||
await fs.writeFile(path.join(repository, "work.txt"), "first\n");
|
||||
if (workspaceFileMode === "all") {
|
||||
await fs.writeFile(path.join(repository, "notes.txt"), "keep after untracking\n");
|
||||
await fs.writeFile(path.join(repository, "absent.txt"), "truly deleted\n");
|
||||
}
|
||||
await git(repository, "add", ".");
|
||||
await git(repository, "-c", "user.name=Fixture", "-c", "user.email=fixture@example.test", "commit", "-m", "first");
|
||||
await git(repository, "branch", "private-fixture");
|
||||
@@ -34,6 +38,10 @@ it.each([undefined, "all"] as const)("keeps nested Git metadata sanitized in col
|
||||
await fs.writeFile(path.join(repository, ".git/hooks/post-commit"), "private-hook-fixture");
|
||||
await fs.writeFile(path.join(repository, "work.txt"), "dirty\n");
|
||||
await fs.writeFile(path.join(repository, "ignored.txt"), "ordinary ignored bytes");
|
||||
if (workspaceFileMode === "all") {
|
||||
await git(repository, "rm", "--cached", "notes.txt");
|
||||
await fs.unlink(path.join(repository, "absent.txt"));
|
||||
}
|
||||
const run = async (command: string) => { await execute("sh", ["-c", command]); };
|
||||
const sync = async (operations: SandboxSyncOperation[]) => {
|
||||
for (const operation of operations) {
|
||||
@@ -68,7 +76,11 @@ it.each([undefined, "all"] as const)("keeps nested Git metadata sanitized in col
|
||||
expect(await git(checkout, "rev-parse", "HEAD")).toBe(head);
|
||||
expect(await git(checkout, "show", "HEAD:work.txt")).toBe("committed");
|
||||
expect(await fs.readFile(path.join(checkout, "work.txt"), "utf8")).toBe("dirty\n");
|
||||
if (workspaceFileMode === "all") expect(await fs.readFile(path.join(checkout, "ignored.txt"), "utf8")).toBe("ordinary ignored bytes");
|
||||
if (workspaceFileMode === "all") {
|
||||
expect(await fs.readFile(path.join(checkout, "ignored.txt"), "utf8")).toBe("ordinary ignored bytes");
|
||||
expect(await fs.readFile(path.join(checkout, "notes.txt"), "utf8")).toBe("keep after untracking\n");
|
||||
await expect(fs.stat(path.join(checkout, "absent.txt"))).rejects.toMatchObject({ code: "ENOENT" });
|
||||
}
|
||||
};
|
||||
const cold = await prepare(); await assertSanitized();
|
||||
expect([...cold.workspaceSyncSnapshot!.baseline.entries].some(([key]) => key.startsWith(`${relative}/.git/`))).toBe(false);
|
||||
@@ -94,6 +106,10 @@ it.each([undefined, "all"] as const)("keeps nested Git metadata sanitized in col
|
||||
expect(await fs.readFile(path.join(repository, "work.txt"), "utf8")).toBe("restored dirty bytes\n");
|
||||
expect(await fs.readFile(path.join(repository, ".git/config"), "utf8")).toContain("fixture-token");
|
||||
expect(await fs.readFile(path.join(repository, ".git/hooks/post-commit"), "utf8")).toBe("changed private hook");
|
||||
if (workspaceFileMode === "all") expect(await fs.readFile(path.join(repository, "ignored.txt"), "utf8")).toBe("updated ordinary ignored bytes");
|
||||
if (workspaceFileMode === "all") {
|
||||
expect(await fs.readFile(path.join(repository, "ignored.txt"), "utf8")).toBe("updated ordinary ignored bytes");
|
||||
expect(await fs.readFile(path.join(repository, "notes.txt"), "utf8")).toBe("keep after untracking\n");
|
||||
await expect(fs.stat(path.join(repository, "absent.txt"))).rejects.toMatchObject({ code: "ENOENT" });
|
||||
}
|
||||
await recovered.cleanupWorkspaceSnapshot(); await cached.cleanupWorkspaceSnapshot();
|
||||
}, 30000);
|
||||
Reference in new issue
Block a user