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