This repository has no description
4.0 kB
133 lines
1use std::io::Write;
2use std::path::Path;
3use std::process::{Command, Output, Stdio};
4
5pub const AUTHOR_NAME: &str = "nel";
6pub const AUTHOR_EMAIL: &str = "nel@oyster.cafe";
7pub const PINNED_DATE: &str = "2026-01-01T00:00:00 +0000";
8
9pub fn command(cwd: &Path) -> Command {
10 let mut command = Command::new("git");
11 command
12 .current_dir(cwd)
13 .env("GIT_CONFIG_GLOBAL", "/dev/null")
14 .env("GIT_CONFIG_SYSTEM", "/dev/null")
15 .env("GIT_TERMINAL_PROMPT", "0")
16 .env("GIT_ASKPASS", "true")
17 .env("GIT_AUTHOR_NAME", AUTHOR_NAME)
18 .env("GIT_AUTHOR_EMAIL", AUTHOR_EMAIL)
19 .env("GIT_COMMITTER_NAME", AUTHOR_NAME)
20 .env("GIT_COMMITTER_EMAIL", AUTHOR_EMAIL);
21 command
22}
23
24pub fn command_at(cwd: &Path, stamp: &str) -> Command {
25 let mut command = command(cwd);
26 command
27 .env("GIT_AUTHOR_DATE", stamp)
28 .env("GIT_COMMITTER_DATE", stamp);
29 command
30}
31
32pub fn available() -> bool {
33 Command::new("git")
34 .arg("--version")
35 .output()
36 .map(|out| out.status.success())
37 .unwrap_or(false)
38}
39
40fn combined(out: &Output) -> String {
41 format!(
42 "{}{}",
43 String::from_utf8_lossy(&out.stdout),
44 String::from_utf8_lossy(&out.stderr)
45 )
46}
47
48pub fn run(cwd: &Path, args: &[&str]) -> (bool, String) {
49 let out = command_at(cwd, PINNED_DATE)
50 .args(args)
51 .output()
52 .expect("git is available");
53 (out.status.success(), combined(&out))
54}
55
56pub fn must(cwd: &Path, args: &[&str]) -> String {
57 let out = command_at(cwd, PINNED_DATE)
58 .args(args)
59 .output()
60 .expect("git is available");
61 assert!(out.status.success(), "git {args:?}:\n{}", combined(&out));
62 String::from_utf8_lossy(&out.stdout).trim().to_string()
63}
64
65pub fn feed(cwd: &Path, args: &[&str], stdin: &[u8]) -> (bool, String) {
66 let mut child = command_at(cwd, PINNED_DATE)
67 .args(args)
68 .stdin(Stdio::piped())
69 .stdout(Stdio::piped())
70 .stderr(Stdio::piped())
71 .spawn()
72 .expect("git is available");
73 child
74 .stdin
75 .take()
76 .expect("stdin was piped")
77 .write_all(stdin)
78 .expect("the write to git's stdin succeeds");
79 let out = child.wait_with_output().expect("git exits");
80 (out.status.success(), combined(&out))
81}
82
83pub fn fsck(bare: &Path) -> Result<(), String> {
84 match run(
85 bare,
86 &["fsck", "--no-dangling", "--no-reflogs", "--no-progress"],
87 ) {
88 (true, _) => Ok(()),
89 (false, report) => Err(report),
90 }
91}
92
93pub fn commit(work: &Path, file: &str, contents: &str, message: &str) {
94 std::fs::write(work.join(file), contents).expect("fixture file is writable");
95 must(work, &["add", "-A"]);
96 must(work, &["commit", "-q", "-m", message]);
97}
98
99pub fn contains(haystack: &[u8], needle: &[u8]) -> bool {
100 haystack
101 .windows(needle.len())
102 .any(|window| window == needle)
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn every_helper_pins_its_dates_so_the_same_sequence_yields_the_same_oid() {
111 if !available() {
112 return;
113 }
114 let oids = || {
115 let dir = tempfile::tempdir().unwrap();
116 must(dir.path(), &["init", "-q", "-b", "main"]);
117 commit(dir.path(), "README.md", "kelp\n", "initial");
118 let tree = must(dir.path(), &["hash-object", "-t", "tree", "-w", "--stdin"]);
119 let (ok, from_stdin) = feed(dir.path(), &["commit-tree", &tree, "-F", "-"], b"empty\n");
120 assert!(ok, "{from_stdin}");
121 (
122 must(dir.path(), &["rev-parse", "HEAD"]),
123 from_stdin.trim().to_string(),
124 )
125 };
126 assert_eq!(
127 oids(),
128 oids(),
129 "an unpinned committer date would make every differential run disagree, \
130 so a fixture writing through stdin pins the same dates as one that doesn't"
131 );
132 }
133}