@@ -56,6 +56,7 @@ pub struct BranchWithWorkdir {
5656 pub base_branch : String ,
5757 pub pr_number : Option < u64 > ,
5858 pub worktree_path : Option < String > ,
59+ pub is_main_worktree : bool ,
5960 pub created_at : i64 ,
6061 pub updated_at : i64 ,
6162}
@@ -402,74 +403,72 @@ fn list_projects(
402403}
403404
404405/// Import existing worktrees for a project.
405- ///
406+ ///
406407/// Scans the repository for existing worktrees and creates Branch + Workdir
407408/// records for each one. Skips the main worktree (the repo itself).
408- ///
409+ ///
409410/// Returns the number of worktrees imported.
410411fn import_existing_worktrees (
411412 store : & Arc < Store > ,
412413 project : & store:: Project ,
413414) -> Result < usize , String > {
414415 let repo_path = Path :: new ( & project. repo_path ) ;
415-
416- // Get the default branch to use as base for imported branches
416+
417417 let default_branch = git:: detect_default_branch ( repo_path)
418- . map_err ( |e| format ! ( "Failed to detect default branch: {}" , e) ) ?;
419-
420- // List all worktrees in the repository
421- let worktrees = git:: list_worktrees ( repo_path)
422- . map_err ( |e| format ! ( "Failed to list worktrees: {}" , e) ) ?;
423-
418+ . map_err ( |e| format ! ( "Failed to detect default branch: {e}" ) ) ?;
419+
420+ let worktrees =
421+ git:: list_worktrees ( repo_path) . map_err ( |e| format ! ( "Failed to list worktrees: {e}" ) ) ?;
422+
424423 let mut imported_count = 0 ;
425-
424+
426425 for ( worktree_path, branch_name) in worktrees {
427- // Skip the main worktree (the repo itself)
428- if worktree_path == repo_path {
429- continue ;
430- }
431-
432- // Skip worktrees without a branch (detached HEAD)
433426 let branch_name = match branch_name {
434427 Some ( name) => name,
435428 None => {
436- log:: debug!( "Skipping worktree at {} (detached HEAD)" , worktree_path. display( ) ) ;
429+ log:: debug!(
430+ "Skipping worktree at {} (detached HEAD)" ,
431+ worktree_path. display( )
432+ ) ;
437433 continue ;
438434 }
439435 } ;
440-
441- // Check if we already have a branch with this name
436+
442437 let existing_branches = store
443438 . list_branches_for_project ( & project. id )
444439 . map_err ( |e| e. to_string ( ) ) ?;
445-
446- if existing_branches. iter ( ) . any ( |b| b. branch_name == branch_name) {
447- log:: debug!( "Branch '{}' already exists, skipping import" , branch_name) ;
440+
441+ if existing_branches
442+ . iter ( )
443+ . any ( |b| b. branch_name == branch_name)
444+ {
445+ log:: debug!( "Branch '{branch_name}' already exists, skipping import" ) ;
448446 continue ;
449447 }
450-
451- // Create a Branch record
448+
452449 let branch = store:: Branch :: new ( & project. id , & branch_name, & default_branch) ;
453- store. create_branch ( & branch) . map_err ( |e| {
454- format ! ( "Failed to create branch '{}': {}" , branch_name, e)
455- } ) ?;
456-
457- // Create a Workdir record linked to this branch
450+ store
451+ . create_branch ( & branch)
452+ . map_err ( |e| format ! ( "Failed to create branch '{branch_name}': {e}" ) ) ?;
453+
458454 let worktree_str = worktree_path
459455 . to_str ( )
460456 . ok_or_else ( || format ! ( "Invalid worktree path: {}" , worktree_path. display( ) ) ) ?;
461-
462- let workdir = store:: Workdir :: new ( & project. id , worktree_str)
463- . with_branch ( & branch. id ) ;
464-
465- store. create_workdir ( & workdir) . map_err ( |e| {
466- format ! ( "Failed to create workdir for '{}': {}" , branch_name, e)
467- } ) ?;
468-
469- log:: info!( "Imported existing worktree: {} -> {}" , branch_name, worktree_path. display( ) ) ;
457+
458+ let workdir = store:: Workdir :: new ( & project. id , worktree_str) . with_branch ( & branch. id ) ;
459+
460+ store
461+ . create_workdir ( & workdir)
462+ . map_err ( |e| format ! ( "Failed to create workdir for '{branch_name}': {e}" ) ) ?;
463+
464+ log:: info!(
465+ "Imported worktree: {} -> {}" ,
466+ branch_name,
467+ worktree_path. display( )
468+ ) ;
470469 imported_count += 1 ;
471470 }
472-
471+
473472 Ok ( imported_count)
474473}
475474
@@ -484,14 +483,27 @@ fn create_project(
484483 // Validate that the path is a git repo
485484 let path = Path :: new ( & repo_path) ;
486485 if !path. join ( ".git" ) . exists ( ) && !path. is_dir ( ) {
487- return Err ( format ! ( "Not a git repository: {}" , repo_path ) ) ;
486+ return Err ( format ! ( "Not a git repository: {repo_path}" ) ) ;
488487 }
489488
490- // Check for duplicate
489+ // Check for duplicate — still import worktrees even for existing projects,
490+ // since they may have been added before this feature existed.
491491 if let Some ( existing) = store
492492 . get_project_by_repo ( & repo_path)
493493 . map_err ( |e| e. to_string ( ) ) ?
494494 {
495+ match import_existing_worktrees ( & store, & existing) {
496+ Ok ( count) if count > 0 => {
497+ log:: info!(
498+ "Imported {count} worktree(s) for existing project '{}'" ,
499+ existing. repo_path
500+ ) ;
501+ }
502+ Err ( e) => {
503+ log:: warn!( "Failed to import worktrees for existing project: {e}" ) ;
504+ }
505+ _ => { }
506+ }
495507 return Ok ( existing) ;
496508 }
497509
@@ -500,20 +512,23 @@ fn create_project(
500512 project = project. with_subpath ( sub) ;
501513 }
502514 store. create_project ( & project) . map_err ( |e| e. to_string ( ) ) ?;
503-
515+
504516 // Import any existing worktrees for this project
505517 match import_existing_worktrees ( & store, & project) {
506518 Ok ( count) => {
507519 if count > 0 {
508- log:: info!( "Imported {} existing worktree(s) for project '{}'" , count, project. repo_path) ;
520+ log:: info!(
521+ "Imported {count} existing worktree(s) for project '{}'" ,
522+ project. repo_path
523+ ) ;
509524 }
510525 }
511526 Err ( e) => {
512527 // Log the error but don't fail project creation
513- log:: warn!( "Failed to import existing worktrees: {}" , e ) ;
528+ log:: warn!( "Failed to import existing worktrees: {e}" ) ;
514529 }
515530 }
516-
531+
517532 Ok ( project)
518533}
519534
@@ -537,6 +552,15 @@ fn list_branches_for_project(
537552 project_id : String ,
538553) -> Result < Vec < BranchWithWorkdir > , String > {
539554 let store = get_store ( & store) ?;
555+ let project = store
556+ . get_project ( & project_id)
557+ . map_err ( |e| e. to_string ( ) ) ?
558+ . ok_or_else ( || format ! ( "Project not found: {project_id}" ) ) ?;
559+
560+ let canonical_repo = Path :: new ( & project. repo_path )
561+ . canonicalize ( )
562+ . unwrap_or_else ( |_| PathBuf :: from ( & project. repo_path ) ) ;
563+
540564 let branches = store
541565 . list_branches_for_project ( & project_id)
542566 . map_err ( |e| e. to_string ( ) ) ?;
@@ -547,13 +571,21 @@ fn list_branches_for_project(
547571 . get_workdir_for_branch ( & branch. id )
548572 . map_err ( |e| e. to_string ( ) ) ?;
549573
574+ let is_main = workdir. as_ref ( ) . is_some_and ( |w| {
575+ Path :: new ( & w. path )
576+ . canonicalize ( )
577+ . unwrap_or_else ( |_| PathBuf :: from ( & w. path ) )
578+ == canonical_repo
579+ } ) ;
580+
550581 result. push ( BranchWithWorkdir {
551582 id : branch. id ,
552583 project_id : branch. project_id ,
553584 branch_name : branch. branch_name ,
554585 base_branch : branch. base_branch ,
555586 pr_number : branch. pr_number ,
556587 worktree_path : workdir. map ( |w| w. path ) ,
588+ is_main_worktree : is_main,
557589 created_at : branch. created_at ,
558590 updated_at : branch. updated_at ,
559591 } ) ;
@@ -574,7 +606,7 @@ fn create_branch(
574606 let project = store
575607 . get_project ( & project_id)
576608 . map_err ( |e| e. to_string ( ) ) ?
577- . ok_or_else ( || format ! ( "Project not found: {}" , project_id ) ) ?;
609+ . ok_or_else ( || format ! ( "Project not found: {project_id}" ) ) ?;
578610
579611 let repo_path = Path :: new ( & project. repo_path ) ;
580612
@@ -608,6 +640,7 @@ fn create_branch(
608640 base_branch : branch. base_branch ,
609641 pr_number : branch. pr_number ,
610642 worktree_path : Some ( worktree_str) ,
643+ is_main_worktree : false ,
611644 created_at : branch. created_at ,
612645 updated_at : branch. updated_at ,
613646 } )
@@ -624,19 +657,31 @@ fn delete_branch(
624657 let branch = store
625658 . get_branch ( & branch_id)
626659 . map_err ( |e| e. to_string ( ) ) ?
627- . ok_or_else ( || format ! ( "Branch not found: {}" , branch_id ) ) ?;
660+ . ok_or_else ( || format ! ( "Branch not found: {branch_id}" ) ) ?;
628661
629662 // Get the project for the repo path
630663 let project = store
631664 . get_project ( & branch. project_id )
632665 . map_err ( |e| e. to_string ( ) ) ?
633666 . ok_or_else ( || format ! ( "Project not found: {}" , branch. project_id) ) ?;
634667
635- // Get the workdir (if any) so we can remove the worktree
636668 let workdir = store
637669 . get_workdir_for_branch ( & branch_id)
638670 . map_err ( |e| e. to_string ( ) ) ?;
639671
672+ // Prevent deletion of the main worktree (the repo checkout itself)
673+ if let Some ( ref wd) = workdir {
674+ let canonical_repo = Path :: new ( & project. repo_path )
675+ . canonicalize ( )
676+ . unwrap_or_else ( |_| PathBuf :: from ( & project. repo_path ) ) ;
677+ let canonical_wd = Path :: new ( & wd. path )
678+ . canonicalize ( )
679+ . unwrap_or_else ( |_| PathBuf :: from ( & wd. path ) ) ;
680+ if canonical_wd == canonical_repo {
681+ return Err ( "Cannot delete the main worktree" . to_string ( ) ) ;
682+ }
683+ }
684+
640685 if let Some ( ref wd) = workdir {
641686 let repo_path = Path :: new ( & project. repo_path ) ;
642687 let worktree_path = Path :: new ( & wd. path ) ;
@@ -664,7 +709,7 @@ fn delete_note(
664709 let note = store
665710 . get_note ( & note_id)
666711 . map_err ( |e| e. to_string ( ) ) ?
667- . ok_or_else ( || format ! ( "Note not found: {}" , note_id ) ) ?;
712+ . ok_or_else ( || format ! ( "Note not found: {note_id}" ) ) ?;
668713
669714 store. delete_note ( & note_id) . map_err ( |e| e. to_string ( ) ) ?;
670715
@@ -694,7 +739,7 @@ fn delete_commit(
694739 let workdir = store
695740 . get_workdir_for_branch ( & branch_id)
696741 . map_err ( |e| e. to_string ( ) ) ?
697- . ok_or_else ( || format ! ( "No worktree for branch: {}" , branch_id ) ) ?;
742+ . ok_or_else ( || format ! ( "No worktree for branch: {branch_id}" ) ) ?;
698743
699744 let worktree = Path :: new ( & workdir. path ) ;
700745
@@ -744,7 +789,7 @@ fn get_branch_timeline(
744789 let branch = store
745790 . get_branch ( & branch_id)
746791 . map_err ( |e| e. to_string ( ) ) ?
747- . ok_or_else ( || format ! ( "Branch not found: {}" , branch_id ) ) ?;
792+ . ok_or_else ( || format ! ( "Branch not found: {branch_id}" ) ) ?;
748793
749794 let workdir = store
750795 . get_workdir_for_branch ( & branch_id)
@@ -877,12 +922,12 @@ fn resolve_branch_context(
877922 let branch = store
878923 . get_branch ( branch_id)
879924 . map_err ( |e| e. to_string ( ) ) ?
880- . ok_or_else ( || format ! ( "Branch not found: {}" , branch_id ) ) ?;
925+ . ok_or_else ( || format ! ( "Branch not found: {branch_id}" ) ) ?;
881926
882927 let workdir = store
883928 . get_workdir_for_branch ( branch_id)
884929 . map_err ( |e| e. to_string ( ) ) ?
885- . ok_or_else ( || format ! ( "No worktree for branch: {}" , branch_id ) ) ?;
930+ . ok_or_else ( || format ! ( "No worktree for branch: {branch_id}" ) ) ?;
886931
887932 Ok ( BranchDiffContext {
888933 worktree_path : workdir. path ,
@@ -1017,7 +1062,7 @@ async fn ensure_review(
10171062) -> Result < store:: Review , String > {
10181063 let store = get_store ( & store) ?;
10191064 let review_scope =
1020- store:: ReviewScope :: parse ( & scope) . ok_or_else ( || format ! ( "Invalid scope: {}" , scope ) ) ?;
1065+ store:: ReviewScope :: parse ( & scope) . ok_or_else ( || format ! ( "Invalid scope: {scope}" ) ) ?;
10211066
10221067 store
10231068 . ensure_review ( & branch_id, & commit_sha, review_scope)
0 commit comments