Skip to content

desktop (fix): init git if project has no git#265

Merged
AviPeltz merged 8 commits intomainfrom
mad-lungfish-c185ce
Dec 7, 2025
Merged

desktop (fix): init git if project has no git#265
AviPeltz merged 8 commits intomainfrom
mad-lungfish-c185ce

Conversation

@AviPeltz
Copy link
Copy Markdown
Collaborator

@AviPeltz AviPeltz commented Dec 6, 2025

Description

Related Issues

Type of Change

  • [ x] Bug fix
  • [ x] New feature
  • Documentation
  • [ x] Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Additional Notes

Summary by CodeRabbit

  • New Features

    • Initialize Git repositories from the app via a new dialog when opening non-repo folders.
    • New "init and open" flow: initialize a repo, determine its default branch, then open/create the project and workspace.
  • Bug Fixes / Improvements

    • Clearer feedback when a folder needs Git initialization or when opening fails.
    • Project records now update default branch and last-opened timestamps automatically.
    • Worktree creation is remote-aware and falls back gracefully when no origin is present.

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

@vercel
Copy link
Copy Markdown

vercel Bot commented Dec 6, 2025

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

Project Deployment Preview Comments Updated (UTC)
website Ready Ready Preview Comment Dec 7, 2025 4:42am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 6, 2025

Warning

Rate limit exceeded

@AviPeltz has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 21 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 84f92c2 and 3b8f5a9.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (1 hunks)

Walkthrough

Open-new flow now returns a typed result that can require Git initialization; a new initGitAndOpen mutation initializes a repo and upserts a project; worktree start-point logic is remote-aware; UI dialog and client flows surface the new init flow and explicit error/result handling.

Changes

Cohort / File(s) Summary
Projects router
apps/desktop/src/lib/trpc/routers/projects/projects.ts
Adds OpenNewResult union types (OpenNewCanceled, OpenNewSuccess, OpenNewNeedsGitInit, OpenNewError); adds upsertProject(mainRepoPath, defaultBranch); changes openNew to return OpenNewResult and emit needsGitInit for non-git paths; adds initGitAndOpen mutation and helper extractRepoName() plus SAFE_REPO_NAME_REGEX.
Git utilities
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
Exports hasOriginRemote(mainRepoPath): Promise<boolean> to detect whether an "origin" remote exists.
Worktrees logic
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
Makes new worktree start point remote-aware: if origin exists, tries a fetch and uses origin/<defaultBranch> as start point; otherwise uses local defaultBranch. Fetch is guarded by hasOriginRemote() with graceful fallbacks.
Init Git UI
apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
New React component prompting to initialize Git for a selected folder; calls initGitAndOpen, then useCreateWorkspace on success; manages processing state, errors, keyboard/backdrop interactions, and recents invalidation.
Start view integration
apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
Imports/renders InitGitDialog; tracks dialog visibility and selected path; opens dialog when openNew returns needsGitInit; otherwise continues existing workspace-creation flow.
Workspace dropdown handling
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
Replaces unconditional workspace creation with explicit handling for canceled, error, and needsGitInit cases; shows specific toasts and only creates workspace when result.project is present.

Sequence Diagram

sequenceDiagram
    actor User
    participant StartView as StartView UI
    participant TRPC as TRPC Server
    participant Projects as Projects Router
    participant GitUtil as Git Utils
    participant InitDialog as InitGitDialog UI
    participant Workspaces as Workspace Router

    User->>StartView: Click "Open Project" (selectedPath)
    StartView->>TRPC: openNew(selectedPath)
    TRPC->>GitUtil: resolve git root / check repo
    alt selectedPath is not a git repo
        TRPC-->>StartView: OpenNewNeedsGitInit(selectedPath)
        StartView->>InitDialog: show InitGitDialog(selectedPath)
        User->>InitDialog: Click "Initialize Git"
        InitDialog->>TRPC: initGitAndOpen(path)
        TRPC->>GitUtil: git init, create initial commit, detect defaultBranch
        TRPC->>Projects: upsertProject(path, defaultBranch)
        Projects-->>TRPC: Project
        TRPC-->>InitDialog: { project }
        InitDialog->>Workspaces: createWorkspace(project.id)
        Workspaces-->>InitDialog: Workspace created
        InitDialog-->>StartView: close dialog / success
    else selectedPath is a git repo
        TRPC->>Projects: upsertProject(path, defaultBranch)
        Projects-->>TRPC: Project
        TRPC-->>StartView: OpenNewSuccess(project)
        StartView->>Workspaces: createWorkspace(project.id)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Verify exhaustive handling of the new OpenNewResult union at all call sites.
  • Review git init/commit/default-branch detection and fallbacks (older Git versions).
  • Inspect hasOriginRemote + fetch logic in worktree creation for network/offline error handling.
  • Check UI async unmount guards, loading state, and recents cache invalidation in InitGitDialog.

Possibly related PRs

Poem

🐰 A path without a git to show,
I nudged and made a brand new repo.
One tiny hop, a first small commit,
Now branches sprout and projects fit.
hop hop 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is largely empty with only checkboxes marked but no actual content describing the changes, impact, testing, or related issues. Fill in the Description section explaining the problem being solved and the solution implemented; add Related Issues links; and include Testing details describing how the changes were validated.
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding Git initialization when a project has no Git repository, which is the core functionality across the modified files.

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.

@AviPeltz AviPeltz marked this pull request as ready for review December 7, 2025 04:15
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 7, 2025

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 (2)
apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (2)

53-72: Consider awaiting workspace creation before closing the dialog.

onClose() is called immediately after triggering createWorkspace.mutate(), which means the dialog closes before the workspace is actually created. If workspace creation fails, the user won't see the error since the dialog is already closed.

Consider using mutateAsync and awaiting it, or moving onClose() into the workspace mutation's success callback:

 const handleInitGit = () => {
   if (isLoading) return; // Prevent double-clicks
   initGitAndOpen.mutate(
     { path: selectedPath },
     {
       onSuccess: (result) => {
         if (result.project) {
           utils.projects.getRecents.invalidate();
-          createWorkspace.mutate({ projectId: result.project.id });
-          onClose();
+          createWorkspace.mutate(
+            { projectId: result.project.id },
+            {
+              onSuccess: () => onClose(),
+              onError: (err) => onError(err.message || "Failed to create workspace"),
+            },
+          );
         } else {
           onError("Unexpected error: project was not created");
         }
       },
       onError: (err) => {
         onError(err.message || "Failed to initialize git repository");
       },
     },
   );
 };

6-11: Minor: Consider extracting or co-locating shared path utilities.

getBasename duplicates similar path normalization logic found in StartView/index.tsx (normalizeSeparators) and WorkspaceDropdown.tsx (formatPath). If this pattern grows, consider extracting to a shared utility.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ddc5f18 and cdc639d.

📒 Files selected for processing (6)
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts (3 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (2 hunks)
  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx (3 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
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/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.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/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Co-locate component dependencies (utils, hooks, constants, config, tests, stories) next to the file using them
Avoid any type in TypeScript unless absolutely necessary

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
**/{components,pages}/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/{components,pages}/**/*.tsx: Use one folder per component with structure ComponentName/ComponentName.tsx plus index.ts for barrel export
Create one component per file - do not use multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
apps/desktop/src/{renderer,shared,preload}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in renderer process code or shared code in the desktop app

Files:

  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
🧬 Code graph analysis (4)
apps/desktop/src/renderer/screens/main/components/StartView/index.tsx (1)
apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (1)
  • InitGitDialog (20-117)
apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (2)
apps/website/src/app/api/trpc/[trpc]/route.ts (1)
  • onError (26-28)
packages/ui/src/components/button.tsx (1)
  • Button (61-61)
apps/desktop/src/lib/trpc/routers/projects/projects.ts (3)
apps/desktop/src/main/lib/db/schemas.ts (1)
  • Project (1-11)
apps/desktop/src/lib/trpc/routers/projects/utils/colors/colors.ts (1)
  • assignRandomColor (3-7)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1)
  • getDefaultBranch (302-331)
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (3)
  • hasOriginRemote (286-294)
  • fetchDefaultBranch (339-347)
  • createWorktree (132-223)
⏰ 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 (10)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1)

283-294: LGTM!

Clean implementation that correctly checks for an 'origin' remote. The silent error handling is appropriate since the function is designed to return a boolean, and errors (e.g., invalid repo path) should be treated the same as "no origin".

apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (1)

60-82: LGTM!

Good implementation of remote-aware start point logic. The fallback to local defaultBranch for repos without origin is essential for the new Git initialization workflow. The best-effort fetch with silent failure is appropriate since the local branch still exists.

apps/desktop/src/renderer/screens/main/components/StartView/index.tsx (3)

60-63: LGTM!

Clean state shape for tracking the dialog's visibility and selected path.


67-95: LGTM!

Good handling of all OpenNewResult variants. The early returns for canceled, error, and needsGitInit keep the logic clean and readable.


272-277: LGTM!

Dialog integration is correct. The onClose callback properly resets both isOpen and selectedPath.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx (1)

97-125: LGTM!

Good defensive handling of all result variants. The decision to show a toast directing users to the Start view (rather than opening a dialog inline) is a reasonable UX choice for this secondary entry point.

apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (1)

78-116: LGTM!

Clean modal implementation with proper accessibility considerations (biome-ignore for the backdrop pattern is appropriate). The loading state is correctly reflected in button text and disabled states.

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

16-29: LGTM!

Clean discriminated union types for OpenNewResult. The structure makes it easy for consumers to narrow the result type using property checks.


154-189: LGTM!

The updated openNew mutation correctly returns the needsGitInit variant when getGitRoot fails, allowing the UI to offer Git initialization.


191-236: Good fallback handling for Git version differences.

The fallback from --initial-branch=main to plain init correctly handles older Git versions. The error detection for missing user config is helpful. After the fallback git.init(), the code correctly handles the case where the default branch might be master instead of main by reading branchSummary.current.

Comment on lines +36 to +71
async function upsertProject(
mainRepoPath: string,
defaultBranch: string,
): Promise<Project> {
const name = basename(mainRepoPath);

let project = db.data.projects.find((p) => p.mainRepoPath === mainRepoPath);

if (project) {
await db.update((data) => {
const p = data.projects.find((p) => p.id === project?.id);
if (p) {
p.lastOpenedAt = Date.now();
p.defaultBranch = defaultBranch;
}
});
} else {
project = {
id: nanoid(),
mainRepoPath,
name,
color: assignRandomColor(),
tabOrder: null,
lastOpenedAt: Date.now(),
createdAt: Date.now(),
defaultBranch,
};

await db.update((data) => {
// biome-ignore lint/style/noNonNullAssertion: project is assigned above, TypeScript can't see it inside callback
data.projects.push(project!);
});
}

return project;
}
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

Potential stale reference when returning updated project.

When updating an existing project, the function modifies the database but returns the original project reference from line 42, which won't reflect the updated lastOpenedAt and defaultBranch values.

Consider re-fetching the project after update or constructing the return value with updated fields:

 if (project) {
   await db.update((data) => {
     const p = data.projects.find((p) => p.id === project?.id);
     if (p) {
       p.lastOpenedAt = Date.now();
       p.defaultBranch = defaultBranch;
     }
   });
+  // Re-fetch to get updated values
+  project = db.data.projects.find((p) => p.mainRepoPath === mainRepoPath);
 }
🤖 Prompt for AI Agents
In apps/desktop/src/lib/trpc/routers/projects/projects.ts around lines 36 to 71,
when updating an existing project the function currently returns the stale local
`project` reference that doesn't include the updated `lastOpenedAt` and
`defaultBranch`; re-fetch the project from `db.data.projects` after the `await
db.update(...)` completes (or build a new object merging the original `project`
with the updated fields) and return that up-to-date object so the caller
receives the updated values.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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/renderer/screens/main/components/StartView/InitGitDialog.tsx (1)

1-129: Consider reorganizing to match component folder structure guideline.

Per the coding guidelines, components should follow the pattern ComponentName/ComponentName.tsx with an index.ts for barrel exports.

Current structure:

StartView/InitGitDialog.tsx

Recommended structure:

StartView/InitGitDialog/InitGitDialog.tsx
StartView/InitGitDialog/index.ts

This improves organization and makes it easier to co-locate related files (tests, utilities, etc.) with the component.

Based on coding guidelines: "Use one folder per component with structure ComponentName/ComponentName.tsx plus index.ts for barrel export"

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cdc639d and ef3f5b9.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
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/renderer/screens/main/components/StartView/InitGitDialog.tsx
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/renderer/screens/main/components/StartView/InitGitDialog.tsx
**/{components,pages}/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/{components,pages}/**/*.tsx: Use one folder per component with structure ComponentName/ComponentName.tsx plus index.ts for barrel export
Create one component per file - do not use multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Co-locate component dependencies (utils, hooks, constants, config, tests, stories) next to the file using them
Avoid any type in TypeScript unless absolutely necessary

Files:

  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
apps/desktop/src/{renderer,shared,preload}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in renderer process code or shared code in the desktop app

Files:

  • apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx
🧬 Code graph analysis (1)
apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (2)
apps/website/src/app/api/trpc/[trpc]/route.ts (1)
  • onError (26-28)
packages/ui/src/components/button.tsx (1)
  • Button (61-61)
⏰ 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 (7)
apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx (7)

1-4: LGTM! Imports follow Electron and TypeScript guidelines.

The imports correctly use tRPC for IPC, leverage path aliases, and avoid Node.js modules in renderer code.


6-11: LGTM! Cross-platform path handling is correct.

The utility properly normalizes both Unix and Windows paths and handles edge cases appropriately.


13-18: LGTM! Props interface is well-defined.

Clear, type-safe interface with appropriate props for a modal dialog component.


33-44: LGTM! Proper Escape key handling with cleanup.

This is a legitimate use of useEffect for keyboard event handling, which is necessary per the guidelines. The cleanup prevents memory leaks.


46-51: LGTM! Correct backdrop dismiss pattern.

The handler properly distinguishes backdrop clicks from content clicks and prevents dismissal during loading.


53-84: LGTM! Robust async flow with proper error handling.

The mutation sequence is well-structured with appropriate error boundaries, user-friendly messages, and intentional non-blocking cache invalidation. The double-click guard is a nice touch.


26-30: This review comment references code that does not exist in the repository.

The file apps/desktop/src/renderer/screens/main/components/StartView/InitGitDialog.tsx and all hooks/mutations mentioned (useCreateWorkspace, initGitAndOpen, trpc.useUtils()) are not found in the codebase. This may be a stale review comment from a previous PR state, or the paths have changed since the review was created.

Likely an incorrect or invalid review comment.

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