Skip to content

Commit 233815e

Browse files
committed
mod: correctly traverses the direcotires
1 parent 4c3db2f commit 233815e

File tree

1 file changed

+113
-48
lines changed

1 file changed

+113
-48
lines changed

git_better-branch/src/main.rs

Lines changed: 113 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,111 @@
1+
//! A Rust program that scans directories for Git repositories, checks branches,
2+
//! and displays their ahead/behind status relative to the main branch.
3+
//!
4+
//! This program uses the `walkdir` crate for directory traversal, the `regex` crate
5+
//! for parsing branch data, and executes Git commands to gather repository information.
6+
17
use std::env;
2-
use std::path::Path;
38
use std::process::{Command, Stdio};
49
use std::str;
510
use walkdir::WalkDir;
611
use regex::Regex;
712

13+
/// ANSI escape codes for colored terminal output
814
const RED: &str = "\x1b[0;31m";
915
const GREEN: &str = "\x1b[0;32m";
1016
const NO_COLOR: &str = "\x1b[0m";
1117
const BLUE: &str = "\x1b[0;34m";
1218
const YELLOW: &str = "\x1b[0;33m";
1319

14-
fn count_commits(dir: &str, branch: &str, base_branch: &str) -> (i32, i32) {
20+
/// Runs a Git command with the provided arguments and returns its trimmed output.
21+
///
22+
/// # Arguments
23+
///
24+
/// * `args` - A slice of string slices containing the Git command arguments.
25+
///
26+
/// # Returns
27+
///
28+
/// A `String` containing the trimmed output of the Git command.
29+
fn run_command_and_trim(args: &[&str]) -> String {
1530
let output = Command::new("git")
16-
.args(&["-C", dir, "rev-list", "--left-right", "--count", &format!("{}...{}", base_branch, branch)])
31+
.args(args)
1732
.output()
18-
.expect("Failed to execute git rev-list command");
33+
.expect("Failed to execute git command");
34+
35+
str::from_utf8(&output.stdout)
36+
.expect("Command output is not valid UTF-8")
37+
.trim()
38+
.to_string()
39+
}
1940

20-
let ahead_behind = str::from_utf8(&output.stdout).unwrap().trim();
21-
let parts: Vec<&str> = ahead_behind.split('\t').collect();
41+
/// Counts the commits that are ahead and behind between two branches.
42+
///
43+
/// # Arguments
44+
///
45+
/// * `dir` - The path to the Git repository.
46+
/// * `branch` - The branch being compared.
47+
/// * `base_branch` - The base branch for comparison.
48+
///
49+
/// # Returns
50+
///
51+
/// A tuple `(ahead, behind)` where `ahead` is the number of commits the branch is ahead
52+
/// of the base branch, and `behind` is the number of commits it is behind.
53+
fn count_commits(dir: &str, branch: &str, base_branch: &str) -> (i32, i32) {
54+
let output = run_command_and_trim(&[
55+
"-C",
56+
dir,
57+
"rev-list",
58+
"--left-right",
59+
"--count",
60+
&format!("{}...{}", base_branch, branch),
61+
]);
62+
63+
let parts: Vec<&str> = output.split('\t').collect();
2264
let behind = parts[0].parse().unwrap_or(0);
2365
let ahead = parts[1].parse().unwrap_or(0);
2466
(ahead, behind)
2567
}
2668

69+
/// Checks if a given directory is a Git repository.
70+
///
71+
/// # Arguments
72+
///
73+
/// * `path` - The path to the directory being checked.
74+
///
75+
/// # Returns
76+
///
77+
/// `true` if the directory is a Git repository, `false` otherwise.
2778
fn is_git_repo(path: &str) -> bool {
28-
Command::new("git")
29-
.args(&["-C", path, "rev-parse"])
79+
let status = Command::new("git")
80+
.args(&["-C", path, "rev-parse", "--is-inside-work-tree"])
3081
.stderr(Stdio::null())
31-
.status()
32-
.map_or(false, |status| status.success())
82+
.output();
83+
84+
match status {
85+
Ok(output) => output.status.success(),
86+
Err(_) => {
87+
eprintln!("Failed to check if '{}' is a git repository", path);
88+
false
89+
}
90+
}
3391
}
3492

93+
/// Processes a Git repository and displays information about its branches.
94+
///
95+
/// # Arguments
96+
///
97+
/// * `dir` - The path to the Git repository.
3598
fn process_repo(dir: &str) {
36-
let repo_name = Command::new("git")
37-
.args(&["-C", dir, "remote", "get-url", "origin"])
38-
.output()
39-
.expect("Failed to get remote URL")
40-
.stdout;
41-
42-
let repo_name = str::from_utf8(&repo_name)
43-
.unwrap()
44-
.trim()
99+
// Get the repository name by retrieving the remote URL.
100+
let repo_name = run_command_and_trim(&["-C", dir, "remote", "get-url", "origin"])
45101
.replace(".git", "");
46102

47103
println!("Repo: {}", repo_name);
48104

49-
let main_branch = Command::new("git")
50-
.args(&["-C", dir, "rev-parse", "HEAD"])
51-
.output()
52-
.expect("Failed to get main branch")
53-
.stdout;
54-
55-
let main_branch = str::from_utf8(&main_branch).unwrap().trim();
105+
// Get the main branch (HEAD).
106+
let main_branch = run_command_and_trim(&["-C", dir, "rev-parse", "HEAD"]);
56107

108+
// Print the header for branch information.
57109
println!(
58110
"{}{:5} {}{:6} {}{:30} {}{:20} {}{:40}",
59111
GREEN, "Ahead", RED, "Behind", BLUE, "Branch", YELLOW, "Last Commit", NO_COLOR, " "
@@ -63,15 +115,19 @@ fn process_repo(dir: &str) {
63115
GREEN, "-----", RED, "------", BLUE, "------------------------------", YELLOW, "-------------------", NO_COLOR, " "
64116
);
65117

66-
let branches_output = Command::new("git")
67-
.args(&["-C", dir, "for-each-ref", "--sort=-authordate", "--format=%(objectname:short)@%(refname:short)@%(committerdate:relative)", "refs/heads/"])
68-
.output()
69-
.expect("Failed to list branches")
70-
.stdout;
71-
72-
let branch_output_str = str::from_utf8(&branches_output).expect("Invalid UTF-8 in branch output");
118+
// Retrieve branch information using `git for-each-ref`.
119+
let branches_output = run_command_and_trim(&[
120+
"-C",
121+
dir,
122+
"for-each-ref",
123+
"--sort=-authordate",
124+
"--format=%(objectname:short)@%(refname:short)@%(committerdate:relative)",
125+
"refs/heads/",
126+
]);
127+
128+
// Regex to parse branch data.
73129
let branch_regex = Regex::new(r"([^\@]+)@([^\@]+)@([^\@]+)").unwrap();
74-
let branches = branch_output_str.trim().lines();
130+
let branches = branches_output.trim().lines();
75131

76132
for branchdata in branches {
77133
if let Some(caps) = branch_regex.captures(branchdata) {
@@ -80,7 +136,7 @@ fn process_repo(dir: &str) {
80136
let time = &caps[3];
81137

82138
if branch != main_branch {
83-
let (ahead, behind) = count_commits(dir, sha, main_branch);
139+
let (ahead, behind) = count_commits(dir, sha, &main_branch);
84140
println!(
85141
"{}{:5} {}{:6} {}{:30} {}{:20} {}{:40}",
86142
GREEN, ahead, RED, behind, BLUE, branch, YELLOW, time, NO_COLOR, ""
@@ -91,26 +147,35 @@ fn process_repo(dir: &str) {
91147
println!();
92148
}
93149

94-
fn check_all_dirs(path: &Path, depth: usize) {
95-
for entry in WalkDir::new(path)
150+
/// The entry point of the program. Scans directories for Git repositories and processes them.
151+
fn main() {
152+
// Parse command-line arguments for depth.
153+
let args: Vec<String> = env::args().collect();
154+
let depth = args.get(1).and_then(|d| d.parse().ok()).unwrap_or(0);
155+
156+
// Get the current working directory.
157+
let current_dir = env::current_dir().expect("Failed to get current directory");
158+
159+
println!("Scanning '{}' up to a depth of {}", current_dir.display(), depth);
160+
161+
let mut found_repos = false;
162+
163+
// Walk directories using `walkdir`.
164+
for entry in WalkDir::new(&current_dir)
96165
.min_depth(0)
97166
.max_depth(depth)
98167
.into_iter()
99168
.filter_map(Result::ok)
100169
{
101170
let path = entry.path();
102-
if path.is_dir() {
103-
if is_git_repo(path.to_str().unwrap()) {
104-
process_repo(path.to_str().unwrap());
105-
}
171+
if path.is_dir() && is_git_repo(path.to_str().unwrap()) {
172+
found_repos = true;
173+
process_repo(path.to_str().unwrap());
106174
}
107175
}
108-
}
109-
110-
fn main() {
111-
let args: Vec<String> = env::args().collect();
112-
let depth = args.get(1).and_then(|d| d.parse().ok()).unwrap_or(0);
113-
let current_dir = env::current_dir().expect("Failed to get current directory");
114176

115-
check_all_dirs(&current_dir, depth);
177+
// If no repositories are found, notify the user.
178+
if !found_repos {
179+
println!("No git repositories found in '{}'.", current_dir.display());
180+
}
116181
}

0 commit comments

Comments
 (0)