use std::path::Path;
use gix::hash::ObjectId;
use crate::git_export::{git_command, run_cmd, stdin_bytes};
pub(crate) fn git(repo: &Path, args: &[&str], stdin: &[u8]) -> String {
let mut command = git_command(repo);
command
.args(args)
.env("GIT_AUTHOR_NAME", "t")
.env("GIT_AUTHOR_EMAIL", "t@t")
.env("GIT_COMMITTER_NAME", "t")
.env("GIT_COMMITTER_EMAIL", "t@t")
.stdin(stdin_bytes(stdin).unwrap());
let output = run_cmd(command, &format!("git {args:?}")).unwrap();
String::from_utf8(output.stdout).unwrap().trim().to_string()
}
/// letters from a seeded lcg that git can barely compress.
pub(crate) fn noise(seed: u64, len: usize) -> String {
let mut state = seed.wrapping_add(1);
(0..len)
.map(|_| {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
char::from(b'a' + (state >> 59) as u8 % 26)
})
.collect()
}
pub(crate) fn repo(repo: &Path, files: usize, len: usize, changed: usize) -> (ObjectId, ObjectId) {
std::fs::create_dir_all(repo).unwrap();
git(repo, &["init", "-q", "--bare", "-b", "main"], b"");
let path = |n: usize| format!("d{}/e{}/f{n}", n / 256, n / 16 % 16);
let mut stream = String::new();
for n in 0..files + changed {
let text = noise(n as u64, len);
stream += &format!("blob\nmark :{}\ndata {}\n{text}\n", n + 1, text.len());
}
let commit =
|stream: &mut String, mark: usize, files: &mut dyn Iterator<Item = (usize, usize)>| {
*stream += &format!(
"commit refs/heads/main\nmark :{mark}\ncommitter t <t@t> 0 +0000\ndata 1\nc\n"
);
for (blob, n) in files {
*stream += &format!("M 100644 :{} {}\n", blob + 1, path(n));
}
*stream += "\n";
};
let base = files + changed + 1;
commit(&mut stream, base, &mut (0..files).map(|n| (n, n)));
commit(
&mut stream,
base + 1,
&mut (0..changed).map(|n| (files + n, n * files.max(1) / changed.max(1))),
);
git(repo, &["fast-import", "--quiet"], stream.as_bytes());
git(repo, &["repack", "-adq"], b"");
let oid =
|rev: &str| ObjectId::from_hex(git(repo, &["rev-parse", rev], b"").as_bytes()).unwrap();
(oid("main~1"), oid("main"))
}