use std::io::Write; use std::path::Path; use std::process::{Command, Output, Stdio}; pub const AUTHOR_NAME: &str = "nel"; pub const AUTHOR_EMAIL: &str = "nel@oyster.cafe"; pub const PINNED_DATE: &str = "2026-01-01T00:00:00 +0000"; pub fn command(cwd: &Path) -> Command { let mut command = Command::new("git"); command .current_dir(cwd) .env("GIT_CONFIG_GLOBAL", "/dev/null") .env("GIT_CONFIG_SYSTEM", "/dev/null") .env("GIT_CONFIG_COUNT", "2") .env("GIT_CONFIG_KEY_0", "maintenance.auto") .env("GIT_CONFIG_VALUE_0", "false") .env("GIT_CONFIG_KEY_1", "gc.autoDetach") .env("GIT_CONFIG_VALUE_1", "false") .env("GIT_TERMINAL_PROMPT", "0") .env("GIT_ASKPASS", "true") .env("GIT_AUTHOR_NAME", AUTHOR_NAME) .env("GIT_AUTHOR_EMAIL", AUTHOR_EMAIL) .env("GIT_COMMITTER_NAME", AUTHOR_NAME) .env("GIT_COMMITTER_EMAIL", AUTHOR_EMAIL); command } pub fn command_at(cwd: &Path, stamp: &str) -> Command { let mut command = command(cwd); command .env("GIT_AUTHOR_DATE", stamp) .env("GIT_COMMITTER_DATE", stamp); command } pub fn available() -> bool { Command::new("git") .arg("--version") .output() .map(|out| out.status.success()) .unwrap_or(false) } fn combined(out: &Output) -> String { format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ) } pub fn run(cwd: &Path, args: &[&str]) -> (bool, String) { let out = command_at(cwd, PINNED_DATE) .args(args) .output() .expect("git is available"); (out.status.success(), combined(&out)) } pub fn must(cwd: &Path, args: &[&str]) -> String { let out = command_at(cwd, PINNED_DATE) .args(args) .output() .expect("git is available"); assert!(out.status.success(), "git {args:?}:\n{}", combined(&out)); String::from_utf8_lossy(&out.stdout).trim().to_string() } pub fn feed(cwd: &Path, args: &[&str], stdin: &[u8]) -> (bool, String) { let mut child = command_at(cwd, PINNED_DATE) .args(args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .expect("git is available"); child .stdin .take() .expect("stdin was piped") .write_all(stdin) .expect("the write to git's stdin succeeds"); let out = child.wait_with_output().expect("git exits"); (out.status.success(), combined(&out)) } pub fn fsck(bare: &Path) -> Result<(), String> { match run( bare, &["fsck", "--no-dangling", "--no-reflogs", "--no-progress"], ) { (true, _) => Ok(()), (false, report) => Err(report), } } pub fn commit(work: &Path, file: &str, contents: &str, message: &str) { std::fs::write(work.join(file), contents).expect("fixture file is writable"); must(work, &["add", "-A"]); must(work, &["commit", "-q", "-m", message]); } pub fn contains(haystack: &[u8], needle: &[u8]) -> bool { haystack .windows(needle.len()) .any(|window| window == needle) } #[cfg(test)] mod tests { use super::*; #[test] fn every_helper_pins_its_dates_so_the_same_sequence_yields_the_same_oid() { if !available() { return; } let oids = || { let dir = tempfile::tempdir().unwrap(); must(dir.path(), &["init", "-q", "-b", "main"]); commit(dir.path(), "README.md", "kelp\n", "initial"); let tree = must(dir.path(), &["hash-object", "-t", "tree", "-w", "--stdin"]); let (ok, from_stdin) = feed(dir.path(), &["commit-tree", &tree, "-F", "-"], b"empty\n"); assert!(ok, "{from_stdin}"); ( must(dir.path(), &["rev-parse", "HEAD"]), from_stdin.trim().to_string(), ) }; assert_eq!( oids(), oids(), "an unpinned committer date would make every differential run disagree, \ so a fixture writing through stdin pins the same dates as one that doesn't" ); } }