grit-lib 0.5.18

Core library for the grit Git implementation
//! Calls commit-graph chain/layer public APIs on a split, Bloom-enabled repository.

use std::path::Path;
use std::process::Command;

use grit_lib::config::environment::RepositoryOptions;
use grit_lib::diff::bloom::BloomFilterSettings;
use grit_lib::objects::commit_graph::load_commit_graph_commit_info;
use grit_lib::objects::commit_graph::{
    diff_changed_paths_for_bloom, dump_bloom_filters, CommitGraphChain,
};
use grit_lib::objects::parse_commit;
use grit_lib::repo::Repository;

fn git_cmd(dir: &Path, args: &[&str]) {
    let out = Command::new("git")
        .current_dir(dir)
        .args(args)
        .env("GIT_CONFIG_GLOBAL", "/dev/null")
        .env("GIT_CONFIG_SYSTEM", "/dev/null")
        .env("GIT_AUTHOR_NAME", "T")
        .env("GIT_AUTHOR_EMAIL", "t@example.com")
        .env("GIT_COMMITTER_NAME", "T")
        .env("GIT_COMMITTER_EMAIL", "t@example.com")
        .output()
        .expect("git");
    assert!(out.status.success(), "git {args:?}");
}

fn merge_repo(root: &Path) {
    git_cmd(root, &["init", "-q", "-b", "main"]);
    for i in 1..=3 {
        std::fs::write(root.join(format!("f{i}.txt")), format!("{i}\n")).unwrap();
        git_cmd(root, &["add", &format!("f{i}.txt")]);
        git_cmd(root, &["commit", "-q", "-m", &format!("c{i}")]);
        git_cmd(root, &["branch", &format!("commits/{i}")]);
    }
    git_cmd(root, &["reset", "--hard", "commits/1"]);
    for i in 4..=5 {
        std::fs::write(root.join(format!("f{i}.txt")), format!("{i}\n")).unwrap();
        git_cmd(root, &["add", &format!("f{i}.txt")]);
        git_cmd(root, &["commit", "-q", "-m", &format!("c{i}")]);
        git_cmd(root, &["branch", &format!("commits/{i}")]);
    }
    git_cmd(root, &["reset", "--hard", "commits/2"]);
    git_cmd(root, &["merge", "-q", "-m", "m", "commits/4"]);
    git_cmd(root, &["repack", "-ad"]);
}

#[test]
fn chain_accessors_and_bloom_helpers_on_split_graph() {
    let dir = tempfile::tempdir().expect("tempdir");
    merge_repo(dir.path());
    git_cmd(
        dir.path(),
        &[
            "commit-graph",
            "write",
            "--reachable",
            "--changed-paths",
            "--split",
        ],
    );
    std::fs::write(dir.path().join("z.txt"), b"z\n").unwrap();
    git_cmd(dir.path(), &["add", "z.txt"]);
    git_cmd(dir.path(), &["commit", "-q", "-m", "z"]);
    git_cmd(
        dir.path(),
        &[
            "commit-graph",
            "write",
            "--reachable",
            "--changed-paths",
            "--split",
        ],
    );
    let repo = Repository::discover(Some(dir.path()), &RepositoryOptions::empty()).expect("open");
    let chain = CommitGraphChain::load(repo.odb().objects_dir()).expect("chain");
    let _ = chain.top_layer_bloom_settings();
    let _ = chain.total_commits();
    let _ = chain.layer_paths_oldest_first();
    let _ = chain.num_layers();
    let _ = chain.layer_commit_counts_tip_first();
    let _ = chain.layer_has_generation_data_tip_first();
    let _ = chain.layer_hashes_tip_first();
    let _ = chain.layer_object_dirs_tip_first();
    let _ = chain.sub_chain_tip_first(0, chain.num_layers());
    for layer in 0..chain.num_layers() {
        let _ = chain.layer_oids(layer);
    }
    let settings = BloomFilterSettings::default();
    let graph_path = dir.path().join(".git/objects/info/commit-graphs");
    for path in chain.layer_paths_oldest_first() {
        let _ = dump_bloom_filters(&path);
        let _ = grit_lib::objects::commit_graph::parse_graph_file(&path);
    }
    for oid in chain.all_oids_in_order() {
        let _ = chain.find_commit(&oid);
        let _ = chain.global_position(&oid);
        let _ = chain.existing_filter_bytes(&oid, &settings);
        let _ = chain.upgradable_filter_bytes(
            &oid,
            &BloomFilterSettings {
                hash_version: 2,
                ..settings
            },
        );
        if let Some((parents, time)) = chain.graph_commit(&oid) {
            let obj = repo.odb().read(&oid).expect("read");
            let c = parse_commit(&obj.data).expect("parse");
            if c.parents.len() <= 2 {
                assert_eq!(c.parents, parents);
            }
            let info = load_commit_graph_commit_info(repo.odb(), oid).expect("info");
            if let Some(p) = c.parents.first() {
                let ptree = load_commit_graph_commit_info(repo.odb(), *p)
                    .expect("pinfo")
                    .tree;
                let _ = diff_changed_paths_for_bloom(repo.odb(), Some(ptree), info.tree);
            }
            let _ = chain.bloom_precheck_for_paths(
                repo.odb(),
                oid,
                &["z.txt".to_owned(), ":exclude:missing".to_owned()],
                None,
                -1,
                true,
            );
            let _ = time;
        }
    }
    let _ = graph_path;
}