crates/mimir-ingest/tests/ingest/git_walk.rs
//! The git half on its own: first-parent walk from an offset, merges seen as
//! ONE event, classification by diff against the first parent.
use super::common::Scratch;
use mimir_ingest::git::{Change, classify, first_parent_since};
/// Move `refs/heads/main` back to `sha` (the side commit stays reachable only
/// through the merge made next).
fn reset_main(root: &std::path::Path, sha: &str) {
nornir_git::gitio::point_head_at_branch(root, "main", sha).unwrap();
}
/// A merge commit on main: parents [HEAD, side], tree = side's tree.
fn merge(root: &std::path::Path, side: &str, msg: &str) -> String {
let repo = gix::open(root).unwrap();
let head = repo.head_commit().unwrap().id;
let side_id = gix::ObjectId::from_hex(side.as_bytes()).unwrap();
let tree = repo.find_commit(side_id).unwrap().tree_id().unwrap().detach();
let sig = gix::actor::SignatureRef {
name: "fixture".into(),
email: "fixture@nornir.invalid".into(),
time: "1700000100 +0000",
};
repo.commit_as(sig, sig, "HEAD", msg, tree, [head, side_id]).unwrap().to_string()
}
#[test]
fn first_parent_walk_skips_branch_internal_commits_and_resumes_from_offset() {
let tmp = tempfile::tempdir().unwrap();
let src = Scratch::new(tmp.path().join("r"));
src.write("src/lib.rs", "fn a() {}\n");
let c1 = src.commit("c1");
src.write("README.md", "docs\n");
let c2 = src.commit("c2 docs");
// A side commit (branch-internal), then main is put back and merges it.
src.write("src/lib.rs", "fn a() { side() }\n");
let side = src.commit("side work");
reset_main(&src.root, &c2);
let m = merge(&src.root, &side, "merge side");
src.write("docs/x.md", "more docs\n");
let c4 = src.commit("c4 docs");
let all = first_parent_since(&src.root, "main", None).unwrap();
assert_eq!(all, vec![c1.clone(), c2.clone(), m.clone(), c4.clone()], "side commit must not be an event");
assert_eq!(first_parent_since(&src.root, "main", Some(&c2)).unwrap(), vec![m.clone(), c4.clone()]);
assert!(first_parent_since(&src.root, "main", Some(&c4)).unwrap().is_empty());
// An offset that is not on the first-parent chain is refused, not guessed.
let err = first_parent_since(&src.root, "main", Some(&side)).unwrap_err();
assert!(format!("{err:#}").contains("not on the first-parent chain"), "{err:#}");
assert_eq!(classify(&src.root, &c1).unwrap(), Change::Code, "root commit diffs against the empty tree");
assert_eq!(classify(&src.root, &c2).unwrap(), Change::DocsOnly);
assert_eq!(classify(&src.root, &m).unwrap(), Change::Code, "the merge brings side's .rs change");
assert_eq!(classify(&src.root, &c4).unwrap(), Change::DocsOnly);
}
#[test]
fn manifest_lock_and_toolchain_changes_are_code() {
let tmp = tempfile::tempdir().unwrap();
let src = Scratch::new(tmp.path().join("r"));
src.write("README.md", "x\n");
let root = src.commit("root docs");
assert_eq!(classify(&src.root, &root).unwrap(), Change::DocsOnly);
for (path, text) in [
("crates/a/Cargo.toml", "[package]\n"),
("Cargo.lock", "# lock\n"),
("rust-toolchain.toml", "[toolchain]\n"),
("crates/a/build.rs", "fn main() {}\n"),
] {
src.write(path, text);
let sha = src.commit(path);
assert_eq!(classify(&src.root, &sha).unwrap(), Change::Code, "{path}");
}
}