Skip to content

load in existing worktrees if path exists - #188

Closed
Kitenite wants to merge 3 commits into
mainfrom
pearl-wave-37
Closed

Kitenite wants to merge 3 commits into
mainfrom
pearl-wave-37

Conversation

@Kitenite

@Kitenite Kitenite commented Nov 29, 2025 •

Copy link
Copy Markdown
Member
  • load in existing worktrees if path exists

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • New projects automatically discover and import existing git worktrees from the repository's standardized worktrees directory.
    • For each imported worktree, a corresponding workspace is created with the branch as its name and tab ordering determined relative to existing project workspaces.
    • If a project's tab order was unset, it is now assigned based on existing active projects when imports occur (no DB changes if no worktrees found).

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercel Bot commented Nov 29, 2025 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
website Ready Ready Preview Comment Dec 1, 2025 3:45am

@coderabbitai

coderabbitai Bot commented Nov 29, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Adds automatic discovery of git worktrees during project creation: lists worktrees from the repository, filters those under the superset worktrees directory and that exist on disk, then creates corresponding worktree and workspace DB records and adjusts project tabOrder when applicable.

Changes

Cohort / File(s) Summary
Git worktree utility
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
Added exported WorktreeInfo interface and listWorktrees(mainRepoPath: string): Promise<WorktreeInfo[]> which runs git worktree list --porcelain, parses entries, and returns worktree path/branch pairs.
Project creation with worktree auto-import
apps/desktop/src/lib/trpc/routers/projects/projects.ts
Extended imports (filesystem, home dir, path). Introduced SUPERSET_DIR_NAME and WORKTREES_DIR_NAME. In openNew flow, call listWorktrees, filter worktrees under the superset worktrees dir and that exist on disk, create DB worktree rows for new entries, create corresponding workspace rows (name = branch, tabOrder computed from existing workspaces), and set project tabOrder if previously null. Updates occur only when superset worktrees are found.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant ProjectsRouter as Projects Router
    participant GitUtil as listWorktrees
    participant Filesystem as FS (existsSync)
    participant Database

    Client->>ProjectsRouter: request openNew(projectData)
    ProjectsRouter->>GitUtil: listWorktrees(mainRepoPath)
    GitUtil-->>ProjectsRouter: [ {path, branch}, ... ]
    ProjectsRouter->>Filesystem: check each worktree path exists
    Filesystem-->>ProjectsRouter: exists / not exists
    alt worktrees under superset dir and exist
        ProjectsRouter->>Database: create worktree record(s)
        Database-->>ProjectsRouter: created worktree id(s)
        ProjectsRouter->>Database: create workspace(s) for worktree id(s)
        Database-->>ProjectsRouter: created workspace id(s)
        ProjectsRouter->>Database: possibly update project.tabOrder
        Database-->>ProjectsRouter: project updated
    else no valid worktrees
        ProjectsRouter-->>Database: no additional DB changes
    end
    ProjectsRouter-->>Client: return created project (with worktrees/workspaces)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review parsing logic in listWorktrees for edge cases and ensuring final entry is captured.
  • Verify path filtering uses the correct superset/worktrees directory logic and robustly handles edge cases (symlinks, relative paths).
  • Confirm transactional behavior / ordering when inserting worktrees and workspaces to avoid partial state on failure.
  • Check tabOrder computation (max existing tabOrder among active projects/workspaces) and null handling.

Possibly related PRs

  • move worktree created directory to home dir #148: Adds/changes handling of SUPSERSET_DIR_NAME and WORKTREES_DIR_NAME and relocates created worktrees into the home/.superset/worktrees layout — directly overlaps path/layout logic used here.
  • conditional render #152: Adds git worktree utilities and related integrations; complements the new listWorktrees implementation and usage.

Poem

🐰
I hopped through branches, sniffed each nest,
Found worktrees sleeping in superset rest.
I nudged them in, with a soft little tap,
Workspaces rose — ready, map by map.
Hooray — new projects, a rabbit's clap!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is extremely minimal, containing only a single bullet point that restates the title without following the provided template structure. Expand the description to include the template sections: Description (explain the implementation), Related Issues, Type of Change, Testing, and any Additional Notes about the worktree auto-import feature.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main change: loading existing worktrees from an established path during project creation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pearl-wave-37

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/desktop/src/lib/trpc/routers/projects/projects.ts (1)

137-150: Consider moving project activation outside the loop.

This block runs on every iteration but the condition ensures it only takes effect once. Moving it after the loop would avoid repeated find/filter operations.

 						data.workspaces.push(workspace);
-
-						// Mark project as active since we found workspaces
-						// biome-ignore lint/style/noNonNullAssertion: project is assigned above
-						const p = data.projects.find((proj) => proj.id === project!.id);
-						if (p && p.tabOrder === null) {
-							const activeProjects = data.projects.filter(
-								(proj) => proj.tabOrder !== null,
-							);
-							const maxProjectTabOrder =
-								activeProjects.length > 0
-									? // biome-ignore lint/style/noNonNullAssertion: filter guarantees tabOrder is not null
-										Math.max(...activeProjects.map((proj) => proj.tabOrder!))
-									: -1;
-							p.tabOrder = maxProjectTabOrder + 1;
-						}
 					}
+
+					// Mark project as active since we found workspaces
+					// biome-ignore lint/style/noNonNullAssertion: project is assigned above
+					const p = data.projects.find((proj) => proj.id === project!.id);
+					if (p && p.tabOrder === null) {
+						const activeProjects = data.projects.filter(
+							(proj) => proj.tabOrder !== null,
+						);
+						const maxProjectTabOrder =
+							activeProjects.length > 0
+								? // biome-ignore lint/style/noNonNullAssertion: filter guarantees tabOrder is not null
+									Math.max(...activeProjects.map((proj) => proj.tabOrder!))
+								: -1;
+						p.tabOrder = maxProjectTabOrder + 1;
+					}
 				});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82d8e6d and cae7b79.

📒 Files selected for processing (2)
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts (2 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/desktop/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: Please use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid using any type - use explicit types instead for type safety
Use camelCase for variable and function names following existing codebase patterns
Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Follow existing patterns and match the codebase style when writing new code

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
apps/desktop/src/lib/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in shared code like src/lib/electron-router-dom.ts - this code runs in both main and renderer processes

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
🧠 Learnings (3)
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to apps/desktop/src/lib/**/*.ts : Never import Node.js modules in shared code like `src/lib/electron-router-dom.ts` - this code runs in both main and renderer processes

Applied to files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to apps/desktop/src/main/**/*.{ts,tsx} : Node.js modules (fs, path, os, net, etc.) can be used in main process code only

Applied to files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment only

Applied to files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
🧬 Code graph analysis (1)
apps/desktop/src/lib/trpc/routers/projects/projects.ts (3)
apps/desktop/src/shared/constants.ts (2)
  • SUPERSET_DIR_NAME (21-23)
  • WORKTREES_DIR_NAME (24-24)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1)
  • listWorktrees (158-193)
apps/desktop/src/main/lib/db/index.ts (1)
  • db (18-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (4)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (2)

148-151: LGTM!

The interface is well-defined with clear, appropriately-typed fields for worktree metadata.


158-193: Clean implementation for parsing porcelain worktree output.

The parsing logic correctly handles the porcelain format's structure. Note that worktrees in detached HEAD state will be excluded since they lack a branch line—this appears intentional given downstream usage requires branch names.

apps/desktop/src/lib/trpc/routers/projects/projects.ts (2)

1-14: LGTM!

Imports are well-organized. Node.js modules are acceptable here since tRPC routers execute in the Electron main process.


71-135: Good integration of worktree discovery into project creation.

The logic correctly filters to superset-managed worktrees, avoids duplicates in the database, and properly initializes workspace records with incremental tab ordering.

@Kitenite Kitenite changed the title pearl wave 37 load in existing worktrees if path exists Dec 1, 2025
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cae7b79 and 2476930.

📒 Files selected for processing (1)
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/desktop/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc

Files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: Please use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid using any type - use explicit types instead for type safety
Use camelCase for variable and function names following existing codebase patterns
Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Follow existing patterns and match the codebase style when writing new code

Files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
apps/desktop/src/lib/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in shared code like src/lib/electron-router-dom.ts - this code runs in both main and renderer processes

Files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
🧠 Learnings (4)
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/*.{ts,tsx} : Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications

Applied to files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to apps/desktop/src/lib/**/*.ts : Never import Node.js modules in shared code like `src/lib/electron-router-dom.ts` - this code runs in both main and renderer processes

Applied to files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to apps/desktop/src/main/**/*.{ts,tsx} : Node.js modules (fs, path, os, net, etc.) can be used in main process code only

Applied to files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment only

Applied to files:

  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
🧬 Code graph analysis (1)
apps/desktop/src/lib/trpc/routers/projects/projects.ts (2)
apps/desktop/src/shared/constants.ts (2)
  • SUPERSET_DIR_NAME (21-23)
  • WORKTREES_DIR_NAME (24-24)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1)
  • listWorktrees (158-193)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (3)
apps/desktop/src/lib/trpc/routers/projects/projects.ts (3)

1-13: LGTM!

The imports are appropriate for main process code. Node.js modules (fs, os, path) are correctly used here since tRPC routers run in the Electron main process. Based on learnings, this aligns with the guideline that Node.js modules can be used in main process code only.


71-77: LGTM!

The path construction correctly uses path.join with the appropriate constants.


87-153: LGTM!

The DB update logic correctly:

  • Checks for existing worktrees to avoid duplicates
  • Creates both worktree and workspace records with proper relationships
  • Calculates sequential tabOrder values for workspaces
  • Activates the project (sets tabOrder) only once due to the p.tabOrder === null guard

The repeated filter operations inside the loop are slightly inefficient but acceptable given the typical small number of worktrees.

WORKTREES_DIR_NAME,
);

const existingWorktrees = await listWorktrees(mainRepoPath);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Wrap listWorktrees in try-catch to prevent import failures from breaking project creation.

At this point the project has already been successfully created. If listWorktrees fails (e.g., git command error, permission issues), the entire mutation throws and the user sees an error even though the project was saved. Worktree import is supplementary functionality and shouldn't cause the overall operation to fail.

-			const existingWorktrees = await listWorktrees(mainRepoPath);
+			let existingWorktrees: Awaited<ReturnType<typeof listWorktrees>> = [];
+			try {
+				existingWorktrees = await listWorktrees(mainRepoPath);
+			} catch (error) {
+				console.warn("Failed to list existing worktrees:", error);
+			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const existingWorktrees = await listWorktrees(mainRepoPath);
let existingWorktrees: Awaited<ReturnType<typeof listWorktrees>> = [];
try {
existingWorktrees = await listWorktrees(mainRepoPath);
} catch (error) {
console.warn("Failed to list existing worktrees:", error);
}
🤖 Prompt for AI Agents
In apps/desktop/src/lib/trpc/routers/projects/projects.ts around line 79, the
call to listWorktrees can throw and currently will bubble up causing the whole
mutation to fail even after the project was created; wrap the listWorktrees call
in a try-catch, handle errors by logging them (or reporting to a
diagnostic/error-tracking service) and fall back to an empty array or skip
worktree import so the mutation returns success; ensure the catch does not
rethrow and that any subsequent code that expects the worktree list handles the
empty fallback.

Comment on lines +82 to +85
const supersetWorktrees = existingWorktrees.filter(
(wt) =>
wt.path.startsWith(`${supersetWorktreesDir}/`) && existsSync(wt.path),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Cross-platform path separator mismatch.

path.join produces OS-specific separators (backslash on Windows), but the check uses a hardcoded forward slash. On Windows, supersetWorktreesDir would be something like C:\Users\...\worktrees, so startsWith(${supersetWorktreesDir}/) would never match.

+import { sep } from "node:path";
...
 				const supersetWorktrees = existingWorktrees.filter(
 					(wt) =>
-						wt.path.startsWith(`${supersetWorktreesDir}/`) && existsSync(wt.path),
+						wt.path.startsWith(`${supersetWorktreesDir}${sep}`) && existsSync(wt.path),
 				);

Alternatively, normalize both paths before comparison to handle mixed separators from git output.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const supersetWorktrees = existingWorktrees.filter(
(wt) =>
wt.path.startsWith(`${supersetWorktreesDir}/`) && existsSync(wt.path),
);
import { sep } from "node:path";
const supersetWorktrees = existingWorktrees.filter(
(wt) =>
wt.path.startsWith(`${supersetWorktreesDir}${sep}`) && existsSync(wt.path),
);
🤖 Prompt for AI Agents
In apps/desktop/src/lib/trpc/routers/projects/projects.ts around lines 82-85,
the startsWith check uses a hardcoded '/' which fails on Windows; normalize both
paths and use the platform separator before comparing. Fix by normalizing or
resolving supersetWorktreesDir and wt.path (e.g., with
path.resolve/path.normalize), ensure supersetWorktreesDir ends with path.sep,
then test startsWith against that normalized directory; alternatively use
path.relative(supersetWorktreesDir, wt.path) and check it does not start with
'..' to determine containment.

@Kitenite Kitenite closed this Dec 11, 2025
@Kitenite
Kitenite deleted the pearl-wave-37 branch December 12, 2025 02:06

This branch was successfully deployed

1 active deployment
Preview — 24769303 Deployed Dec 1, 2025 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant