feat: one store directory per checkout

This commit is contained in:
2026-09-21 13:02:26 +00:00
parent 9adcfed28e
commit c96211a28b
3 changed files with 99 additions and 18 deletions

View File

@@ -7,6 +7,7 @@
#![allow(dead_code)]
use std::ffi::OsStr;
use std::fmt::Write as _;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::sync::atomic::{AtomicU32, Ordering};
@@ -15,8 +16,12 @@ use std::{env, fs};
// unique per case even with the suite running in parallel
static NEXT: AtomicU32 = AtomicU32::new(0);
pub const REMOTE: &str = "https://git.example.org/acme/proj.git";
pub struct Case {
root: PathBuf,
// the data home every checkout of a case shares, as one user's would
xdg: PathBuf,
pub repo: PathBuf,
pub store: PathBuf,
pub outside: PathBuf,
@@ -32,29 +37,41 @@ impl Case {
));
let _ = fs::remove_dir_all(&root);
let case = Self {
repo: root.join("repo"),
store: root
.join("xdg/ahab/git.example.org/acme/proj")
.to_path_buf(),
outside: root.join("outside"),
root,
};
Self::checkout(root.clone(), root.join("xdg"))
}
for dir in [&case.repo, &case.outside] {
// a second clone of the same remote, sharing the first one's data home
pub fn second_checkout(&self) -> Self {
Self::checkout(self.root.join("second"), self.xdg.clone())
}
fn checkout(root: PathBuf, xdg: PathBuf) -> Self {
let repo = root.join("repo");
let outside = root.join("outside");
for dir in [&repo, &outside] {
fs::create_dir_all(dir).expect("creating the case directories");
}
// `<dir>-<hash>` of the canonical checkout, as ahab spells it
let canonical = fs::canonicalize(&repo).expect("resolving the repository");
let store = xdg.join("ahab/git.example.org/acme/proj").join(format!(
"repo-{}",
fingerprint(canonical.as_os_str().as_bytes())
));
let case = Self {
root,
xdg,
repo,
store,
outside,
};
case.git(&["init", "-q", "."]);
case.git(&["config", "user.email", "test@example.org"]);
case.git(&["config", "user.name", "test"]);
case.git(&["config", "commit.gpgsign", "false"]);
case.git(&[
"remote",
"add",
"origin",
"https://git.example.org/acme/proj.git",
]);
case.git(&["remote", "add", "origin", REMOTE]);
case.write("tracked.txt", b"tracked\n");
case.git(&["add", "tracked.txt"]);
case.git(&["commit", "-qm", "init"]);
@@ -84,7 +101,7 @@ impl Case {
pub fn ahab<S: AsRef<OsStr>>(&self, args: &[S]) -> Run {
let out = Command::new(env!("CARGO_BIN_EXE_ahab"))
.current_dir(&self.repo)
.env("XDG_DATA_HOME", self.root.join("xdg"))
.env("XDG_DATA_HOME", &self.xdg)
// the git commands ahab runs must not read the developer's own config
.env("GIT_CONFIG_GLOBAL", "/dev/null")
.env("GIT_CONFIG_SYSTEM", "/dev/null")
@@ -205,3 +222,15 @@ impl Run {
out
}
}
// fnv-1a, as the store spells a checkout
pub fn fingerprint(bytes: &[u8]) -> String {
let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
for byte in bytes {
hash ^= u64::from(*byte);
hash = hash.wrapping_mul(0x100_0000_01b3);
}
format!("{:08x}", hash as u32)
}