From 3e582daf459f3f3d046cf8a023cb6072331037c5 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 4 Feb 2026 10:18:18 +0700 Subject: [PATCH] Update files --- packages/server/src/utils/worktree.test.ts | 2 +- packages/server/src/utils/worktree.ts | 36 +++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/worktree.test.ts b/packages/server/src/utils/worktree.test.ts index 06b34566e..1ab1d298a 100644 --- a/packages/server/src/utils/worktree.test.ts +++ b/packages/server/src/utils/worktree.test.ts @@ -192,7 +192,7 @@ describe("createWorktree", () => { // Verify setup ran and env vars were available const setupLog = readFileSync(join(result.worktreePath, "setup.log"), "utf8"); - expect(setupLog).toContain(`root=${result.worktreePath}`); + expect(setupLog).toContain(`root=${repoDir}`); expect(setupLog).toContain(`worktree=${result.worktreePath}`); expect(setupLog).toContain("branch=setup-test"); }); diff --git a/packages/server/src/utils/worktree.ts b/packages/server/src/utils/worktree.ts index 9de91341b..5cf1da7fd 100644 --- a/packages/server/src/utils/worktree.ts +++ b/packages/server/src/utils/worktree.ts @@ -114,10 +114,39 @@ async function execSetupCommand( } } +async function inferRepoRootPathFromWorktreePath(worktreePath: string): Promise { + try { + const commonDir = await getGitCommonDir(worktreePath); + const normalizedCommonDir = normalizePathForOwnership(commonDir); + // Normal repo/worktree: common dir is /.git + if (basename(normalizedCommonDir) === ".git") { + return dirname(normalizedCommonDir); + } + // Bare repo: common dir is the repo dir itself + return normalizedCommonDir; + } catch { + // Fallback: best-effort resolve toplevel (will be the worktree root in typical cases) + try { + const { stdout } = await execAsync( + "git rev-parse --path-format=absolute --show-toplevel", + { cwd: worktreePath, env: READ_ONLY_GIT_ENV } + ); + const topLevel = stdout.trim(); + if (topLevel) { + return normalizePathForOwnership(topLevel); + } + } catch { + // ignore + } + return normalizePathForOwnership(worktreePath); + } +} + export async function runWorktreeSetupCommands(options: { worktreePath: string; branchName: string; cleanupOnFailure: boolean; + repoRootPath?: string; }): Promise { // Read paseo.json from the worktree (it will have the same content as the source repo) const setupCommands = getWorktreeSetupCommands(options.worktreePath); @@ -125,9 +154,14 @@ export async function runWorktreeSetupCommands(options: { return []; } + const repoRootPath = + options.repoRootPath ?? (await inferRepoRootPathFromWorktreePath(options.worktreePath)); + const setupEnv = { ...process.env, - PASEO_ROOT_PATH: options.worktreePath, + // Root is the original git repo root (shared across worktrees), not the worktree itself. + // This allows setup scripts to copy uncommitted local files (e.g. .env) from the main checkout. + PASEO_ROOT_PATH: repoRootPath, PASEO_WORKTREE_PATH: options.worktreePath, PASEO_BRANCH_NAME: options.branchName, };