@@ -118,6 +118,111 @@ impl Command for CleanBranchesCommand {
118118}
119119
120120impl GitCommand for CleanBranchesCommand { }
121+
122+ /// Async parallel version of CleanBranchesCommand
123+ pub struct AsyncCleanBranchesCommand {
124+ dry_run : bool ,
125+ }
126+
127+ impl AsyncCleanBranchesCommand {
128+ pub fn new ( dry_run : bool ) -> Self {
129+ Self { dry_run }
130+ }
131+
132+ pub async fn execute_parallel ( & self ) -> Result < String > {
133+ use crate :: core:: { git:: AsyncGitOperations , safety:: Safety } ;
134+
135+ // Get merged branches and current branch in parallel
136+ let ( merged_branches_result, current_branch_result) = tokio:: try_join!(
137+ AsyncGitOperations :: merged_branches( ) ,
138+ AsyncGitOperations :: current_branch( )
139+ ) ?;
140+
141+ let branches_to_delete: Vec < String > = merged_branches_result
142+ . into_iter ( )
143+ . filter ( |branch| branch != & current_branch_result)
144+ . filter ( |branch| !Self :: is_protected_branch ( branch) )
145+ . collect ( ) ;
146+
147+ if branches_to_delete. is_empty ( ) {
148+ return Ok ( "No merged branches to delete." . to_string ( ) ) ;
149+ }
150+
151+ if self . dry_run {
152+ let mut result = format ! (
153+ "🧪 (dry run) {} branches would be deleted:\n " ,
154+ branches_to_delete. len( )
155+ ) ;
156+ for branch in & branches_to_delete {
157+ result. push_str ( & format ! ( "(dry run) Would delete: {branch}\n " ) ) ;
158+ }
159+ return Ok ( result) ;
160+ }
161+
162+ // Confirm deletion
163+ let details = format ! (
164+ "This will delete {} merged branches: {}" ,
165+ branches_to_delete. len( ) ,
166+ branches_to_delete. join( ", " )
167+ ) ;
168+
169+ if !Safety :: confirm_destructive_operation ( "Clean merged branches" , & details) ? {
170+ return Ok ( "Operation cancelled by user." . to_string ( ) ) ;
171+ }
172+
173+ // Delete branches in parallel (but carefully)
174+ let delete_tasks = branches_to_delete
175+ . iter ( )
176+ . map ( |branch| self . delete_branch_async ( branch. clone ( ) ) ) ;
177+
178+ let results = futures:: future:: join_all ( delete_tasks) . await ;
179+
180+ let mut deleted = Vec :: new ( ) ;
181+ let mut failed = Vec :: new ( ) ;
182+
183+ for ( branch, result) in branches_to_delete. iter ( ) . zip ( results. iter ( ) ) {
184+ match result {
185+ Ok ( true ) => deleted. push ( branch. clone ( ) ) ,
186+ Ok ( false ) | Err ( _) => failed. push ( branch. clone ( ) ) ,
187+ }
188+ }
189+
190+ let mut result = String :: new ( ) ;
191+
192+ if !deleted. is_empty ( ) {
193+ result. push_str ( & format ! ( "✅ Deleted {} branches:\n " , deleted. len( ) ) ) ;
194+ for branch in deleted {
195+ result. push_str ( & format ! ( " 🗑️ {branch}\n " ) ) ;
196+ }
197+ }
198+
199+ if !failed. is_empty ( ) {
200+ result. push_str ( & format ! ( "❌ Failed to delete {} branches:\n " , failed. len( ) ) ) ;
201+ for branch in failed {
202+ result. push_str ( & format ! ( " ⚠️ {branch}\n " ) ) ;
203+ }
204+ }
205+
206+ Ok ( result)
207+ }
208+
209+ async fn delete_branch_async ( & self , branch : String ) -> Result < bool > {
210+ use crate :: core:: git:: AsyncGitOperations ;
211+
212+ match AsyncGitOperations :: run_status ( & [ "branch" , "-d" , & branch] ) . await {
213+ Ok ( _) => Ok ( true ) ,
214+ Err ( _) => Ok ( false ) ,
215+ }
216+ }
217+
218+ fn get_protected_branches ( ) -> Vec < & ' static str > {
219+ vec ! [ "main" , "master" , "develop" ]
220+ }
221+
222+ fn is_protected_branch ( branch : & str ) -> bool {
223+ Self :: get_protected_branches ( ) . contains ( & branch)
224+ }
225+ }
121226impl DryRunnable for CleanBranchesCommand {
122227 fn execute_dry_run ( & self ) -> Result < String > {
123228 CleanBranchesCommand :: new ( true ) . execute ( )
0 commit comments