From c146ea754764448b70e63ca64e56cbb122d79a6e Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Wed, 27 Aug 2025 16:12:25 -0600 Subject: [PATCH] fix(windows): normalize worktree comparison for checkpoints on Windows (short/long path and case-insensitive) to stabilize tests --- .../checkpoints/ShadowCheckpointService.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/services/checkpoints/ShadowCheckpointService.ts b/src/services/checkpoints/ShadowCheckpointService.ts index e0267f88b4..42d9f5154d 100644 --- a/src/services/checkpoints/ShadowCheckpointService.ts +++ b/src/services/checkpoints/ShadowCheckpointService.ts @@ -90,7 +90,26 @@ export abstract class ShadowCheckpointService extends EventEmitter { this.log(`[${this.constructor.name}#initShadowGit] shadow git repo already exists at ${this.dotGitDir}`) const worktree = await this.getShadowGitConfigWorktree(git) - if (worktree !== this.workspaceDir) { + // Normalize and compare paths in a cross-platform safe way (handles: + // - Windows path separators + // - Case-insensitivity + // - Short (8.3) vs long paths via realpath fallback) + const normalizeFsPath = (p: string) => { + const normalized = path.normalize(p) + return process.platform === "win32" ? normalized.toLowerCase() : normalized + } + const pathsEqual = async (a?: string, b?: string) => { + if (!a || !b) return false + try { + const [ra, rb] = await Promise.all([fs.realpath(a), fs.realpath(b)]) + return normalizeFsPath(ra) === normalizeFsPath(rb) + } catch { + return normalizeFsPath(a) === normalizeFsPath(b) + } + } + + const sameWorkspace = await pathsEqual(worktree, this.workspaceDir) + if (!sameWorkspace) { throw new Error( `Checkpoints can only be used in the original workspace: ${worktree} !== ${this.workspaceDir}`, )