cascade_cli/cli/commands/
cleanup.rs1use crate::cli::output::Output;
2use crate::errors::{CascadeError, Result};
3use crate::git::GitRepository;
4use chrono::{DateTime, Utc};
5use std::env;
6
7pub async fn run(execute: bool, force: bool) -> Result<()> {
9 let repo_path = env::current_dir()
10 .map_err(|e| CascadeError::config(format!("Failed to get current directory: {e}")))?;
11
12 let git_repo = GitRepository::open(&repo_path)?;
13
14 Output::section("๐งน Scanning for orphaned temporary branches");
15
16 let all_branches = git_repo.list_branches()?;
18 let temp_branches: Vec<String> = all_branches
19 .iter()
20 .filter(|b| b.contains("-temp-"))
21 .cloned()
22 .collect();
23
24 if temp_branches.is_empty() {
25 Output::success("โ No orphaned temporary branches found");
26 return Ok(());
27 }
28
29 Output::info(format!("Found {} temporary branches:", temp_branches.len()));
30
31 for branch_name in &temp_branches {
33 let branch_info = analyze_temp_branch(&git_repo, branch_name)?;
34
35 Output::sub_item(format!(" {} {}", branch_name, branch_info));
36 }
37
38 println!(); if !execute {
41 Output::warning("๐ DRY RUN MODE - No branches will be deleted");
42 Output::info("Run with --execute to actually delete these branches");
43 Output::info("Use --force to delete branches with unmerged commits");
44 return Ok(());
45 }
46
47 Output::section(format!(
49 "Deleting {} temporary branches...",
50 temp_branches.len()
51 ));
52
53 let mut deleted = 0;
54 let mut failed = 0;
55
56 for branch_name in &temp_branches {
57 match git_repo.delete_branch_unsafe(branch_name) {
58 Ok(_) => {
59 Output::success(format!("โ Deleted: {}", branch_name));
60 deleted += 1;
61 }
62 Err(e) if !force => {
63 Output::warning(format!("โ ๏ธ Skipped: {} ({})", branch_name, e));
64 Output::sub_item(" Use --force to delete branches with unmerged commits");
65 failed += 1;
66 }
67 Err(e) => {
68 Output::error(format!("โ Failed to delete: {} ({})", branch_name, e));
69 failed += 1;
70 }
71 }
72 }
73
74 println!(); if deleted > 0 {
77 Output::success(format!("โ Successfully deleted {} branches", deleted));
78 }
79
80 if failed > 0 {
81 Output::warning(format!("โ ๏ธ {} branches could not be deleted", failed));
82 }
83
84 Ok(())
85}
86
87fn analyze_temp_branch(git_repo: &GitRepository, branch_name: &str) -> Result<String> {
89 let parts: Vec<&str> = branch_name.split("-temp-").collect();
91
92 if parts.len() == 2 {
93 if let Ok(timestamp) = parts[1].parse::<i64>() {
94 if let Some(created_at) = DateTime::from_timestamp(timestamp, 0) {
95 let now = Utc::now();
96 let age = now.signed_duration_since(created_at);
97
98 if age.num_days() > 0 {
99 return Ok(format!("(created {} days ago)", age.num_days()));
100 } else if age.num_hours() > 0 {
101 return Ok(format!("(created {} hours ago)", age.num_hours()));
102 } else {
103 return Ok(format!("(created {} minutes ago)", age.num_minutes()));
104 }
105 }
106 }
107 }
108
109 match git_repo.get_branch_commit_hash(branch_name) {
111 Ok(commit_hash) => Ok(format!("(commit: {})", &commit_hash[..8])),
112 Err(_) => Ok("(orphaned)".to_string()),
113 }
114}