diff --git a/src/cli.rs b/src/cli.rs index 83b732b..ec8da3f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -32,8 +32,8 @@ pub struct Args { short = 'w', long = "workers", help = "Number of threads to use for scanning repositories", - default_value_t = 3, - value_parser(clap::value_parser!(u16).range(1..=5)), + default_value_t = 5, + value_parser(clap::value_parser!(u16).range(1..=10)), required = false )] pub workers: u16, @@ -56,4 +56,11 @@ pub struct Args { required = false )] pub exclude: Option>, + + #[arg( + long = "scan-all", + help = "Scan all git repos, with or without untracked files", + required = false + )] + pub scan_all: bool, } diff --git a/src/git_ops.rs b/src/git_ops.rs index 90d438d..a653c5c 100644 --- a/src/git_ops.rs +++ b/src/git_ops.rs @@ -3,27 +3,47 @@ use git2::{DiffOptions, Repository, StatusOptions}; use std::io; use std::io::ErrorKind; use std::path::{Path, PathBuf}; +use std::sync::mpsc; +use std::sync::mpsc::Sender; +use threadpool::ThreadPool; use walkdir::{DirEntry, WalkDir}; +// Check if a directory should be excluded fn is_excluded_dir(entry: &DirEntry, exclude_dirs: &[String]) -> bool { exclude_dirs.iter().any(|dir| entry.path().starts_with(dir)) } -pub fn find_git_repos(start_path: &Path, exclude_dirs: &[String]) -> Vec { - let mut git_repos = Vec::new(); +// Send the path to the channel if it is a Git repository +fn check_and_send_repo(path: PathBuf, tx: Sender) { + if path.join(".git").exists() { + tx.send(path).unwrap(); + } +} - for entry in WalkDir::new(start_path) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| !is_excluded_dir(e, exclude_dirs)) - { - let path = entry.path(); - if path.is_dir() && path.join(".git").exists() { - git_repos.push(path.to_path_buf()); +// Find Git repositories starting from a given directory using a ThreadPool +pub fn find_git_repos( + start_path: &Path, + exclude_dirs: &[String], + num_threads: usize, +) -> Vec { + let pool = ThreadPool::new(num_threads); // Create a thread pool with the specified number of threads + let (tx, rx) = mpsc::channel(); // Create a channel to send results from threads to the main thread + + // Iterate over all entries in the starting path + for entry in WalkDir::new(start_path).into_iter().filter_map(|e| e.ok()) { + let path = entry.path().to_path_buf(); + if path.is_dir() && !is_excluded_dir(&entry, exclude_dirs) { + let tx = tx.clone(); // Clone the sender to be used in the thread + pool.execute(move || { + check_and_send_repo(path, tx); // Check if the directory is a Git repository and send the path if it is + }); } } - git_repos + drop(tx); // Close the sender side of the channel to indicate no more sends + + // Collect all the paths from the receiver into a vector + rx.into_iter().collect() } pub fn check_untracked_files(repo_path: &Path) -> Result, git2::Error> { diff --git a/src/main.rs b/src/main.rs index bb2a700..887fc44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,58 +39,52 @@ fn main() { let start_path = Path::new(&args.path); // If user not specify exclude dirs, set it to empty let exclude_dirs = args.exclude.as_deref().unwrap_or(&[]); - let git_repos = find_git_repos(start_path, exclude_dirs); + let git_repos = find_git_repos(start_path, &exclude_dirs, args.workers as usize); - // Create a thread pool with a limited number of threads - let num_threads: usize = args.workers as usize; - let pool = ThreadPool::new(num_threads); - - // Create a channel to send the results back to the main thread - let (tx, rx) = mpsc::channel(); - - // Spawn a thread for each repo to check for untracked files - for repo_path in git_repos { - let tx = tx.clone(); - let repo_path = repo_path.clone(); - pool.execute(move || { - match check_untracked_files(&repo_path) { - Ok(untracked_files) => { - if !untracked_files.is_empty() { - // Send the results back to the main thread - tx.send((repo_path.clone(), untracked_files)).unwrap(); - } - } - Err(e) => eprintln!("{}: {}", "Error checking repository".red(), e), - } - }); - } - - // Close the sending side of the channel - drop(tx); - - // Print the results as they arrive - let mut has_results = false; - while let Ok((repo_path, untracked_files)) = rx.recv() { - has_results = true; - println_orange!("Untracked files in: {}", repo_path.display()); - if !args.summary { - for file in untracked_files { - println_light_orange!(" - {}", file); - if args.diff { - match show_diff(&repo_path, &file) { - Ok(diff) => println!("{}", diff), - Err(e) => eprintln!("{}: {}", "Error showing diff".red(), e), - } - } - } - } - } - - // Print a message if no results were found - if !has_results { - println_orange!( - "-----> There are no changes to git in {}", - start_path.display() - ); - } + // + // // Spawn a thread for each repo to check for untracked files + // for repo_path in git_repos { + // let tx = tx.clone(); + // let repo_path = repo_path.clone(); + // pool.execute(move || { + // match check_untracked_files(&repo_path) { + // Ok(untracked_files) => { + // if !untracked_files.is_empty() { + // // Send the results back to the main thread + // tx.send((repo_path.clone(), untracked_files)).unwrap(); + // } + // } + // Err(e) => eprintln!("{}: {}", "Error checking repository".red(), e), + // } + // }); + // } + // + // // Close the sending side of the channel + // drop(tx); + // + // // Print the results as they arrive + // let mut has_results = false; + // while let Ok((repo_path, untracked_files)) = rx.recv() { + // has_results = true; + // println_orange!("Untracked files in: {}", repo_path.display()); + // if !args.summary { + // for file in untracked_files { + // println_light_orange!(" - {}", file); + // if args.diff { + // match show_diff(&repo_path, &file) { + // Ok(diff) => println!("{}", diff), + // Err(e) => eprintln!("{}: {}", "Error showing diff".red(), e), + // } + // } + // } + // } + // } + // + // // Print a message if no results were found + // if !has_results { + // println_orange!( + // "-----> There are no changes to git in {}", + // start_path.display() + // ); + // } }