use std::fs;
use std::path::Path;
use std::process::Command;
pub(crate) fn git(path: &Path, args: &[&str]) {
let status = Command::new("git")
.arg("-C")
.arg(path)
.args(["-c", "user.email=test@example.com", "-c", "user.name=Test"])
.args(args)
.status()
.expect("run git");
assert!(status.success(), "git {args:?} failed");
}
pub(crate) fn head_sha(path: &Path) -> String {
let output = Command::new("git")
.arg("-C")
.arg(path)
.args(["rev-parse", "HEAD"])
.output()
.expect("run git rev-parse");
assert!(output.status.success());
String::from_utf8(output.stdout)
.expect("utf8 sha")
.trim()
.to_string()
}
pub(crate) fn current_branch(path: &Path) -> String {
let output = Command::new("git")
.arg("-C")
.arg(path)
.args(["symbolic-ref", "--short", "HEAD"])
.output()
.expect("run git symbolic-ref");
assert!(output.status.success());
String::from_utf8(output.stdout)
.expect("utf8 branch name")
.trim()
.to_string()
}
pub(crate) fn loose_object_count(repo: &Path) -> usize {
let objects = repo.join(".git").join("objects");
let mut count = 0;
for fan_out in fs::read_dir(&objects).expect("read objects dir") {
let fan_out = fan_out.expect("dir entry");
if !fan_out.file_type().expect("file type").is_dir() {
continue;
}
let name = fan_out.file_name();
if name == "pack" || name == "info" {
continue;
}
count += fs::read_dir(fan_out.path())
.expect("read fan-out dir")
.count();
}
count
}
pub(crate) fn init_bare(path: &Path) {
let status = Command::new("git")
.arg("init")
.arg("--bare")
.arg("--initial-branch=main")
.arg(path)
.status()
.expect("run git init --bare");
assert!(status.success());
}
pub(crate) fn commit_file(path: &Path, name: &str, contents: &str) {
fs::write(path.join(name), contents).expect("write fixture file");
git(path, &["add", name]);
git(path, &["commit", "-m", "add a file"]);
}
pub(crate) fn push_new_commit(remote: &Path, name: &str, contents: &str) {
let contributor = tempfile::tempdir().expect("temp dir");
let status = Command::new("git")
.arg("clone")
.arg(remote)
.arg(contributor.path())
.status()
.expect("run git clone");
assert!(status.success());
commit_file(contributor.path(), name, contents);
git(contributor.path(), &["push", "origin", "main"]);
}
pub(crate) fn remote_and_clone() -> (tempfile::TempDir, tempfile::TempDir) {
let remote = tempfile::tempdir().expect("temp dir");
init_bare(remote.path());
push_new_commit(remote.path(), "README.md", "seed\n");
let clone = tempfile::tempdir().expect("temp dir");
let status = Command::new("git")
.arg("clone")
.arg(remote.path())
.arg(clone.path())
.status()
.expect("run git clone");
assert!(status.success());
set_identity(clone.path());
(remote, clone)
}
pub(crate) fn set_identity(path: &Path) {
for (key, value) in [("user.email", "test@example.com"), ("user.name", "Test")] {
let status = Command::new("git")
.arg("-C")
.arg(path)
.args(["config", key, value])
.status()
.expect("run git config");
assert!(status.success(), "git config {key} failed");
}
}