Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/storage-git-gix/parked-gix-2/gunnar-store-gix/tests/repack_recomputes_deltas.rs

#![cfg(all(feature = "fs", feature = "repack-delta"))]

//! **A repository with one pack can have its deltas recomputed**, and stock git
//! is the judge of the result.
//!
//! # Why this file exists, and why it is built the way it is
//!
//! Until 2026-08-04 `repack()` returned early whenever a repository held one
//! pack, no loose objects and nothing to prune — which is the state a repository
//! seeded by a single push is in, and the state 18 of the 19 real repositories
//! in gunnar's corpus were in. `gunnar gc` therefore could not re-encode a
//! delta anywhere it mattered, and a bench lane measured exactly that: 1 pack of
//! 1 618 917 B before `gc` and 1 pack of 1 618 917 B after.
//!
//! **That was the third feature lost to the same branch**, after the
//! reachability bitmap and the cold-tier fold. All three had green tests. The
//! reason is one sentence and it governs this whole file:
//!
//! > A fixture built by three pushes holds three packs and never takes the
//! > branch.
//!
//! So [`one_pack`] pushes **once**, and asserts the pack count before asserting
//! anything else. A fixture that quietly ended up with two packs would make
//! every test below pass while testing the wrong branch, which is precisely how
//! this bug survived twice.
//!
//! # The red, in the same test as the green
//!
//! [`a_single_pack_repository_can_have_its_deltas_recomputed`] runs the *same*
//! repository under [`DeltaPolicy::Preserve`] and under
//! [`DeltaPolicy::Recompute`], and asserts the first does nothing. The first is
//! the old behaviour, exactly; if the fix regressed to it, the second assertion
//! goes red rather than the test quietly proving that two identical paths agree.

use std::path::{Path, PathBuf};

use gix_hash::Kind;
use gunnar_store::fs::FsStore;
use gunnar_store::repack::{DeltaOrder, DeltaPolicy, RepackOptions, RepackOutcome};

// ── the `git` binary ─────────────────────────────────────────────────────────

fn git(dir: &Path, args: &[&str]) -> String {
    let out = git_try(dir, args);
    assert!(
        out.status.success(),
        "git {args:?} in {} failed: {:?}\n--- stdout ---\n{}\n--- stderr ---\n{}",
        dir.display(),
        out.status,
        String::from_utf8_lossy(&out.stdout),
        String::from_utf8_lossy(&out.stderr),
    );
    String::from_utf8_lossy(&out.stdout).into_owned()
}

fn git_try(dir: &Path, args: &[&str]) -> std::process::Output {
    std::process::Command::new("git")
        .current_dir(dir)
        .args(args)
        .env("GIT_AUTHOR_NAME", "Gunnar Test")
        .env("GIT_AUTHOR_EMAIL", "gunnar@example.invalid")
        .env("GIT_AUTHOR_DATE", "1700000000 +0000")
        .env("GIT_COMMITTER_NAME", "Gunnar Test")
        .env("GIT_COMMITTER_EMAIL", "gunnar@example.invalid")
        .env("GIT_COMMITTER_DATE", "1700000000 +0000")
        .env("GIT_CONFIG_GLOBAL", "/dev/null")
        .env("GIT_CONFIG_SYSTEM", "/dev/null")
        .output()
        .expect("could not run git")
}

fn git_available() -> bool {
    std::process::Command::new("git")
        .arg("--version")
        .output()
        .map(|o| o.status.success())
        .unwrap_or(false)
}

// ── the fixture ──────────────────────────────────────────────────────────────

struct Fixture {
    _dir: tempfile::TempDir,
    git_dir: PathBuf,
    hash: Kind,
}

impl Fixture {
    fn store(&self) -> FsStore {
        FsStore::open_or_init(&self.git_dir, self.hash).expect("open the repository")
    }

    fn packs(&self) -> Vec<PathBuf> {
        let mut out: Vec<PathBuf> = std::fs::read_dir(self.git_dir.join("objects/pack"))
            .expect("the pack directory exists")
            .flatten()
            .map(|e| e.path())
            .filter(|p| p.extension().and_then(|e| e.to_str()) == Some("pack"))
            .collect();
        out.sort();
        out
    }

    fn pack_bytes(&self) -> u64 {
        self.packs()
            .iter()
            .filter_map(|p| std::fs::metadata(p).ok())
            .map(|m| m.len())
            .sum()
    }

    /// Every object id the repository can reach, sorted. The set a repack must
    /// leave alone.
    fn object_ids(&self) -> Vec<String> {
        let mut v: Vec<String> = git(&self.git_dir, &["rev-list", "--objects", "--all"])
            .lines()
            .filter_map(|l| l.split_whitespace().next().map(str::to_owned))
            .collect();
        v.sort();
        v
    }
}

/// A monorepo shape, pushed **once**, so the repository holds exactly one pack.
///
/// `dirs` directories each holding an `index.ts` and a `README.md`, over
/// `revisions` commits. The shape is not decoration: it is the case git's
/// name-hash v1 cannot see, because every `index.ts` in the tree shares its last
/// sixteen path bytes and therefore its v1 hash. A fixture of one file per
/// commit would order identically under all three [`DeltaOrder`]s and could not
/// tell them apart.
fn one_pack(hash: Kind, dirs: usize, revisions: usize) -> Fixture {
    let dir = tempfile::tempdir().expect("temporary directory");
    let git_dir = dir.path().join("repo.git");
    FsStore::open_or_init(&git_dir, hash).expect("create the bare repository");
    // Without this git unpacks a small push into loose objects and the
    // single-pack state this whole file is about never arises.
    git(
        dir.path(),
        &[
            "--git-dir",
            git_dir.to_str().unwrap(),
            "config",
            "receive.unpackLimit",
            "1",
        ],
    );

    let work = dir.path().join("work");
    std::fs::create_dir_all(&work).expect("create the working tree");
    let fmt = match hash {
        Kind::Sha256 => "sha256",
        _ => "sha1",
    };
    git(
        &work,
        &[
            "init",
            "--quiet",
            &format!("--object-format={fmt}"),
            "--initial-branch=main",
            ".",
        ],
    );

    for rev in 0..revisions {
        for d in 0..dirs {
            let path = work.join(format!("packages/pkg{d:03}/src"));
            std::fs::create_dir_all(&path).expect("mkdir");
            // Long, highly similar content: what a delta encoder is for. Each
            // revision changes one line in the middle, so the right base makes
            // the delta tiny and the wrong base makes it the whole file.
            let body: String = (0..200)
                .map(|line| {
                    if line == rev % 200 {
                        format!("export const marker{d}_{rev} = {line};\n")
                    } else {
                        format!("export const value{line} = 'package {d} line {line}';\n")
                    }
                })
                .collect();
            std::fs::write(path.join("index.ts"), &body).expect("write");
            std::fs::write(
                path.parent().unwrap().join("README.md"),
                format!("# package {d}\n\nrevision {rev}\n{}", "filler\n".repeat(50)),
            )
            .expect("write");
        }
        git(&work, &["add", "-A"]);
        git(&work, &["commit", "--quiet", "-m", &format!("rev {rev}")]);
    }

    // ONE push. This is the whole premise of the file.
    git(
        &work,
        &[
            "push",
            "--quiet",
            git_dir.to_str().unwrap(),
            "HEAD:refs/heads/main",
        ],
    );

    let f = Fixture {
        _dir: dir,
        git_dir,
        hash,
    };
    // The premise, asserted rather than assumed. A fixture that ended up with
    // two packs would exercise the branch that always worked, and every
    // assertion in this file would be green for the wrong reason — which is the
    // exact mistake that let this bug survive two earlier fixes.
    assert_eq!(
        f.packs().len(),
        1,
        "the fixture must hold exactly ONE pack, or it tests the wrong branch"
    );
    f
}

fn repack(f: &Fixture, options: &RepackOptions) -> RepackOutcome {
    f.store().repack(options).expect("repack")
}

// ── 1. the headline, with its own red ────────────────────────────────────────

/// A single-pack repository re-encodes its deltas, and the *same* repository
/// under the old policy does not.
///
/// The `Preserve` half is not a formality. It is the behaviour this change
/// removed, run against the identical fixture: if the single-pack short-circuit
/// ever comes back, that half stays green and the `Recompute` half goes red,
/// which is the only arrangement in which the green means anything.
#[test]
fn a_single_pack_repository_can_have_its_deltas_recomputed() {
    assert!(git_available(), "this test requires `git` on PATH");
    let f = one_pack(Kind::Sha1, 12, 8);
    let before_bytes = f.pack_bytes();
    let before_ids = f.object_ids();

    // ── the old behaviour, on purpose ────────────────────────────────────────
    let preserved = repack(
        &f,
        &RepackOptions::new().delta_policy(DeltaPolicy::Preserve),
    );
    assert!(
        !preserved.rewrote,
        "consolidating a single-pack repository rewrote it, so this fixture is \
         not in the state the test is about: {preserved:?}"
    );
    assert_eq!(
        f.pack_bytes(),
        before_bytes,
        "a preserving repack of one pack changed its bytes"
    );

    // ── and the fix ─────────────────────────────────────────────────────────
    let recoded = repack(
        &f,
        &RepackOptions::new().delta_policy(DeltaPolicy::Recompute),
    );
    assert!(
        recoded.rewrote,
        "a single-pack repository STILL cannot be re-encoded; this is the bug \
         the whole change exists to remove: {recoded:?}"
    );
    assert!(
        recoded.objects_deltified > 0,
        "the repack rewrote the pack but computed not one delta, so it did the \
         expensive half of the work and none of the useful half: {recoded:?}"
    );
    assert_eq!(
        recoded.objects_copied_from_pack, 0,
        "the re-encoding producer copied an entry, which means it did not \
         re-encode it: {recoded:?}"
    );
    assert_eq!(
        recoded.objects_copied_from_pack + recoded.objects_recompressed + recoded.objects_deltified,
        recoded.objects as usize,
        "the generator's accounting does not add up: {recoded:?}"
    );

    // The applied output, read back off the pack that is on disk — not the
    // encoder's own report about itself.
    let after = recoded
        .delta_fraction_after
        .expect("the pack's composition was not measured");
    let before = recoded
        .delta_fraction_before
        .expect("the pack's composition was not measured");
    assert!(
        after > 0.0,
        "the pack on disk holds no deltas at all after a re-encode: {recoded:?}"
    );
    eprintln!(
        "delta fraction {before:.3} -> {after:.3}, pack {before_bytes} -> {} bytes",
        f.pack_bytes()
    );

    // ── and nothing was lost ────────────────────────────────────────────────
    assert_eq!(
        f.packs().len(),
        1,
        "a re-encoding repack left more than one pack behind"
    );
    assert_eq!(
        f.object_ids(),
        before_ids,
        "the re-encode changed which objects the repository holds"
    );
}

// ── 2. stock git is the judge ────────────────────────────────────────────────

/// `index-pack --strict`, `fsck --full --strict`, and every object's bytes
/// compared back through `cat-file`.
///
/// gunnar reading back what gunnar wrote can be wrong at both ends, and a delta
/// that reconstructs wrongly is invisible to every check that does not actually
/// reconstruct it. `--strict` on `index-pack` is what makes the pack's deltas be
/// applied rather than merely parsed.
#[test]
fn stock_git_accepts_a_re_encoded_pack() {
    assert!(git_available(), "this test requires `git` on PATH");
    for hash in [Kind::Sha1, Kind::Sha256] {
        let f = one_pack(hash, 8, 6);
        let before = object_bytes(&f);

        let outcome = repack(
            &f,
            &RepackOptions::new().delta_policy(DeltaPolicy::Recompute),
        );
        assert!(outcome.rewrote, "{hash}: nothing was re-encoded");
        assert!(
            outcome.objects_deltified > 0,
            "{hash}: no deltas were computed, so `--strict` would be applying none"
        );

        // fsck walks and reconstructs every object in the repository.
        git(&f.git_dir, &["fsck", "--full", "--strict"]);

        // …and the bitmap written beside the re-encoded pack still answers what
        // the walk answers. This is not covered by fsck and it is not
        // hypothetical: a re-encode emits its entries in the delta search's
        // order rather than the order they sat in the old pack, so anything
        // that keyed on pack position would go wrong here and nowhere else.
        assert!(
            outcome.bitmap.is_some(),
            "{hash}: no bitmap was written beside the re-encoded pack"
        );
        let ids = |args: &[&str]| -> Vec<String> {
            let mut v: Vec<String> = git(&f.git_dir, args)
                .lines()
                .filter_map(|l| l.split_whitespace().next().map(str::to_owned))
                .collect();
            v.sort();
            v
        };
        let walked = ids(&["rev-list", "--objects", "--all"]);
        let bitmapped = ids(&["rev-list", "--objects", "--use-bitmap-index", "--all"]);
        assert!(
            walked.len() > 50,
            "{hash}: the plain walk found only {} objects",
            walked.len()
        );
        assert_eq!(
            bitmapped, walked,
            "{hash}: the bitmap over the re-encoded pack disagrees with the walk"
        );

        // …and the pack re-verifies from its bytes alone, in a directory that
        // holds nothing else, so nothing can be resolved out of the repository.
        let pack = f.packs().pop().expect("a .pack");
        let scratch = tempfile::tempdir().expect("temporary directory");
        // `--object-format` is not optional here and its absence is not a
        // compile error: outside a repository `index-pack` assumes SHA-1, reads
        // a SHA-256 pack with 20-byte ids and reports `badTree: cannot be
        // parsed as a tree` — a corruption message about a pack that is
        // perfectly fine. MEASURED 2026-08-04 by hitting exactly that.
        let format = format!(
            "--object-format={}",
            match hash {
                Kind::Sha256 => "sha256",
                _ => "sha1",
            }
        );
        let out = git_try(
            scratch.path(),
            &[
                "index-pack",
                "--strict",
                &format,
                "-o",
                "out.idx",
                pack.to_str().unwrap(),
            ],
        );
        assert!(
            out.status.success(),
            "{hash}: git index-pack --strict rejected the re-encoded pack: {}",
            String::from_utf8_lossy(&out.stderr)
        );

        // Byte for byte, not by count. A pack that returned different bytes
        // under the right ids would pass every check above.
        let after = object_bytes(&f);
        assert_eq!(
            before.len(),
            after.len(),
            "{hash}: the object count changed across the re-encode"
        );
        assert!(
            before.len() > 50,
            "{hash}: the fixture has only {} objects, which is not evidence",
            before.len()
        );
        assert_eq!(
            before, after,
            "{hash}: an object came back with different bytes across the re-encode"
        );
    }
}

/// The negative control for the check above: `index-pack --strict` must be
/// **able** to reject a pack whose delta bytes are wrong.
///
/// Without it, every green in [`stock_git_accepts_a_re_encoded_pack`] could be a
/// check that accepts anything. The damage is one byte inside the pack's body,
/// past the header, which is the shape of a mis-encoded delta.
#[test]
fn git_goes_red_on_a_damaged_re_encoded_pack() {
    assert!(git_available(), "this test requires `git` on PATH");
    let f = one_pack(Kind::Sha1, 6, 4);
    let outcome = repack(
        &f,
        &RepackOptions::new().delta_policy(DeltaPolicy::Recompute),
    );
    assert!(outcome.rewrote);

    let pack = f.packs().pop().expect("a .pack");
    let mut bytes = std::fs::read(&pack).expect("read the pack");

    // Positive control first: undamaged, it must be accepted, or the red below
    // proves nothing.
    let scratch = tempfile::tempdir().expect("temporary directory");
    std::fs::write(scratch.path().join("copy.pack"), &bytes).expect("write");
    let good = git_try(
        scratch.path(),
        &["index-pack", "--strict", "-o", "good.idx", "copy.pack"],
    );
    assert!(
        good.status.success(),
        "the undamaged pack is already rejected, so this test cannot prove \
         anything: {}",
        String::from_utf8_lossy(&good.stderr)
    );

    // One byte in the middle of the body.
    let at = bytes.len() / 2;
    bytes[at] ^= 0xff;
    let scratch = tempfile::tempdir().expect("temporary directory");
    std::fs::write(scratch.path().join("copy.pack"), &bytes).expect("write");
    let bad = git_try(
        scratch.path(),
        &["index-pack", "--strict", "-o", "bad.idx", "copy.pack"],
    );
    assert!(
        !bad.status.success(),
        "git accepted a pack with a corrupted body, so `--strict` is blind here"
    );
}

/// Every object's canonical bytes, straight out of `git cat-file`.
fn object_bytes(f: &Fixture) -> Vec<(String, Vec<u8>)> {
    let mut out = Vec::new();
    for id in f.object_ids() {
        let raw = std::process::Command::new("git")
            .current_dir(&f.git_dir)
            .args(["cat-file", "-p", &id])
            .env("GIT_CONFIG_GLOBAL", "/dev/null")
            .env("GIT_CONFIG_SYSTEM", "/dev/null")
            .output()
            .expect("run git cat-file");
        assert!(raw.status.success(), "git cat-file -p {id} failed");
        out.push((id, raw.stdout));
    }
    out
}

// ── 3. the policy ────────────────────────────────────────────────────────────

/// `Auto` re-encodes a repository with no deltas, and declines one that already
/// has them.
///
/// Both halves, because a policy that always fired and a policy that never fired
/// would each pass one of them.
#[test]
fn the_auto_policy_fires_on_a_bad_pack_and_declines_a_good_one() {
    assert!(git_available(), "this test requires `git` on PATH");
    let f = one_pack(Kind::Sha1, 10, 6);

    // git's own push is well deltified, so `Auto` must leave it alone.
    let healthy = repack(&f, &RepackOptions::new());
    let fraction = healthy
        .delta_fraction_before
        .expect("the composition was not measured");
    assert!(
        fraction >= 0.20,
        "premise: git's pushed pack is only {fraction:.3} deltified, so this \
         half is testing the wrong case"
    );
    assert!(
        !healthy.rewrote,
        "`Auto` re-encoded a pack that is {fraction:.3} deltified: {healthy:?}"
    );

    // Now a repository whose objects are all loose and therefore all
    // undeltified, which is what gunnar's own client used to produce.
    let loose = loose_only(Kind::Sha1, 6, 6);
    let bad = repack(&loose, &RepackOptions::new());
    assert_eq!(
        bad.delta_fraction_before,
        Some(0.0),
        "premise: the loose fixture is supposed to hold no deltas at all"
    );
    assert!(
        bad.rewrote,
        "`Auto` declined a repository with no deltas whatever: {bad:?}"
    );
    assert!(
        bad.objects_deltified > 0,
        "`Auto` fired but computed no deltas: {bad:?}"
    );
}

/// A repository whose objects are all loose: no packs, no deltas, nothing to
/// copy. `git gc`'s starting point, and gunnar's own client's output before
/// `write_pack_deltified` existed.
fn loose_only(hash: Kind, dirs: usize, revisions: usize) -> Fixture {
    let dir = tempfile::tempdir().expect("temporary directory");
    let git_dir = dir.path().join("repo.git");
    FsStore::open_or_init(&git_dir, hash).expect("create the bare repository");

    let work = dir.path().join("work");
    std::fs::create_dir_all(&work).expect("create the working tree");
    let fmt = match hash {
        Kind::Sha256 => "sha256",
        _ => "sha1",
    };
    git(
        &work,
        &[
            "init",
            "--quiet",
            &format!("--object-format={fmt}"),
            "--initial-branch=main",
            ".",
        ],
    );
    for rev in 0..revisions {
        for d in 0..dirs {
            let path = work.join(format!("packages/pkg{d:03}/src"));
            std::fs::create_dir_all(&path).expect("mkdir");
            let body: String = (0..150)
                .map(|line| {
                    if line == rev {
                        format!("changed at {d}/{rev}\n")
                    } else {
                        format!("stable line {line} of package {d}\n")
                    }
                })
                .collect();
            std::fs::write(path.join("index.ts"), &body).expect("write");
        }
        git(&work, &["add", "-A"]);
        git(&work, &["commit", "--quiet", "-m", &format!("rev {rev}")]);
    }
    // `--no-thin` plus a high unpackLimit: git unpacks the push into loose
    // objects, which is exactly the state being tested.
    git(
        dir.path(),
        &[
            "--git-dir",
            git_dir.to_str().unwrap(),
            "config",
            "receive.unpackLimit",
            "100000",
        ],
    );
    git(
        &work,
        &[
            "push",
            "--quiet",
            "--no-thin",
            git_dir.to_str().unwrap(),
            "HEAD:refs/heads/main",
        ],
    );

    let f = Fixture {
        _dir: dir,
        git_dir,
        hash,
    };
    assert!(
        f.packs().is_empty(),
        "the loose fixture ended up with a pack, so it is not loose"
    );
    f
}

// ── 4. the ordering, which is the whole lever ────────────────────────────────

/// The three orderings produce **different packs**, and the path-aware ones are
/// not worse.
///
/// This is the assertion that the sort key is wired at all. A `DeltaOrder` that
/// was accepted, logged and then ignored would give three identical pack sizes,
/// and every other test in this file would still be green — which is the exact
/// shape of a silent green this tree has found nine of.
///
/// It asserts *difference* and a *direction*, not a ratio. The published ratios
/// (fluentui 439 MiB → 160 MiB) are for repositories four orders of magnitude
/// larger than a fixture, and claiming one here would be inventing a number.
#[test]
fn the_delta_ordering_changes_the_pack_it_produces() {
    assert!(git_available(), "this test requires `git` on PATH");

    let mut sizes = Vec::new();
    for order in [
        DeltaOrder::Size,
        DeltaOrder::NameHashV1,
        DeltaOrder::NameHashV2,
        DeltaOrder::PathWalk,
    ] {
        // A fresh fixture per ordering: repacking the same one repeatedly would
        // measure each ordering against the previous ordering's output.
        //
        // **Loose**, not a pushed pack, and that is not incidental. A repack of
        // a single pack refuses to install a result that is not smaller than the
        // pack it would replace (see
        // `a_re_encode_that_would_not_shrink_the_pack_is_declined`), and
        // `DeltaOrder::Size` really is worse than what stock git shipped — so on
        // a pushed fixture this loop would measure "the guard declined" three
        // times and compare nothing. A repository with no pack has nothing to be
        // declined against, so every ordering's output reaches the disk and can
        // be read.
        let f = loose_only(Kind::Sha1, 16, 10);
        let outcome = repack(
            &f,
            &RepackOptions::new()
                .delta_policy(DeltaPolicy::Recompute)
                .delta_order(order),
        );
        assert!(outcome.rewrote, "{order:?}: nothing was re-encoded");
        git(&f.git_dir, &["fsck", "--full", "--strict"]);
        let bytes = f.pack_bytes();
        eprintln!(
            "{order:?}: {bytes} pack bytes, {} deltified of {}",
            outcome.objects_deltified, outcome.objects
        );
        sizes.push((order, bytes, outcome.objects_deltified));
    }

    let by_size: u64 = sizes[0].1;
    let v2: u64 = sizes[2].1;
    let path_walk: u64 = sizes[3].1;

    // The key is wired: at least one path-aware ordering produced a different
    // pack from the size-only one. Identical bytes would mean the sort key never
    // reached the window.
    assert!(
        v2 != by_size || path_walk != by_size,
        "every ordering produced the same {by_size}-byte pack, so the sort key \
         is not reaching the delta search at all: {sizes:?}"
    );

    // And it is not wired backwards. On a fixture whose every `index.ts` shares
    // its last sixteen path bytes, grouping by path must not do *worse* than
    // ignoring paths entirely — a 5% margin, because a fixture this small has
    // window-boundary noise a real repository does not.
    let margin = by_size + by_size / 20;
    assert!(
        v2 <= margin,
        "name-hash v2 produced {v2} bytes against {by_size} for no ordering at \
         all, which is the wrong direction: {sizes:?}"
    );
    assert!(
        path_walk <= margin,
        "path-walk produced {path_walk} bytes against {by_size} for no ordering \
         at all, which is the wrong direction: {sizes:?}"
    );
}

/// A re-encode that would leave the repository **bigger** is thrown away, and
/// the pack that was already there is kept.
///
/// `DeltaOrder::Size` is the honest case, not a contrived one: on a monorepo
/// shape it is genuinely worse than the ordering stock git used when it built
/// the pack in the first place, so the search runs, produces a valid pack, and
/// that pack is larger. Installing it would churn the pack id — invalidating
/// every reader's mmap — to make the repository bigger.
///
/// Seen red: delete the `new_bytes >= old_bytes` branch in `repack()` and this
/// goes red on `rewrote`, with the pack on disk grown.
#[test]
fn a_re_encode_that_would_not_shrink_the_pack_is_declined() {
    assert!(git_available(), "this test requires `git` on PATH");
    let f = one_pack(Kind::Sha1, 16, 10);
    let before_bytes = f.pack_bytes();
    let before_packs = f.packs();

    let outcome = repack(
        &f,
        &RepackOptions::new()
            .delta_policy(DeltaPolicy::Recompute)
            .delta_order(DeltaOrder::Size),
    );

    // The premise, and the whole reason `reencode_declined` is a field: without
    // it, "the search ran and its answer was worse" is indistinguishable from
    // "the search never ran", and this test would pass just as happily against a
    // build where `DeltaPolicy::Recompute` did nothing at all.
    assert!(
        outcome.reencode_declined,
        "the re-encode was never declined, so either it was not attempted or its \
         result was installed: {outcome:?}"
    );

    assert!(
        !outcome.rewrote,
        "a re-encode that makes the repository bigger was installed anyway: {outcome:?}"
    );
    assert_eq!(
        f.pack_bytes(),
        before_bytes,
        "the pack on disk changed size despite the repack reporting no rewrite"
    );
    assert_eq!(
        f.packs(),
        before_packs,
        "the declined pack was left on disk, or the original was replaced"
    );
    // And the repository is still readable, which is the thing a half-finished
    // decline would break.
    git(&f.git_dir, &["fsck", "--full", "--strict"]);
}

// ── 5. real repositories ─────────────────────────────────────────────────────

/// The same checks against **real repositories**, which is where the branch
/// actually mattered.
///
/// Point `GUNNAR_REAL_REPOS` at a colon-separated list of repository paths.
/// Skipped when unset — the corpus lives outside this repository — but the skip
/// is printed, so a green here is never mistaken for a run.
///
/// The mirrors are built by **pushing**, not by `git clone --mirror`: a mirror
/// clone silently repacks, so a corpus built that way measures an
/// already-consolidated, already-well-deltified pack and reports that `gc` had
/// nothing to do. That mistake cost one lane a published conclusion on
/// 2026-08-04 and is why the pack count is asserted below.
#[test]
fn real_repositories_survive_a_re_encode_and_report_what_it_cost() {
    assert!(git_available(), "this test requires `git` on PATH");
    let Ok(list) = std::env::var("GUNNAR_REAL_REPOS") else {
        eprintln!("SKIPPED: set GUNNAR_REAL_REPOS to a colon-separated list of repositories");
        return;
    };
    let scratch = tempfile::tempdir().expect("temporary directory");
    let mut checked = 0usize;
    let mut smaller = 0usize;

    for src in list.split(':').filter(|s| !s.is_empty()) {
        let name = Path::new(src)
            .file_name()
            .unwrap()
            .to_str()
            .unwrap()
            .to_owned();
        let mirror = scratch.path().join(format!("{name}.git"));
        let fmt = git(Path::new(src), &["rev-parse", "--show-object-format"]);
        let hash = if fmt.trim() == "sha256" {
            Kind::Sha256
        } else {
            Kind::Sha1
        };
        FsStore::open_or_init(&mirror, hash).expect("create the bare repository");
        git(
            scratch.path(),
            &[
                "--git-dir",
                mirror.to_str().unwrap(),
                "config",
                "receive.unpackLimit",
                "1",
            ],
        );
        let pushed = git_try(
            Path::new(src),
            &["push", "--quiet", "--mirror", mirror.to_str().unwrap()],
        );
        if !pushed.status.success() {
            eprintln!("skipping {name}: push failed");
            continue;
        }

        let f = Fixture {
            _dir: tempfile::tempdir().expect("temporary directory"),
            git_dir: mirror.clone(),
            hash,
        };
        let before_ids = f.object_ids();
        if before_ids.len() < 200 {
            eprintln!("skipping {name}: only {} objects", before_ids.len());
            continue;
        }
        let before_bytes = f.pack_bytes();
        let packs_before = f.packs().len();

        let started = std::time::Instant::now();
        let outcome = repack(
            &f,
            &RepackOptions::new()
                .delta_policy(DeltaPolicy::Recompute)
                .delta_order(DeltaOrder::NameHashV2),
        );
        let elapsed = started.elapsed();

        assert!(outcome.rewrote, "{name}: nothing was re-encoded");
        assert_eq!(
            f.object_ids(),
            before_ids,
            "{name}: the re-encode changed the object graph"
        );
        git(&f.git_dir, &["fsck", "--full", "--strict"]);

        let after_bytes = f.pack_bytes();
        if after_bytes < before_bytes {
            smaller += 1;
        }
        eprintln!(
            "{name} ({}): {} packs, {} objects, {before_bytes} -> {after_bytes} bytes \
             ({:.4}x), delta fraction {:.3} -> {:.3}, {:.1} s",
            fmt.trim(),
            packs_before,
            before_ids.len(),
            after_bytes as f64 / before_bytes as f64,
            outcome.delta_fraction_before.unwrap_or(f32::NAN),
            outcome.delta_fraction_after.unwrap_or(f32::NAN),
            elapsed.as_secs_f64(),
        );
        checked += 1;
    }

    assert!(
        checked > 0,
        "the list named no usable repository, so nothing was checked"
    );
    eprintln!("{smaller} of {checked} repositories came out smaller");
}

// ── 5. the window, which is the ordering's other half ────────────────────────

/// `RepackOptions::delta_search` reaches the encoder, and the proof is an
/// **applied output**: a repack given a window of `1` produces a *different,
/// larger* pack than the same repack given a window of `32`.
///
/// The two numbers were `..Default::default()`'s hardcoded 10 and 50 until
/// 2026-08-04 and no caller could reach them. A field added to `RepackOptions`,
/// threaded through `write_recoded_pack_file` and then dropped on the floor
/// would leave both repacks byte-identical and every other test in this file
/// green — the exact shape of the silent green this tree has found nine of, so
/// the assertion is on the bytes and not on the option round-tripping.
///
/// **Seen red on 2026-08-04**: revert the `window:`/`depth:` lines in
/// `write_recoded_pack_file` to `..Default::default()` and the two repacks
/// produce the same byte count, which fails on `assert_ne`.
///
/// The direction is asserted too, and it is not a tuning claim: a window of one
/// offers every object exactly one candidate, so it can only find a subset of
/// the bases a window of 32 can. Fewer bases found cannot make a pack smaller.
#[test]
fn the_delta_window_reaches_the_search_and_changes_the_pack() {
    assert!(git_available(), "this test requires `git` on PATH");

    let mut out = Vec::new();
    for window in [1usize, 32] {
        // Loose, for the same reason the ordering test is loose: a repack of a
        // single pack declines a result that is not smaller, so the narrow
        // window would be measured as "declined" rather than as its own pack.
        let f = loose_only(Kind::Sha1, 16, 10);
        let outcome = repack(
            &f,
            &RepackOptions::new()
                .delta_policy(DeltaPolicy::Recompute)
                .delta_order(DeltaOrder::NameHashV2)
                .delta_search(window, 50),
        );
        assert!(outcome.rewrote, "window {window}: nothing was re-encoded");
        git(&f.git_dir, &["fsck", "--full", "--strict"]);
        let bytes = f.pack_bytes();
        eprintln!(
            "window {window}: {bytes} pack bytes, {} deltified of {}",
            outcome.objects_deltified, outcome.objects
        );
        out.push((window, bytes, outcome.objects_deltified));
    }

    let (_, narrow, narrow_deltas) = out[0];
    let (_, wide, wide_deltas) = out[1];
    assert_ne!(
        narrow, wide,
        "a window of 1 and a window of 32 produced the same {narrow}-byte pack, \
         so `RepackOptions::delta_window` is not reaching the delta search: {out:?}"
    );
    assert!(
        wide < narrow,
        "the wider window produced the LARGER pack ({wide} against {narrow}), \
         which means the window is wired backwards: {out:?}"
    );
    assert!(
        wide_deltas >= narrow_deltas,
        "the wider window found FEWER bases ({wide_deltas} against \
         {narrow_deltas}), which a superset search cannot do: {out:?}"
    );
}

/// The depth cap reaches the search too, and it is a **separate** wire from the
/// window.
///
/// One setter sets both, so a mapping that assigned the window to both fields
/// would pass the test above and be undetectable there. Here the window is held
/// at 32 and only the depth moves; a pack built with a chain limit of 1 cannot
/// be the same bytes as one built with a limit of 50.
///
/// **Seen red on 2026-08-04**: map `depth: options.delta_window` in
/// `write_recoded_pack_file` and this fails while
/// `the_delta_window_reaches_the_search_and_changes_the_pack` still passes.
#[test]
fn the_delta_depth_is_its_own_wire() {
    assert!(git_available(), "this test requires `git` on PATH");

    let mut out = Vec::new();
    for depth in [1usize, 50] {
        let f = loose_only(Kind::Sha1, 16, 10);
        let outcome = repack(
            &f,
            &RepackOptions::new()
                .delta_policy(DeltaPolicy::Recompute)
                .delta_order(DeltaOrder::NameHashV2)
                .delta_search(32, depth),
        );
        assert!(outcome.rewrote, "depth {depth}: nothing was re-encoded");
        git(&f.git_dir, &["fsck", "--full", "--strict"]);
        let bytes = f.pack_bytes();
        eprintln!("depth {depth}: {bytes} pack bytes");
        out.push((depth, bytes));
    }
    assert_ne!(
        out[0].1, out[1].1,
        "a chain limit of 1 and one of 50 produced the same pack, so \
         `RepackOptions::delta_depth` is not reaching the delta search — or it \
         is being fed the window: {out:?}"
    );
    assert!(
        out[1].1 < out[0].1,
        "the deeper chain limit produced the LARGER pack ({} against {}), which \
         means the depth is wired backwards: {out:?}",
        out[1].1,
        out[0].1
    );
}

// ── 6. the copy path must not roll dice ──────────────────────────────────────

/// A store shaped like a real one: **several pushes**, so several packs, with
/// the same objects in more than one of them.
///
/// That last part is the whole point and it is not contrived. A push sends a
/// thin pack and `index-pack --fix-thin` appends the bases it was thin against,
/// so every object that was a delta base for the next push is physically in two
/// packs. All 19 repositories in gunnar's corpus are in this state; the
/// single-pack fixtures above are the state a repository is in only immediately
/// after a `gc`.
fn many_packs(hash: Kind, dirs: usize, revisions: usize, pushes: usize) -> Fixture {
    let dir = tempfile::tempdir().expect("temporary directory");
    let git_dir = dir.path().join("repo.git");
    FsStore::open_or_init(&git_dir, hash).expect("create the bare repository");
    git(
        dir.path(),
        &[
            "--git-dir",
            git_dir.to_str().unwrap(),
            "config",
            "receive.unpackLimit",
            "1",
        ],
    );

    let work = dir.path().join("work");
    std::fs::create_dir_all(&work).expect("create the working tree");
    let fmt = match hash {
        Kind::Sha256 => "sha256",
        _ => "sha1",
    };
    git(
        &work,
        &[
            "init",
            "--quiet",
            &format!("--object-format={fmt}"),
            "--initial-branch=main",
            ".",
        ],
    );

    let per_push = revisions.div_ceil(pushes).max(1);
    for rev in 0..revisions {
        for d in 0..dirs {
            let path = work.join(format!("packages/pkg{d:03}/src"));
            std::fs::create_dir_all(&path).expect("mkdir");
            let body: String = (0..200)
                .map(|line| {
                    if line == rev % 200 {
                        format!("export const marker{d}_{rev} = {line};\n")
                    } else {
                        format!("export const value{line} = 'package {d} line {line}';\n")
                    }
                })
                .collect();
            std::fs::write(path.join("index.ts"), &body).expect("write");
            std::fs::write(
                path.parent().unwrap().join("README.md"),
                format!("# package {d}\n\nrevision {rev}\n{}", "filler\n".repeat(50)),
            )
            .expect("write");
        }
        git(&work, &["add", "-A"]);
        git(&work, &["commit", "--quiet", "-m", &format!("rev {rev}")]);
        // A push every `per_push` commits, and one at the end: each one is a
        // thin pack against what the server already has, and `--fix-thin`
        // duplicates its bases into the new pack.
        if (rev + 1) % per_push == 0 || rev + 1 == revisions {
            git(
                &work,
                &[
                    "push",
                    "--quiet",
                    git_dir.to_str().unwrap(),
                    "HEAD:refs/heads/main",
                ],
            );
        }
    }

    let f = Fixture {
        _dir: dir,
        git_dir,
        hash,
    };
    assert!(
        f.packs().len() >= 3,
        "the fixture must hold several packs or it exercises the wrong branch, \
         got {}",
        f.packs().len()
    );
    f
}

/// **Two `gc`s of one store produce the same pack, byte for byte.**
///
/// One store, copied three times with its timestamps preserved, repacked three
/// times. A copy-path repack — the delta policy is `Preserve`, nothing is
/// re-encoded — so the three runs differ in exactly one thing: which thread got
/// to which object first.
///
/// # Why a forge may not roll dice here
///
/// gunnar's serve path copies pack entries verbatim (`P-001`). Whatever the
/// repacker leaves in the pack is what every clone and every fetch is sent
/// until the next `gc`. A nondeterministic repacker is therefore a
/// nondeterministic *wire format*, and "the same server, the same repository,
/// the same client, a different number of bytes" is not a thing anyone can
/// debug.
///
/// # Seen red, on the corpus and then here
///
/// MEASURED on oden 2026-08-04, before the fix: three `gunnar gc` runs over
/// three byte-identical copies of the 19-repository `workspace_nordisk` store,
/// default flags, the policy declining on all 19 —
///
/// | repository | run 1 | run 2 | run 3 | spread |
/// |---|---:|---:|---:|---:|
/// | nornir | 17 063 575 | 17 550 935 | 18 106 353 | **6.1%** |
/// | knut | 3 461 019 | 3 628 435 | 3 647 139 | 5.4% |
/// | modgunn | 1 582 224 | 1 516 439 | 1 558 746 | 4.3% |
///
/// Pinned to a single CPU the same runs were byte-identical, and nornir came
/// out 15 854 870 — smaller than all three. The cause is `count::objects`
/// recording whichever pack a duplicated object was found in first, and
/// `iter_from_counts` then sorting by `(pack_id, pack_offset)` and being able
/// to copy a delta only when its base precedes it.
///
/// Restore `thread_limit: options.thread_limit` on the count in
/// `write_pack_file` and this test goes red on run 1.
///
/// # What this does NOT guarantee, and it was found the hard way
///
/// The copies keep their **mtimes**, deliberately. An earlier version of this
/// test built a fresh fixture per run and failed even with the fix, because a
/// store's pack *load order* is its packs' mtime order, several pushes into a
/// fixture land inside one timestamp tick, and a duplicated object then
/// resolves to a different pack. That is a second, separate nondeterminism,
/// it is inside gix's odb slot assignment rather than in this module, and it is
/// NOT fixed here. What is fixed and what is guarded is the thread race; two
/// stores that differ only in pack mtimes may still repack differently.
#[test]
fn two_copy_repacks_of_one_store_produce_the_same_pack() {
    assert!(git_available(), "this test requires `git` on PATH");

    // ONE store, built once. Three copies of it, not three fixtures: the
    // question is whether one input has one output.
    let origin = many_packs(Kind::Sha1, 24, 24, 6);
    let packs_in = origin.packs().len();

    let mut results = Vec::new();
    for run in 0..3 {
        let dir = tempfile::tempdir().expect("temporary directory");
        let git_dir = dir.path().join("repo.git");
        // `cp -a`: contents AND timestamps, so the pack load order is the same
        // in all three copies and the only remaining variable is scheduling.
        let out = std::process::Command::new("cp")
            .arg("-a")
            .arg(&origin.git_dir)
            .arg(&git_dir)
            .output()
            .expect("cp");
        assert!(out.status.success(), "cp -a failed: {out:?}");

        let f = Fixture {
            _dir: dir,
            git_dir,
            hash: Kind::Sha1,
        };
        assert_eq!(
            f.packs().len(),
            packs_in,
            "run {run}: the copy does not hold the same packs as the original"
        );
        let outcome = repack(
            &f,
            &RepackOptions::new().delta_policy(DeltaPolicy::Preserve),
        );
        assert!(outcome.rewrote, "run {run}: nothing was consolidated");
        assert_eq!(
            f.packs().len(),
            1,
            "run {run}: {packs_in} packs did not fold into one"
        );
        git(&f.git_dir, &["fsck", "--full", "--strict"]);
        let pack = f.packs().into_iter().next().expect("one pack");
        let bytes = std::fs::read(&pack).expect("read the pack");
        results.push((
            bytes.len(),
            pack.file_name().unwrap().to_string_lossy().into_owned(),
        ));
    }

    // The pack's NAME is its own content hash, so comparing names compares
    // every byte; the length is carried alongside so a failure says how far
    // apart the two answers were rather than only that they differed.
    let first = &results[0];
    for (i, r) in results.iter().enumerate().skip(1) {
        assert_eq!(
            r, first,
            "run {i} produced a different pack from run 0 ({r:?} against \
             {first:?}); `gunnar gc` is copying pack entries in a \
             thread-dependent order, and gunnar's serve path copies those \
             entries verbatim, so this is a nondeterministic wire format"
        );
    }
    eprintln!("three copy repacks of one {packs_in}-pack store agreed: {first:?}");
}

// ── 7. the default ordering, asserted as an applied output ───────────────────

/// **`RepackOptions::default()` re-encodes under `TransferV2`, and the proof is
/// the pack it writes.**
///
/// `assert_eq!(DeltaOrder::default(), DeltaOrder::TransferV2)` would be a state
/// round-trip: it would pass just as well if `write_recoded_pack_file` ignored
/// the field, which is the failure this file has caught twice. So the default
/// is compared against two *explicit* repacks of the same fixture — it must
/// produce the transfer ordering's pack and not the previous default's.
///
/// The `NameHashV2` half is the negative control. Without it, a fixture on
/// which the two orderings happened to agree would make the assertion vacuous
/// and it would stay green for ever.
///
/// **Seen red on 2026-08-04**: move `#[default]` back to `NameHashV2` and this
/// fails on the first assertion, with the default's bytes equal to the
/// name-hash pack's.
#[test]
fn the_default_ordering_is_the_transfer_ordering_in_the_pack_it_writes() {
    assert!(git_available(), "this test requires `git` on PATH");

    let mut out = Vec::new();
    for (label, options) in [
        // The default: `delta_order` deliberately NOT named.
        (
            "default",
            RepackOptions::new().delta_policy(DeltaPolicy::Recompute),
        ),
        (
            "transfer-v2",
            RepackOptions::new()
                .delta_policy(DeltaPolicy::Recompute)
                .delta_order(DeltaOrder::TransferV2),
        ),
        (
            "name-hash-v2",
            RepackOptions::new()
                .delta_policy(DeltaPolicy::Recompute)
                .delta_order(DeltaOrder::NameHashV2),
        ),
    ] {
        let f = loose_only(Kind::Sha1, 16, 10);
        let outcome = repack(&f, &options);
        assert!(outcome.rewrote, "{label}: nothing was re-encoded");
        git(&f.git_dir, &["fsck", "--full", "--strict"]);
        let bytes = f.pack_bytes();
        eprintln!("{label}: {bytes} pack bytes");
        out.push((label, bytes));
    }

    assert_eq!(
        out[0].1, out[1].1,
        "the default repack produced {} bytes and an explicit `TransferV2` \
         repack produced {}, so `DeltaOrder::default()` is not what a default \
         repack actually runs: {out:?}",
        out[0].1, out[1].1
    );
    assert_ne!(
        out[0].1, out[2].1,
        "the default repack and an explicit `NameHashV2` repack produced the \
         same {} bytes, so this fixture cannot tell the two orderings apart and \
         the assertion above proves nothing: {out:?}",
        out[0].1
    );
}