fix(diff): keep non-Git workspace refresh successful

Diff invalidation has no target repository for an ordinary folder. Treat that expected absence as a no-op while preserving other root-resolution errors.
This commit is contained in:
Mohamed Boudra
2026-11-06 09:18:45 +01:00
parent a5c5ad8e1d
commit 850471276a
2 changed files with 49 additions and 1 deletions
+8 -1
View File
@@ -216,7 +216,14 @@ export class DiffSubscriptions {
};
}
async invalidateForCwd(cwd: string): Promise<void> {
const matches = createRealpathAwarePathMatcher(await worktreeRoot(expandTilde(cwd)));
let root: string;
try {
root = await worktreeRoot(expandTilde(cwd));
} catch (error) {
if (error instanceof DiffReadError && error.code === "NOT_GIT_REPO") return;
throw error;
}
const matches = createRealpathAwarePathMatcher(root);
for (const target of this.targets.values())
if (matches(target.compare.cwd)) this.invalidate(target);
}
@@ -194,3 +194,44 @@ test("checkout refresh from a subdirectory updates a pinned streamed comparison"
rmSync(cwd, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
test("refreshing a non-Git workspace succeeds while a Git comparison is open", async () => {
const cwd = mkdtempSync(join(tmpdir(), "stream-refresh-git-"));
const plain = mkdtempSync(join(tmpdir(), "stream-refresh-plain-"));
const git = (...args: string[]) => execFileSync("git", args, { cwd, encoding: "utf8" }).trim();
const daemon = await createTestPaseoDaemon({ agentClients: {} });
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
try {
git("init", "-b", "main");
git("config", "user.name", "Test");
git("config", "user.email", "test@example.com");
writeFileSync(join(cwd, "file.txt"), "committed content\n");
git("add", ".");
git("-c", "commit.gpgsign=false", "commit", "-m", "base");
await client.connect();
const states: import("@getpaseo/client").DiffState[] = [];
const observation = client.observeDiff({
cwd,
base: { kind: "empty" },
head: { kind: "rev", rev: git("rev-parse", "HEAD") },
});
const unsubscribe = observation.subscribe({
snapshot: (state) => states.push(state),
update: () => {},
});
try {
await expect.poll(() => states.at(-1)?.phase, { timeout: 15000 }).toBe("ready");
const revision = states.at(-1)?.revision;
expect(await client.checkoutRefresh(plain)).toMatchObject({ success: true, error: null });
expect(states.at(-1)).toMatchObject({ phase: "ready", revision });
} finally {
unsubscribe();
await observation.release();
}
} finally {
await client.close();
await daemon.close();
rmSync(cwd, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
rmSync(plain, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});