@@ -183,6 +183,10 @@ pub struct SyncPlan {
183183 pub local_changes : Vec < LocalChange > ,
184184 pub remote_changes : Vec < RemoteChange > ,
185185 pub warnings : Vec < String > ,
186+ /// Branches that will be unmounted (for checkout logic if user is on one)
187+ pub branches_to_unmount : Vec < String > ,
188+ /// Branches safe to delete locally (work preserved on remote)
189+ pub branches_to_delete : Vec < String > ,
186190}
187191
188192impl SyncPlan {
@@ -712,9 +716,10 @@ fn compute_sync_plan(
712716 }
713717 }
714718
715- // Detect merged PRs and handle unmounting (pull direction)
719+ // Detect merged/closed PRs and handle unmounting (pull direction)
716720 // This runs regardless of push_only/pull_only since it's about reconciling state
717721 let mut branches_to_unmount: Vec < ( String , String ) > = Vec :: new ( ) ; // (branch, repoint_to)
722+ let mut branches_to_delete: Vec < String > = Vec :: new ( ) ;
718723
719724 tracing:: debug!(
720725 "Checking for merged PRs. Local branches: {:?}, Closed PRs: {:?}" ,
@@ -738,14 +743,31 @@ fn compute_sync_plan(
738743 closed_pr. number,
739744 closed_pr. state
740745 ) ;
741- if closed_pr. state == RemotePrState :: Merged {
742- // This branch's PR was merged - it should be unmounted
746+
747+ // Any closed PR (merged or just closed) should unmount from git-stack
748+ if matches ! ( closed_pr. state, RemotePrState :: Merged | RemotePrState :: Closed ) {
749+ // This branch's PR was merged/closed - it should be unmounted
743750 // Children should be repointed to this branch's parent
744751 let repoint_to = local_branch
745752 . parent
746753 . clone ( )
747754 . unwrap_or_else ( || local. trunk . clone ( ) ) ;
748755 branches_to_unmount. push ( ( branch_name. clone ( ) , repoint_to) ) ;
756+
757+ // Determine if local branch is safe to delete
758+ // Safe if: merged, OR (closed AND remote exists AND local is ancestor of remote)
759+ let safe_to_delete = if closed_pr. state == RemotePrState :: Merged {
760+ true
761+ } else {
762+ // Closed but not merged - check if remote has our work
763+ let remote_ref = format ! ( "{}/{}" , DEFAULT_REMOTE , branch_name) ;
764+ git_repo. ref_exists ( & remote_ref)
765+ && git_repo. is_ancestor ( branch_name, & remote_ref) . unwrap_or ( false )
766+ } ;
767+
768+ if safe_to_delete {
769+ branches_to_delete. push ( branch_name. clone ( ) ) ;
770+ }
749771 }
750772 }
751773 }
@@ -953,6 +975,11 @@ fn compute_sync_plan(
953975 local_changes,
954976 remote_changes,
955977 warnings,
978+ branches_to_unmount : branches_to_unmount
979+ . iter ( )
980+ . map ( |( name, _) | name. clone ( ) )
981+ . collect ( ) ,
982+ branches_to_delete,
956983 }
957984}
958985
@@ -1037,6 +1064,38 @@ fn apply_plan(
10371064 repo_id : & RepoIdentifier ,
10381065 plan : & SyncPlan ,
10391066) -> Result < ( ) > {
1067+ // If current branch is being unmounted, checkout a safe ancestor first
1068+ if !plan. branches_to_unmount . is_empty ( ) {
1069+ let current_branch = git_repo. current_branch ( ) . unwrap_or_default ( ) ;
1070+ let unmount_set: HashSet < & str > = plan
1071+ . branches_to_unmount
1072+ . iter ( )
1073+ . map ( |s| s. as_str ( ) )
1074+ . collect ( ) ;
1075+
1076+ if unmount_set. contains ( current_branch. as_str ( ) ) {
1077+ // Find the first ancestor that's NOT being unmounted
1078+ let trunk = git_trunk ( git_repo) ?;
1079+ let mut safe_branch = trunk. main_branch . clone ( ) ;
1080+ let mut current = current_branch. clone ( ) ;
1081+
1082+ while let Some ( parent) = state. get_parent_branch_of ( repo, & current) {
1083+ if !unmount_set. contains ( parent. name . as_str ( ) ) {
1084+ safe_branch = parent. name . clone ( ) ;
1085+ break ;
1086+ }
1087+ current = parent. name . clone ( ) ;
1088+ }
1089+
1090+ println ! (
1091+ "Branch {} was merged. Switching to {}..." ,
1092+ current_branch. yellow( ) ,
1093+ safe_branch. green( )
1094+ ) ;
1095+ run_git ( & [ "checkout" , & safe_branch] ) ?;
1096+ }
1097+ }
1098+
10401099 // Apply local changes first (checkout, mount, update pr_number)
10411100 for change in & plan. local_changes {
10421101 apply_local_change ( git_repo, state, repo, change) ?;
@@ -1053,6 +1112,16 @@ fn apply_plan(
10531112 // Save state again if PR numbers were updated
10541113 state. save_state ( ) ?;
10551114
1115+ // Delete local branches that are safe to delete (work preserved on remote)
1116+ for branch_name in & plan. branches_to_delete {
1117+ if git_repo. branch_exists ( branch_name) {
1118+ println ! ( "Deleting local branch {}..." , branch_name. yellow( ) ) ;
1119+ if let Err ( e) = run_git ( & [ "branch" , "-D" , branch_name] ) {
1120+ tracing:: warn!( "Failed to delete local branch {}: {}" , branch_name, e) ;
1121+ }
1122+ }
1123+ }
1124+
10561125 Ok ( ( ) )
10571126}
10581127
0 commit comments