Skip to main content

git_same/commands/
status.rs

1//! Status command handler.
2
3use crate::cli::StatusArgs;
4use crate::config::{Config, WorkspaceManager};
5use crate::discovery::DiscoveryOrchestrator;
6use crate::errors::Result;
7use crate::git::{GitOperations, ShellGit};
8use crate::output::{format_count, Output};
9
10/// Show status of repositories.
11pub async fn run(args: &StatusArgs, config: &Config, output: &Output) -> Result<()> {
12    let workspace = WorkspaceManager::resolve(args.workspace.as_deref(), config)?;
13
14    // Ensure base path exists (offer to fix if user moved it)
15    super::ensure_base_path(&workspace, output)?;
16    let base_path = workspace.expanded_base_path();
17
18    let structure = workspace.structure.as_deref().unwrap_or(&config.structure);
19
20    // Scan local repositories
21    let git = ShellGit::new();
22    let orchestrator = DiscoveryOrchestrator::new(workspace.filters.clone(), structure.to_string());
23    let local_repos = orchestrator.scan_local(&base_path, &git);
24
25    if local_repos.is_empty() {
26        output.warn("No repositories found");
27        return Ok(());
28    }
29
30    output.info(&format_count(local_repos.len(), "repositories found"));
31
32    // Get status for each
33    let mut uncommitted_count = 0;
34    let mut behind_count = 0;
35    let mut error_count = 0;
36
37    for (path, org, name) in &local_repos {
38        let status = git.status(path);
39
40        match status {
41            Ok(s) => {
42                let is_uncommitted = s.is_uncommitted || s.has_untracked;
43                let is_behind = s.behind > 0;
44
45                // Apply filters
46                if args.uncommitted && !is_uncommitted {
47                    continue;
48                }
49                if args.behind && !is_behind {
50                    continue;
51                }
52                if !args.org.is_empty() && !args.org.contains(org) {
53                    continue;
54                }
55
56                if is_uncommitted {
57                    uncommitted_count += 1;
58                }
59                if is_behind {
60                    behind_count += 1;
61                }
62
63                // Print status
64                let full_name = format!("{}/{}", org, name);
65                if args.detailed {
66                    println!("{}", full_name);
67                    println!("  Branch: {}", s.branch);
68                    if s.ahead > 0 || s.behind > 0 {
69                        println!("  Ahead: {}, Behind: {}", s.ahead, s.behind);
70                    }
71                    if s.is_uncommitted {
72                        println!("  Status: uncommitted changes");
73                    }
74                    if s.has_untracked {
75                        println!("  Status: has untracked files");
76                    }
77                } else {
78                    let mut indicators = Vec::new();
79                    if is_uncommitted {
80                        indicators.push("*".to_string());
81                    }
82                    if s.ahead > 0 {
83                        indicators.push(format!("+{}", s.ahead));
84                    }
85                    if s.behind > 0 {
86                        indicators.push(format!("-{}", s.behind));
87                    }
88
89                    if indicators.is_empty() {
90                        println!("  {} (clean)", full_name);
91                    } else {
92                        println!("  {} [{}]", full_name, indicators.join(", "));
93                    }
94                }
95            }
96            Err(e) => {
97                error_count += 1;
98                output.verbose(&format!("  {}/{} - error: {}", org, name, e));
99            }
100        }
101    }
102
103    // Summary
104    println!();
105    if uncommitted_count > 0 {
106        output.warn(&format!(
107            "{} repositories have uncommitted changes",
108            uncommitted_count
109        ));
110    }
111    if behind_count > 0 {
112        output.info(&format!(
113            "{} repositories are behind upstream",
114            behind_count
115        ));
116    }
117    if error_count > 0 {
118        output.warn(&format!(
119            "{} repositories could not be checked",
120            error_count
121        ));
122    } else if uncommitted_count == 0 && behind_count == 0 {
123        output.success("All repositories are clean and up to date");
124    }
125
126    Ok(())
127}
128
129#[cfg(test)]
130#[path = "status_tests.rs"]
131mod tests;