@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
55use std:: process:: Command ;
66
77use crate :: models:: Worktree ;
8- use crate :: utils:: { discover_bare_clone, get_project_root} ;
8+ use crate :: utils:: { discover_bare_clone, get_project_root, trim_trailing_branch_slashes } ;
99
1010pub const MAIN_BRANCHES : & [ & str ] = & [ "main" , "master" ] ;
1111pub const DETACHED_HEAD : & str = "detached HEAD" ;
@@ -247,32 +247,36 @@ pub fn find_worktree_by_name(
247247 name : & str ,
248248) -> Result < Option < Worktree > , String > {
249249 let worktrees = list_worktrees ( context) ?;
250+ Ok ( match_worktree_by_name ( & worktrees, name) . cloned ( ) )
251+ }
252+
253+ fn match_worktree_by_name < ' a > ( worktrees : & ' a [ Worktree ] , name : & str ) -> Option < & ' a Worktree > {
254+ let normalized_name = trim_trailing_branch_slashes ( name) ;
255+
256+ if normalized_name. is_empty ( ) {
257+ return None ;
258+ }
250259
251- // First, try exact branch name match
252- if let Some ( wt) = worktrees. iter ( ) . find ( |wt| wt. branch == name ) {
253- return Ok ( Some ( wt. clone ( ) ) ) ;
260+ // First, try exact branch name match.
261+ if let Some ( wt) = worktrees. iter ( ) . find ( |wt| wt. branch == normalized_name ) {
262+ return Some ( wt) ;
254263 }
255264
256- // Try matching by directory name
265+ // Try matching by directory name.
257266 if let Some ( wt) = worktrees. iter ( ) . find ( |wt| {
258267 Path :: new ( & wt. path )
259268 . file_name ( )
260269 . and_then ( |n| n. to_str ( ) )
261- . map ( |n| n == name )
270+ . map ( |n| n == normalized_name )
262271 . unwrap_or ( false )
263272 } ) {
264- return Ok ( Some ( wt. clone ( ) ) ) ;
273+ return Some ( wt) ;
265274 }
266275
267- // Try partial branch name match (suffix matching)
268- if let Some ( wt ) = worktrees
276+ // Try partial branch name match (suffix matching).
277+ worktrees
269278 . iter ( )
270- . find ( |wt| wt. branch . ends_with ( & format ! ( "/{}" , name) ) )
271- {
272- return Ok ( Some ( wt. clone ( ) ) ) ;
273- }
274-
275- Ok ( None )
279+ . find ( |wt| wt. branch . ends_with ( & format ! ( "/{}" , normalized_name) ) )
276280}
277281
278282struct PartialWorktree {
@@ -399,6 +403,20 @@ fn metadata_created_at(meta: &fs::Metadata) -> Option<DateTime<Utc>> {
399403#[ cfg( test) ]
400404mod tests {
401405 use super :: * ;
406+ use chrono:: DateTime ;
407+
408+ fn make_worktree ( path : & str , branch : & str ) -> Worktree {
409+ Worktree {
410+ path : path. to_string ( ) ,
411+ branch : branch. to_string ( ) ,
412+ head : "abc123" . to_string ( ) ,
413+ created_at : DateTime :: from_timestamp ( 0 , 0 ) . unwrap ( ) ,
414+ is_dirty : false ,
415+ is_locked : false ,
416+ is_prunable : false ,
417+ is_main : false ,
418+ }
419+ }
402420
403421 // --- parseWorktreeLines tests ---
404422
@@ -460,4 +478,32 @@ mod tests {
460478 assert ! ( worktrees[ 1 ] . is_locked) ;
461479 assert ! ( worktrees[ 2 ] . is_prunable) ;
462480 }
481+
482+ #[ test]
483+ fn match_worktree_by_name_trims_trailing_slashes ( ) {
484+ let worktrees = vec ! [
485+ make_worktree( "/repo/main" , "main" ) ,
486+ make_worktree( "/repo/feature/my-branch" , "feature/my-branch" ) ,
487+ ] ;
488+
489+ let found = match_worktree_by_name ( & worktrees, "feature/my-branch/" ) ;
490+ assert_eq ! (
491+ found. map( |wt| wt. branch. as_str( ) ) ,
492+ Some ( "feature/my-branch" )
493+ ) ;
494+ }
495+
496+ #[ test]
497+ fn match_worktree_by_name_suffix_match_with_trailing_slash ( ) {
498+ let worktrees = vec ! [ make_worktree(
499+ "/repo/feature/my-branch" ,
500+ "feature/my-branch" ,
501+ ) ] ;
502+
503+ let found = match_worktree_by_name ( & worktrees, "my-branch/" ) ;
504+ assert_eq ! (
505+ found. map( |wt| wt. branch. as_str( ) ) ,
506+ Some ( "feature/my-branch" )
507+ ) ;
508+ }
463509}
0 commit comments