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