use std::path::Path;
use std::process::Command;
pub(crate) const GIT_ROUTING_ENV_VARS: &[&str] = &[
"GIT_DIR",
"GIT_WORK_TREE",
"GIT_INDEX_FILE",
"GIT_OBJECT_DIRECTORY",
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
"GIT_COMMON_DIR",
"GIT_CEILING_DIRECTORIES",
];
pub(crate) fn clear_git_environment(command: &mut Command) {
for name in GIT_ROUTING_ENV_VARS {
command.env_remove(name);
}
}
#[derive(Debug)]
pub struct GitOutput {
pub status: Option<i32>,
pub stdout: Vec<u8>,
pub stderr: Vec<u8>,
}
pub fn run_git(args: &[&str], cwd: &Path) -> GitOutput {
let mut command = Command::new("git");
command.args(args).current_dir(cwd);
clear_git_environment(&mut command);
match command.output() {
Ok(out) => GitOutput {
status: out.status.code(),
stdout: out.stdout,
stderr: out.stderr,
},
Err(err) => GitOutput {
status: None,
stdout: Vec::new(),
stderr: format!("{err}").into_bytes(),
},
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn run_git_success_status_and_stdout() {
let res = run_git(
&["rev-parse", "--short", "HEAD"],
env!("CARGO_MANIFEST_DIR").as_ref(),
);
assert_eq!(res.status, Some(0));
assert!(String::from_utf8_lossy(&res.stdout).trim().len() > 3);
}
#[test]
fn run_git_failing_command_nonzero() {
let res = run_git(
&["rev-parse", "not-a-real-ref-xyz"],
env!("CARGO_MANIFEST_DIR").as_ref(),
);
assert_ne!(res.status, Some(0));
}
#[test]
fn run_git_spawn_error_surfaced() {
let res = run_git(
&["rev-parse", "HEAD"],
"/nonexistent-dir-for-rungit-test".as_ref(),
);
assert_eq!(res.status, None);
assert!(String::from_utf8_lossy(&res.stderr).contains("No such file or directory"));
}
}