fix(desktop): auto-repair worktree path when directory is moved/unnested#2060
fix(desktop): auto-repair worktree path when directory is moved/unnested#2060
Conversation
When a worktree directory is moved or unnested, the stored path in the local database becomes stale, causing terminal connections to fail with "Workspace path does not exist" errors. This adds auto-detection of the new path by querying `git worktree list` and matching by branch name, then updating the database so the terminal (and git status) can reconnect.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a worktree path repair utility and integrates it into terminal createOrAttach and git-status refresh flows to detect and repair stale or moved worktree paths by consulting the database, filesystem, and repo branch worktree resolution. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Router as Terminal/GitStatus Router
participant Repair as tryRepairWorktreePath
participant DB as Database
participant FS as Filesystem
participant Git as getBranchWorktreePath
Client->>Router: createOrAttach / refreshGitStatus request
Router->>FS: check stored worktree path
FS-->>Router: path missing
Router->>Repair: tryRepairWorktreePath(worktreeId)
Repair->>DB: fetch worktree & project
DB-->>Repair: worktree + mainRepoPath
Repair->>Git: getBranchWorktreePath(mainRepoPath, branch)
Git-->>Repair: resolved path
Repair->>FS: verify resolved path exists
FS-->>Repair: path exists
Repair->>DB: update worktree path
DB-->>Repair: updated record
Repair-->>Router: repaired path (or null)
Router->>Router: continue operation using repaired path (if any)
Router-->>Client: operation result
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts`:
- Around line 67-76: After attempting tryRepairWorktreePath(worktree.id) you
must re-check that the resolved worktreePath exists before calling
getAheadBehindCount; if the repairedPath is falsy or the filesystem still lacks
the path, throw a deterministic tRPC error (e.g., via trpcError or TRPCError)
instead of calling getAheadBehindCount. Update the block that sets worktreePath
(and the surrounding guard) to perform a second existsSync check after repair
and raise a clear tRPC error mentioning the worktree.id when the path is still
missing so downstream git calls never run against a non-existent path.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b33889fb-433f-45b4-b8ce-bbaa26cfa528
📒 Files selected for processing (3)
apps/desktop/src/lib/trpc/routers/terminal/terminal.tsapps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts
`git worktree list` includes the main worktree. If the stale worktree's branch happens to be checked out in the main repo, the repair would incorrectly rebind the worktree row to mainRepoPath. Guard against this by comparing the candidate path against the project's main repo path.
… tests - Replace resolve() with realpathSync() to canonicalize symlinks (e.g. /var → /private/var on macOS) when comparing candidate path against main repo path. - Add 6 regression tests covering: valid path no-op, successful repair after git worktree move, main repo rejection, missing project/worktree, and branch not found by git.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts">
<violation number="1" location="apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts:185">
P2: This test is non-deterministic across Git configurations because it hardcodes `branch: "main"` after `git init`, so it may pass without exercising the intended rejection path.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts (1)
34-40: Consider splitting shell commands for better cross-platform compatibility.The chained shell command with single quotes (
'init') could fail on Windows. While this may not be a concern if development is Unix-only, splitting into separateexecSynccalls would be more robust.♻️ Optional refactor for portability
function seedCommit(repoPath: string): void { writeFileSync(join(repoPath, "README.md"), "# test\n"); - execSync("git add . && git commit -m 'init'", { - cwd: repoPath, - stdio: "ignore", - }); + execSync("git add .", { cwd: repoPath, stdio: "ignore" }); + execSync('git commit -m "init"', { cwd: repoPath, stdio: "ignore" }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts` around lines 34 - 40, The test helper seedCommit uses a single execSync call with a chained shell command and single quotes which can break on Windows; split the operations inside seedCommit into two execSync calls (one for "git add ." and one for "git commit -m \"init\"" or an equivalent cross-platform commit invocation) so the commands are executed separately and avoid single-quote quoting issues when running on Windows environments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts`:
- Around line 34-40: The test helper seedCommit uses a single execSync call with
a chained shell command and single quotes which can break on Windows; split the
operations inside seedCommit into two execSync calls (one for "git add ." and
one for "git commit -m \"init\"" or an equivalent cross-platform commit
invocation) so the commands are executed separately and avoid single-quote
quoting issues when running on Windows environments.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 521558a6-139c-45ba-ac6b-961337a550a2
📒 Files selected for processing (2)
apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts
Use git rev-parse instead of hardcoding "main" so the test exercises the guard on environments where git init defaults to "master".
…hGitStatus After attempting path repair, check existsSync again before calling getAheadBehindCount. Without this, a stale path silently produces ahead=0/behind=0 which misleads the UI into showing the workspace as up-to-date.
There was a problem hiding this comment.
1 issue found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts">
<violation number="1" location="apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts:169">
P2: Use `path.basename()` instead of `split("/").pop()` for cross-platform correctness. The manual split won't extract the correct directory name on Windows paths.
(Based on your team's feedback about using cross-platform path utilities instead of split.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const worktreeName = worktree.path.split("/").pop() ?? worktree.branch; | ||
| const worktreePath = | ||
| (await resolveWorktreePathWithRepair(worktree.id)) ?? worktree.path; | ||
| const worktreeName = worktreePath.split("/").pop() ?? worktree.branch; |
There was a problem hiding this comment.
P2: Use path.basename() instead of split("/").pop() for cross-platform correctness. The manual split won't extract the correct directory name on Windows paths.
(Based on your team's feedback about using cross-platform path utilities instead of split.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts, line 169:
<comment>Use `path.basename()` instead of `split("/").pop()` for cross-platform correctness. The manual split won't extract the correct directory name on Windows paths.
(Based on your team's feedback about using cross-platform path utilities instead of split.) </comment>
<file context>
@@ -164,7 +164,9 @@ export const createGitStatusProcedures = () => {
- const worktreeName = worktree.path.split("/").pop() ?? worktree.branch;
+ const worktreePath =
+ (await resolveWorktreePathWithRepair(worktree.id)) ?? worktree.path;
+ const worktreeName = worktreePath.split("/").pop() ?? worktree.branch;
const branchName = worktree.branch;
</file context>
# Conflicts: # apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts
Summary
Why / Context
When a worktree directory is moved or unnested, the absolute path stored in local SQLite can go stale. The original PR fixed terminal attach and
refreshGitStatus, but renderer flows like the workspace page and sidebar still received the staleworktreePathfromworkspaces.get/getAllGroupedand passed it into changes/status queries.That meant the path repair happened too late: some UI still broke until an unrelated refetch happened.
What Changed
Shared path resolver
Added
resolveWorktreePathWithRepair()inapps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts.It returns:
tryRepairWorktreePath()succeedsnullif the worktree still cannot be resolvedRenderer-facing workspace queries
These now repair stale worktree paths before returning them to the renderer:
workspaces.getworkspaces.getAllGroupedThis is the key fix for the actual UI path that powers Changes / Files / tab content.
Remaining worktree-path endpoints
These now use the same shared resolver:
terminal.createOrAttachterminal.getWorkspaceCwdworkspaces.refreshGitStatusworkspaces.getGitHubStatusworkspaces.getWorktreeInfoFrontend cache propagation
After a successful terminal
createOrAttach, invalidate:workspaces.get({ id })workspaces.getAllGrouped()terminal.getWorkspaceCwd(workspaceId)This prevents the renderer from continuing to use a stale cached path after the DB row has been repaired.
Limitation
Auto-repair still depends on
git worktree listknowing the new path. So it works when:git worktree movewas usedgit worktree repairwas run after a manual moveIf Git itself still reports the stale path, the app cannot discover the new location automatically.
Manual QA Checklist
git worktree moveworktreePathTesting
bun test apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts✅bun run --filter @superset/desktop typecheck✅bunx biome check --write ...✅