diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a81b14 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Giterator +Ever wondered why Git histories exist if we just ignore them? Giterator is a powerful command-line tool designed to simplify your Git workflow by providing seamless iteration over commit histories. Explore your project's evolution effortlessly, perform analyses, execute scripts, and generate output in various formats. + +## Usage +### Count Lines of Code (LoC) Over Time +```shell +# Count lines of code in Rust files over time +giterator 'find . -type f -name "*.rs" -exec cat {} + | wc -l' +``` + +### Explore Commits in Another Repository +```shell +giterator command other_repo +``` + +### Execute Custom Script over each Commit +```shell +# Run a custom script (myscript.sh) on each commit in a repository +giterator --script myscript.sh on_my_repo +``` + +### Output in JSON Format +```shell +# Generate JSON output for commit analysis +giterator --json command +``` + +### Output in CSV Format +```shell +# Generate CSV output for commit analysis +giterator --csv command +``` + +## Environment Variables +When running a custom script or command with Giterator, the following environment variables are automatically exposed: +- `$GIT_REPO`: The name of the repository being iterated over. +- `$COMMIT_HASH`: The hash of the current commit being processed. +- `$COMMIT_DATETIME`: The date and time of the current commit being processed. +- `$COMMIT`: The name of the current commit being processed. + +These environment variables allow your custom script or command to access important information about the repository and the current commit being iterated over. You can use these variables to customize the behavior of your script or command. diff --git a/src/main.rs b/src/main.rs index 34ea293..363f045 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ struct IterationOutput { commit: Commit, stdout: String, stderr: String, + status: i32, } impl IterationOutput { @@ -23,15 +24,21 @@ impl IterationOutput { commit, stdout: String::from_utf8(out.stdout).unwrap(), stderr: String::from_utf8(out.stderr).unwrap(), + status: out.status.code().unwrap(), } } fn print_text(&self) { println!( - "Commit [{}] ({}): {}", + "Commit [{}] ({}): {} {}", Color::Green.paint(&self.commit.hash), Color::Purple.paint(&self.commit.datetime), - Color::Blue.paint(&self.commit.name) + Color::Blue.paint(&self.commit.name), + if self.status == 0 { + Color::Red.paint(String::new()) + } else { + Color::Red.paint(format!("(Command failed with {})", self.status)) + } ); println!("{}", self.stdout); if !self.stderr.is_empty() { @@ -46,20 +53,22 @@ impl IterationOutput { "datetime": self.commit.datetime, "name": self.commit.datetime, "stdout": self.stdout, - "stderr": self.stderr + "stderr": self.stderr, + "status": self.status }) } fn as_csv(&self) -> Vec { vec![ - self.commit.hash.clone(), - self.commit.datetime.clone(), - self.commit.name.clone(), std::fs::canonicalize(self.commit.repo.clone()) .unwrap() .to_str() .unwrap() .to_string(), + self.commit.hash.clone(), + self.commit.datetime.clone(), + self.commit.name.clone(), + self.status.to_string(), self.stdout.replace('\n', "\\n"), self.stderr.replace('\n', "\\n"), ] @@ -69,9 +78,12 @@ impl IterationOutput { impl Commit { pub fn run_command(&self, command: &str) -> IterationOutput { checkout(&self.repo, &self.hash).unwrap(); - // todo : expose env vars let out = Command::new("sh") .current_dir(&self.repo) + .env("GIT_REPO", &self.repo) + .env("COMMIT_HASH", &self.hash) + .env("COMMIT_DATETIME", &self.datetime) + .env("COMMIT", &self.name) .arg("-c") .arg(command) .output() @@ -194,8 +206,10 @@ fn main() { let csv: Vec> = out.into_iter().map(|x| x.as_csv()).collect(); let mut wtr = csv::Writer::from_writer(std::io::stdout()); - wtr.write_record(["hash", "datetime", "name", "repo", "stdout", "stderr"]) - .unwrap(); + wtr.write_record([ + "repo", "hash", "datetime", "name", "status", "stdout", "stderr", + ]) + .unwrap(); for record in csv { wtr.write_record(&record).unwrap();