weavatrix-git 0.3.1

Fast, bounded, evidence-carrying Git reader with an optional read-only MCP server
Documentation
use std::{
    collections::BTreeSet,
    fs,
    path::{Path, PathBuf},
    process::Command,
    sync::atomic::{AtomicU64, Ordering},
    time::{SystemTime, UNIX_EPOCH},
};

use weavatrix_git::{PathBloom, Repository};

struct Fixture {
    path: PathBuf,
}

static FIXTURE_SEQUENCE: AtomicU64 = AtomicU64::new(0);

impl Fixture {
    fn new() -> Self {
        let unique = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let path = std::env::temp_dir().join(format!(
            "weavatrix-storage-{}-{unique}-{}",
            std::process::id(),
            FIXTURE_SEQUENCE.fetch_add(1, Ordering::Relaxed)
        ));
        fs::create_dir_all(&path).unwrap();
        git(&path, &["init", "-q"]);
        git(&path, &["config", "user.name", "Weavatrix Test"]);
        git(&path, &["config", "user.email", "test@weavatrix.local"]);
        Self { path }
    }

    fn commit(&self, revision: usize) {
        fs::write(
            self.path.join("tracked.txt"),
            format!("revision={revision}\n"),
        )
        .unwrap();
        git(&self.path, &["add", "-A"]);
        git(
            &self.path,
            &["commit", "-q", "-m", &format!("revision {revision}")],
        );
    }
}

impl Drop for Fixture {
    fn drop(&mut self) {
        fs::remove_dir_all(&self.path).ok();
    }
}

#[test]
fn reads_split_commit_graph_and_changed_path_bloom() {
    let fixture = Fixture::new();
    fixture.commit(0);
    fixture.commit(1);
    git(
        &fixture.path,
        &[
            "commit-graph",
            "write",
            "--reachable",
            "--changed-paths",
            "--split=no-merge",
        ],
    );
    fixture.commit(2);
    fixture.commit(3);
    git(
        &fixture.path,
        &[
            "commit-graph",
            "write",
            "--reachable",
            "--changed-paths",
            "--split=no-merge",
        ],
    );
    git(&fixture.path, &["commit-graph", "verify"]);

    let repository = Repository::open(&fixture.path).unwrap();
    assert!(repository.commit_graph_layer_count() >= 2);
    let head = repository.resolve("HEAD").unwrap();
    assert_eq!(
        repository
            .commit_maybe_changed_path(head, b"tracked.txt")
            .unwrap(),
        Some(PathBloom::Maybe)
    );
    assert_eq!(
        repository
            .commit_maybe_changed_path(head, b"never-created.txt")
            .unwrap(),
        Some(PathBloom::DefinitelyNot)
    );
    assert_eq!(repository.commit_metadata(head).unwrap().parents.len(), 1);
}

#[test]
fn routes_objects_through_multi_pack_index() {
    let fixture = Fixture::new();
    fixture.commit(0);
    git(&fixture.path, &["repack", "-q", "-d", "-l"]);
    fixture.commit(1);
    git(&fixture.path, &["repack", "-q", "-d", "-l"]);
    git(&fixture.path, &["multi-pack-index", "write", "--bitmap"]);
    git(&fixture.path, &["multi-pack-index", "verify"]);

    let repository = Repository::open(&fixture.path).unwrap();
    assert!(repository.pack_count() >= 2);
    assert_eq!(repository.multi_pack_index_count(), 1);
    let head = repository.resolve("HEAD").unwrap();
    assert_eq!(
        repository.commit(head).unwrap().summary_lossy(),
        "revision 1"
    );
    assert!(repository.contains(head));
    let reachable = repository
        .bitmap_reachable(head)
        .unwrap()
        .expect("Git wrote a MIDX bitmap")
        .into_iter()
        .map(|id| id.to_string())
        .collect::<BTreeSet<_>>();
    let expected = git_output(&fixture.path, &["rev-list", "--objects", "HEAD"])
        .lines()
        .filter_map(|line| line.split_ascii_whitespace().next())
        .map(str::to_owned)
        .collect::<BTreeSet<_>>();
    assert_eq!(reachable, expected);

    let ids = repository
        .history_ids(
            head,
            weavatrix_git::HistoryOptions {
                max_commits: 2,
                ..weavatrix_git::HistoryOptions::default()
            },
        )
        .unwrap();
    let parallel = repository.commit_metadata_parallel(&ids);
    assert_eq!(parallel.len(), 2);
    assert_eq!(parallel[0].as_ref().unwrap().id, ids[0]);
}

#[test]
fn reads_pack_reachability_bitmap() {
    let fixture = Fixture::new();
    fixture.commit(0);
    fixture.commit(1);
    fixture.commit(2);
    git(&fixture.path, &["repack", "-q", "-a", "-d", "-b"]);

    let repository = Repository::open(&fixture.path).unwrap();
    assert_eq!(repository.multi_pack_index_count(), 0);
    let head = repository.resolve("HEAD").unwrap();
    let reachable = repository
        .bitmap_reachable(head)
        .unwrap()
        .expect("Git wrote a pack bitmap")
        .into_iter()
        .map(|id| id.to_string())
        .collect::<BTreeSet<_>>();
    let expected = git_output(&fixture.path, &["rev-list", "--objects", "HEAD"])
        .lines()
        .filter_map(|line| line.split_ascii_whitespace().next())
        .map(str::to_owned)
        .collect::<BTreeSet<_>>();
    assert_eq!(reachable, expected);
}

fn git(path: &Path, args: &[&str]) {
    let output = Command::new("git")
        .args(args)
        .current_dir(path)
        .output()
        .unwrap();
    assert!(
        output.status.success(),
        "git {args:?}: {}",
        String::from_utf8_lossy(&output.stderr)
    );
}

fn git_output(path: &Path, args: &[&str]) -> String {
    let output = Command::new("git")
        .args(args)
        .current_dir(path)
        .output()
        .unwrap();
    assert!(output.status.success());
    String::from_utf8(output.stdout).unwrap()
}