use std::env;
use std::path::PathBuf;
use std::process::{Command, Stdio};
pub struct Commit {
pub hash: String,
pub title: String,
}
#[must_use]
pub fn is_git_in_path() -> bool {
Command::new("git")
.arg("--version")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.is_ok()
}
#[must_use]
pub fn find_git_directory() -> Option<PathBuf> {
let mut current_dir = env::current_dir().ok()?;
loop {
let git_dir = current_dir.join(".git");
if git_dir.is_dir() {
return Some(git_dir);
}
if !current_dir.pop() {
break;
}
}
None
}
#[must_use]
pub fn current_commit_hash() -> Option<String> {
let output = Command::new("git")
.arg("rev-parse")
.arg("--verify")
.arg("--quiet")
.arg("HEAD^{commit}")
.output();
if let Ok(output) = output {
if output.status.success() {
let hash = String::from_utf8_lossy(&output.stdout).trim().to_owned();
return Some(hash);
}
}
None
}
#[must_use]
pub fn current_branch() -> Option<String> {
let output = Command::new("git")
.arg("symbolic-ref")
.arg("--short")
.arg("--quiet")
.arg("HEAD")
.output();
if let Ok(output) = output {
if output.status.success() {
let branch = String::from_utf8_lossy(&output.stdout).trim().to_owned();
return Some(branch);
}
}
None
}
#[must_use]
pub fn ref_to_commit_hash(ref_: &str) -> Option<String> {
let output = Command::new("git")
.arg("rev-parse")
.arg("--verify")
.arg("--quiet")
.arg("--end-of-options")
.arg(format!("{ref_}^{{commit}}"))
.output();
if let Ok(output) = output {
if output.status.success() {
let hash = String::from_utf8_lossy(&output.stdout).trim().to_owned();
return Some(hash);
}
}
None
}
#[cfg(not(tarpaulin_include))] #[must_use]
pub fn history_up_to_commit(commit: &str) -> Vec<Commit> {
let output = Command::new("git")
.arg("rev-list")
.arg("--first-parent")
.arg("--format=%H %s")
.arg("--no-commit-header")
.arg("--reverse")
.arg(commit)
.output();
if let Ok(output) = output {
if output.status.success() {
let commits: Vec<Commit> = String::from_utf8_lossy(&output.stdout)
.lines()
.filter_map(|line| {
let pieces = line.split_once(' ')?;
let hash = String::from(pieces.0);
let title = String::from(pieces.1);
Some(Commit { hash, title })
})
.collect();
return commits;
}
}
Vec::new()
}
#[cfg(not(tarpaulin_include))] #[must_use]
pub fn checkout(commit: &str) -> bool {
let status = Command::new("git")
.arg("checkout")
.arg(commit)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
let Ok(status) = status else {
return false;
};
status.success()
}
#[cfg(not(tarpaulin_include))] #[must_use]
pub fn is_working_directory_clean() -> bool {
let output = Command::new("git")
.arg("status")
.arg("--untracked-files=no")
.arg("--porcelain")
.output();
let Ok(output) = output else {
return false;
};
String::from_utf8_lossy(&output.stdout).trim().is_empty()
}
#[cfg(not(tarpaulin_include))] #[must_use]
pub fn stash() -> bool {
let status = Command::new("git")
.arg("stash")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
let Ok(status) = status else {
return false;
};
status.success()
}