1use crate::engine::cleanup::cleanup_orphaned_worktree;
2use std::path::Path;
3use std::sync::Arc;
4
5pub async fn run(dry_run: bool) -> anyhow::Result<()> {
12 let store = Arc::new(crate::cli::init_store().await?);
13 let all_tasks = store.list_all_global().await?;
14
15 let worktrees_dir = crate::home::worktrees_dir()?;
16
17 let owned: std::collections::HashSet<String> = all_tasks
19 .iter()
20 .filter(|t| !t.worktree.is_empty())
21 .map(|t| t.worktree.clone())
22 .collect();
23
24 let mut removed = 0usize;
25 let mut failed = 0usize;
26
27 let project_dirs = match std::fs::read_dir(&worktrees_dir) {
28 Ok(d) => d,
29 Err(e) => {
30 eprintln!("cannot read worktrees directory: {e}");
31 return Ok(());
32 }
33 };
34
35 for project_entry in project_dirs {
36 let project_entry = match project_entry {
37 Ok(e) => e,
38 Err(e) => {
39 eprintln!("error reading worktrees directory entry: {e}");
40 continue;
41 }
42 };
43 if !project_entry.file_type().is_ok_and(|t| t.is_dir()) {
44 continue;
45 }
46 let Ok(branch_dirs) = std::fs::read_dir(project_entry.path()) else {
47 continue;
48 };
49 for branch_entry in branch_dirs {
50 let branch_entry = match branch_entry {
51 Ok(e) => e,
52 Err(e) => {
53 eprintln!("error reading project directory entry: {e}");
54 continue;
55 }
56 };
57 if !branch_entry.file_type().is_ok_and(|t| t.is_dir()) {
58 continue;
59 }
60 let wt_path = branch_entry.path().to_string_lossy().to_string();
61 if owned.contains(&wt_path) {
62 continue;
63 }
64
65 if dry_run {
66 println!("would remove: {wt_path}");
67 removed += 1;
68 continue;
69 }
70
71 let ok = cleanup_orphaned_worktree("prune", Path::new(&wt_path)).await;
72 if ok {
73 println!("removed: {wt_path}");
74 removed += 1;
75 } else {
76 eprintln!("failed to remove: {wt_path}");
77 failed += 1;
78 }
79 }
80 }
81
82 if removed == 0 && failed == 0 {
83 println!("No orphaned worktrees found.");
84 return Ok(());
85 }
86
87 if dry_run {
88 println!("\n{removed} orphaned worktree(s) would be removed (dry run)");
89 } else {
90 println!(
91 "\nRemoved {} orphaned worktree(s){}",
92 removed,
93 if failed > 0 {
94 format!(", {failed} failed")
95 } else {
96 String::new()
97 }
98 );
99 }
100
101 Ok(())
102}