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/src/serve.rs

//! **`GitServe` over `gix`** — the reading contract's gix arm.
//!
//! `GitOps` is the storage contract and it provably cannot serve the wire:
//! `Stored::bytes` is *"the pack entry, header and all, byte for byte as the
//! client sent them"* — possibly a delta — and none of the eleven returns an
//! inflated object. This module is the other half: [`GitServe::read`] and
//! [`GitServe::header`] for the two consumers that decode
//! (`gunnar-wire`'s `Graph::load`, `gunnar-policy`'s ancestry walk and signature
//! gate), and [`GitServe::emit_pack`] / [`GitServe::select`] for the serving
//! half.
//!
//! # The ordering rule, honoured
//!
//! `GitServe` is defined by what the **Arrow arm** needs to serve a clone well;
//! this arm then implements it. Nothing here asked for a method, and nothing
//! here reads a `.bitmap`. Where the two arms answer the same question by
//! different means the *semantics* are matched deliberately, method by method,
//! against `znippy-plugin-git/src/serve.rs` — the notes below say where and why.
//!
//! # `emit_pack` copies. It does not re-deflate, and it does not walk.
//!
//! Two separate properties, and both are the kind that no exit code can see:
//!
//! * **A copy, not a re-deflate.** A naive emitter inflates every object in
//!   order to deflate it again; the result passes `git index-pack --strict`
//!   *and* `git fsck` while sending a measured **18.4×** the wire bytes. That is
//!   why [`PackStats`] carries `copied` and `recompressed` and why this module
//!   counts them off what happened rather than asserting them. Every payload
//!   here is a slice of a mapped `.pack` written straight to `out`. The only
//!   thing rewritten is an `OFS_DELTA`'s header, and only because it carries a
//!   distance relative to a position in the *input* pack.
//! * **An exact set, not a closure.** `emit_pack`'s first parameter is
//!   `objects`, and it is *"a set to emit, not a set of tips to close over"*.
//!   Upload-pack's caller holds a selection that is already post-filter,
//!   post-shallow and post-`include-tag`, and deliberately **not** closed. An
//!   engine that closes over it adds back exactly what `--filter=blob:none` or
//!   `--depth=N` excluded — an over-send that passes every guard we own. This
//!   module walks no graph on that path. [`GitServe::select`] owns the
//!   close-over-tips half; `emit_pack` does not.
//!
//! **There is no delta-base closure either, and that is the 2026-08-14 fix.**
//! This module used to add a delta's base when the base fell outside the
//! request, on the reasoning that an `OFS_DELTA` names its base by in-pack
//! distance and so cannot be encoded without it. That reasoning is correct about
//! the *pack format* and wrong about the *clone*:
//!
//! * a base pulled in is an object the client did not ask for, and
//! * if it is a **tree**, it arrives owing children the pack does not contain.
//!
//! `git index-pack --check-self-contained-and-connected` — what `git clone`
//! runs — walks every received object's links and then demands each one exist,
//! so the clone dies with `fatal: did not receive expected object <oid>` while
//! this server logs a successful `upload-pack`. Every store that has ever
//! refused a ref update holds unreachable objects (a lost compare-and-swap, a
//! reset branch, a `git fast-import`), and `pack-objects` stores the newest
//! version of a path whole and the older ones as deltas against it — so after a
//! reset the object that is still reachable is routinely a delta against one
//! that is not. That is the whole of the intermittent `gunnar.year` failure and
//! the deterministic `gunnar.import_export` one; a different oid each run
//! because which reachable object hangs off an unreachable base is a property of
//! how that week's push happened to deltify. The identical defect was found and
//! fixed on the Arrow arm on 2026-08-11.
//!
//! So `stats.objects` is exactly `objects.len()`, deduplicated, and the base
//! decides how an entry is **encoded** rather than what the pack contains — base
//! inside the request, copy the stored bytes; base outside it, rebuild the
//! object whole and count it in `recompressed`. That is stock `pack-objects`'
//! rule. `recompressed` is 0 for a whole-repository clone of a store with no
//! unreachable history, because such a request contains every base, and both
//! numbers are counted off what happened rather than asserted.
//!
//! # No rayon, no pool, no fan-out — `P-004`
//!
//! *"Pure `std::thread::scope`; no global pool, no rayon."* Nothing here spawns
//! a thread at all: the serving tier stays serial by decision, concurrency comes
//! from serving many requests, and parallelising one clone at saturation is the
//! same work rearranged. What is emphatically **not** here is gix's pack
//! pipeline — no counting pass with a serial reduce, no `sort_by` over counts,
//! no `BTreeMap` reorder, no hashing of every byte on a consuming thread. That
//! is `P-018`'s serial tail, and the reason none of it is needed is that the
//! bytes on disk are already deflated and already delta-encoded, so emitting is
//! a copy and an addition.
//!
//! # Blocking
//!
//! Every method blocks. Never call one from an async task without
//! `tokio::task::spawn_blocking`.

use std::collections::{HashMap, HashSet};

use anyhow::{anyhow, bail, Context, Result};
use git_storage_trait::{
    Caps, GitOps, GitServe, ObjType, Oid, PackStats, ReachSet, RefRow, RefUpdate, TxId,
};
use gix_hash::ObjectId;

use crate::packs::{BaseRef, EmitParts, Pack, PackSet};
use crate::{refs, GixStore};

/// `HEAD`, which is **not** a row in [`GitOps::refs`] — see the accessor pair
/// below.
const HEAD: &str = "HEAD";

/// gix's decoded object kind, as the contract's type.
///
/// Total, and it cannot produce `OfsDelta` or `RefDelta` by construction:
/// `gix_object::Kind` has four variants and none of them is a delta, which is
/// exactly the *"resolved kind"* guarantee [`GitServe::read`] and
/// [`GitServe::header`] both make. Writing it as a match rather than as a cast
/// is what makes that a compiler-checked claim.
fn resolved_type(kind: gix_object::Kind) -> ObjType {
    match kind {
        gix_object::Kind::Commit => ObjType::Commit,
        gix_object::Kind::Tree => ObjType::Tree,
        gix_object::Kind::Blob => ObjType::Blob,
        gix_object::Kind::Tag => ObjType::Tag,
    }
}

// ── the emitter ──────────────────────────────────────────────────────────────

/// One entry the emitter has decided to write.
///
/// Borrows its pack's mmap for the length of one `emit_pack`, which is why the
/// pack-set read guard is taken once at the top and held: `parts.payload` is a
/// slice of a mapped `.pack`, and nothing between here and `out` copies it.
struct Planned<'a> {
    oid: ObjectId,
    body: Body<'a>,
    /// **What this entry names as its delta base, and where that base lives.**
    /// See [`DeltaBase`]. The base decides how an entry is *encoded*, never
    /// whether the object id is named by an offset — the offset would be the
    /// obvious key and it is the wrong one. An object can sit in two packs at
    /// once in this store *by construction*: `index_thin_pack` copies a push's
    /// delta bases into the new pack so that what lands on disk is
    /// self-contained. So the same object is reachable as `(pack A, offset x)`
    /// and as `(pack B, offset y)`, and keying on the site would emit it twice —
    /// a duplicate oid, which `git index-pack` refuses.
    ///
    /// Keying on the id is also *correct* rather than merely deduplicating: git
    /// resolves an `OFS_DELTA` by decoding whatever entry starts at
    /// `here - distance`, recursively, so any entry that decodes to the base
    /// object will do. It does not have to be the same representation the delta
    /// was originally written against.
    delta_base: DeltaBase,
}

/// Where the base a [`Planned`] delta names lives, which is what decides the
/// spelling [`GixStore::write_emission`] emits — a property of the *request*,
/// not of what happens to be on disk.
///
/// The clone path and the fetch path route through the same three cases, so
/// there is one copy-eligibility decision and not two that can drift (LAW 5):
/// the only fetch-specific input is `External`, and it is reached exactly when
/// the client vouched for the base.
enum DeltaBase {
    /// This entry names nothing: a non-delta, a rebuilt entry, or a stored
    /// `REF_DELTA` left **thin** because its base is not in this store and the
    /// client advertised `thin-pack`. The stored bytes go out header and all.
    None,
    /// The base travels **in this pack**, for either stored spelling. It is
    /// ordered before its delta and re-headed into whichever spelling the
    /// client can read (`OFS_DELTA` by position, or `REF_DELTA` by id).
    InPack(ObjectId),
    /// The base does **not** travel in this pack, but the client already holds
    /// it — it is reachable from the negotiated common tips, and `thin-pack`
    /// was advertised. The stored delta stream goes out verbatim under a
    /// `REF_DELTA` header naming the base by id; the client resolves it out of
    /// its own object database. This is the fetch-path twin of [`Self::InPack`]:
    /// a boundary delta the clone path would copy against an in-pack base is
    /// copied here against a base the receiver vouched for, instead of being
    /// rebuilt whole and paying deflate CPU for bytes the client already has.
    External(ObjectId),
}

/// The bytes one entry contributes, and it is one of exactly two things.
///
/// The distinction is the whole of the 2026-08-14 fix and it is the same rule
/// stock `pack-objects` follows: a stored delta is reused **only when its base
/// is also being packed**. The base decides how an entry is *encoded*; it never
/// decides what the pack *contains*.
enum Body<'a> {
    /// The stored entry, out of the mapped `.pack`. Its header may be re-encoded
    /// to name the base in the spelling this client reads; the deflate stream is
    /// never touched. This is the overwhelming majority of every request and the
    /// reason a clone costs no CPU.
    Copied(EmitParts<'a>),
    /// **Rebuilt whole**, because the stored entry is a delta against an object
    /// the request does not carry.
    ///
    /// Adding the base instead is what shipped broken clones: it satisfies the
    /// pack *format* and violates *connectivity*, because an out-of-set base
    /// that is a tree arrives owing children the pack does not contain, and
    /// `git index-pack --check-self-contained-and-connected` — which is what a
    /// clone runs — then dies with `did not receive expected object`. See
    /// [`GixStore::plan_emission`].
    ///
    /// `size` is the **resolved** object size, because this is no longer a delta
    /// entry and a non-delta header states the inflated length.
    Rebuilt {
        obj_type: ObjType,
        size: u64,
        deflated: Vec<u8>,
    },
    /// A [`Body::Rebuilt`] that [`GixStore::rebuild_bodies`] has not filled in
    /// yet, and which [`GixStore::write_emission`] **refuses**.
    ///
    /// It exists so that a fan-out which dropped a unit cannot be a quiet
    /// under-send. The alternative — an empty `Rebuilt` placeholder — is a
    /// zero-length deflate stream, which is a pack `git index-pack` rejects
    /// somewhere far away with a message about the wrong object, if it rejects
    /// it at all. This one names the entry and the stage.
    PendingRebuild,
}

impl Body<'_> {
    /// The placeholder [`GixStore::plan_emission`] parks in the plan while the
    /// rebuild is deferred to the fan-out.
    fn pending_rebuild() -> Self {
        Body::PendingRebuild
    }
}

/// Deferred rebuilds below which [`GixStore::rebuild_bodies`] does **not** fan
/// out.
///
/// **Eight, and the number is measured rather than picked.** MEASURED on oden
/// 2026-08-14, a whole-repository clone of the `nornir` mirror through this arm:
/// 12 216 objects asked for, of which **55 were rebuilds**, and `perf` put
/// `zlib_rs::deflate::*` at 28.5 % and `zlib_rs::inflate::*` at 7.7 % of the
/// request's ~100 ms of CPU. That is **~0.6 ms per rebuilt object** against a
/// `gatling` worker's ~10–20 µs to spawn: four spawns cost 3 % of *one* unit, so
/// the crossover is a handful of units and not a large batch.
///
/// This is deliberately nothing like the 4096 `znippy-plugin-git`'s emit phase 1
/// uses. Its unit is a bounds check and a varint and it needs thousands of them
/// to be worth a thread; this unit is an inflate, a delta-chain walk and a whole
/// deflate. **A threshold copied from there would have been wrong here** — 64
/// was the first value tried and this clone's 55 rebuilds fell under it, so the
/// fan-out never ran on the workload it was written for.
///
/// Note what it counts: **rebuilds**, not requested objects. A whole-repository
/// clone of a store with no unreachable history rebuilds nothing at all and
/// never reaches this test — which is why a threshold on `asked.len()` would
/// have been the wrong measurement of the same thing.
const FAN_OUT_AT: usize = 8;

/// Ceiling on rebuild workers **per request**.
///
/// The serving tier admits many concurrent transfers and the fan-out primitive
/// has no reentrancy detection, so an uncapped fan here is `admitted × ncores`
/// threads. Four leaves the admission gate in charge of the machine, and is the
/// same bound and the same argument as `znippy-plugin-git`'s emit phase 1.
const MAX_REBUILD_WORKERS: usize = 4;

/// Workers for the rebuild fan: [`MAX_REBUILD_WORKERS`], or `GIX_EMIT_WORKERS`.
///
/// **An operator knob, not an arm.** It selects no implementation and changes no
/// byte of the emitted pack — the fan writes each result back into the plan slot
/// it came from, and `write_emission` walks the plan in one serial order however
/// many workers filled it. `0` is read as "as many as this box has cores", which
/// is `gatling_for_each`'s own convention.
fn rebuild_workers() -> usize {
    match std::env::var("GIX_EMIT_WORKERS") {
        Ok(v) => v.trim().parse::<usize>().unwrap_or(MAX_REBUILD_WORKERS),
        Err(_) => MAX_REBUILD_WORKERS,
    }
}

/// Order `plan` so that every delta follows the base it names.
///
/// An `OFS_DELTA` names its base by a **backwards** distance, so a base that has
/// not been written yet has no distance to name: an order that puts a delta
/// first is not merely inefficient, it cannot be encoded at all.
///
/// A `REF_DELTA` names its base by id and imposes no order of its own — but a
/// stored `REF_DELTA` whose base is here is emitted as an `OFS_DELTA` whenever
/// the client can read one, so it is ordered on exactly the same rule. Ordering
/// it when the client turns out to lack `ofs-delta` costs nothing: a correct
/// order is still a correct order.
///
/// Iterative rather than recursive — a delta chain is allowed to be 50 deep by
/// default and nothing forbids a pathological one, so the depth belongs on the
/// heap where it cannot take the thread's stack with it. Returns the ordered
/// entries and any base that was named but is not in the set; a caller that gets
/// a non-empty second value has a bug rather than a thin pack, because a base
/// that is deliberately left external is recorded as `None` and never named
/// here.
fn topological_order(plan: Vec<Planned<'_>>) -> (Vec<Planned<'_>>, Vec<ObjectId>) {
    let present: HashMap<ObjectId, usize> =
        plan.iter().enumerate().map(|(i, p)| (p.oid, i)).collect();

    let mut missing = Vec::new();
    let mut done: HashSet<usize> = HashSet::with_capacity(plan.len());
    let mut order: Vec<usize> = Vec::with_capacity(plan.len());

    for start in 0..plan.len() {
        if done.contains(&start) {
            continue;
        }
        let mut stack = vec![start];
        let mut on_path: HashSet<usize> = HashSet::new();
        while let Some(&i) = stack.last() {
            if done.contains(&i) {
                stack.pop();
                continue;
            }
            let pending = match plan[i].delta_base {
                // Nothing to order against: a non-delta, a rebuilt entry, or a
                // thin entry whose base is external — [`DeltaBase::External`]
                // names a base this pack does not carry (the client resolves it),
                // so it is deliberately NOT a `missing` base and imposes no order.
                DeltaBase::None | DeltaBase::External(_) => None,
                DeltaBase::InPack(base) => match present.get(&base) {
                    Some(&b) if !done.contains(&b) => {
                        // A cycle is impossible in a well-formed pack — a base
                        // always precedes its delta, so offsets strictly
                        // decrease — but a corrupt one could claim otherwise,
                        // and looping for ever is a worse answer than emitting
                        // in an order the encoder then refuses.
                        if on_path.contains(&b) {
                            None
                        } else {
                            Some(b)
                        }
                    }
                    Some(_) => None,
                    None => {
                        missing.push(base);
                        None
                    }
                },
            };
            match pending {
                Some(b) => {
                    on_path.insert(i);
                    stack.push(b);
                }
                None => {
                    stack.pop();
                    on_path.remove(&i);
                    done.insert(i);
                    order.push(i);
                }
            }
        }
    }

    // Permute in place rather than cloning: `Planned` borrows the mmap, so a
    // clone would be cheap, but building the output by index keeps the move
    // obvious and does not need `Planned: Clone`.
    let mut slots: Vec<Option<Planned<'_>>> = plan.into_iter().map(Some).collect();
    let ordered = order.into_iter().filter_map(|i| slots[i].take()).collect();
    (ordered, missing)
}

/// The type/size varint — byte 0 is `[continue:1][type:3][size low nibble:4]`,
/// each continuation carries 7 more size bits, least significant group first.
///
/// Written out rather than borrowed from gix because gix's pack **writer** is
/// the pipeline this module exists to not use. It is checked against gix's own
/// reader in `tests/`: everything this encodes is read back by
/// `gix_pack::data::File::entry`.
fn encode_type_and_size(out: &mut Vec<u8>, obj_type: ObjType, size: u64) {
    let mut byte = (obj_type.code() << 4) | ((size & 0x0f) as u8);
    let mut rest = size >> 4;
    while rest > 0 {
        out.push(byte | 0x80);
        byte = (rest & 0x7f) as u8;
        rest >>= 7;
    }
    out.push(byte);
}

/// An `OFS_DELTA`'s backwards distance.
///
/// **Not the same varint as the one above**, and that is the whole reason it is
/// spelled out separately: each continuation subtracts one before shifting, so
/// the encoding has no redundant representations. Off by one here produces a
/// pack `git index-pack --strict` rejects — or worse, one it accepts and
/// misreads, because the delta would be applied against the wrong base. Emitted
/// most-significant group first, which is why it is built backwards.
fn encode_ofs_distance(out: &mut Vec<u8>, distance: u64) {
    let mut buf = [0u8; 10];
    let mut i = buf.len() - 1;
    let mut d = distance;
    buf[i] = (d & 0x7f) as u8;
    while d >= 0x80 {
        d >>= 7;
        d -= 1;
        i -= 1;
        buf[i] = 0x80 | (d & 0x7f) as u8;
    }
    out.extend_from_slice(&buf[i..]);
}

/// `out`, plus the running pack checksum and the byte count `OFS_DELTA`
/// distances are measured against.
///
/// The two facts a `Vec<u8>` accumulator would really be kept for, held as three
/// fields instead — so a clone **streams** and nothing sizes an allocation by
/// the repository. Serving a 2 GiB clone must not mean holding 2 GiB.
struct Trailing<'a> {
    inner: &'a mut dyn std::io::Write,
    hasher: gix_hash::Hasher,
    written: u64,
}

impl<'a> Trailing<'a> {
    fn new(inner: &'a mut dyn std::io::Write, hash: gix_hash::Kind) -> Self {
        Trailing {
            inner,
            hasher: gix_hash::hasher(hash),
            written: 0,
        }
    }

    /// Bytes written so far — the output coordinate an `OFS_DELTA` distance is
    /// relative to.
    fn written(&self) -> u64 {
        self.written
    }

    fn put(&mut self, bytes: &[u8]) -> Result<()> {
        self.inner.write_all(bytes).context("write pack bytes")?;
        self.hasher.update(bytes);
        self.written += bytes.len() as u64;
        Ok(())
    }

    /// The trailer is over everything written, which is what a reader checks.
    fn finish(self) -> Result<u64> {
        let id = self
            .hasher
            .try_finalize()
            .context("finalise the pack trailer")?;
        self.inner
            .write_all(id.as_slice())
            .context("write pack trailer")?;
        self.inner.flush().context("flush pack")?;
        Ok(self.written + id.as_slice().len() as u64)
    }
}

impl GixStore {
    /// **Emit a packfile whose objects are exactly `oids`**, deduplicated, and
    /// closed over nothing at all.
    ///
    /// Inherent as well as on the trait for the same reason the Arrow arm's
    /// `emit_set` is: a caller that [`GitServe::select`] told to *"walk it
    /// yourself"* hands the walked set straight back here, and it should not
    /// have to name a trait to do it.
    ///
    /// # Three steps
    ///
    /// 1. **Decide copy or rebuild, per entry**, on whether the base a stored
    ///    delta names is in the request. The set is never widened —
    ///    [`GixStore::plan_emission`] has the 🔴 note on what widening it cost.
    /// 2. **Order topologically**, because backwards distances require every base
    ///    to precede the delta naming it.
    /// 3. **Write.** A copied delta's *payload* goes out byte for byte and only
    ///    its **header** is re-encoded, to name its base in the spelling this
    ///    client can read — see [`GixStore::write_emission`].
    ///
    /// `copied + recompressed == objects`, counted rather than assumed, and
    /// `recompressed` is 0 for a whole-repository clone of a store with no
    /// unreachable history, because such a request contains every base.
    /// Re-heading a delta does not touch the deflate stream, so it costs bytes
    /// on the wire and no CPU at all.
    pub fn emit_oids(
        &self,
        oids: &[Oid<'_>],
        caps: &Caps,
        out: &mut dyn std::io::Write,
    ) -> Result<PackStats> {
        self.emit_oids_with(oids, caps, out, None)
    }

    /// [`GixStore::emit_oids`], with the rebuild fan-out's worker count **named**
    /// instead of decided by [`FAN_OUT_AT`] / [`rebuild_workers`].
    ///
    /// One body, not two (LAW 5): `emit_oids` is this with `None`, so the tested
    /// path and the shipped path are the same code. It exists because the
    /// production threshold is 64 rebuilds and the fixtures in this file rebuild
    /// one or two — a guard going through the front door would exercise the
    /// serial arm every time and the gatling arm never, which is a guard that
    /// cannot fail. `the_fan_out_rebuilds_the_identical_pack_the_serial_pass_does`
    /// calls it with `Some(1)` and `Some(4)` over one set and requires the two
    /// packs byte for byte.
    ///
    /// It takes a count rather than an environment variable on purpose: `cargo
    /// test` runs these in threads of one process, and a guard that has to set a
    /// process-global to choose its arm is a guard that races its neighbours.
    pub fn emit_oids_with(
        &self,
        oids: &[Oid<'_>],
        caps: &Caps,
        out: &mut dyn std::io::Write,
        workers: Option<usize>,
    ) -> Result<PackStats> {
        // No `held`: an inherent caller has no negotiated `have` closure to
        // vouch a boundary base with, so a cut delta chain rebuilds whole
        // exactly as a clone's does. `GitServe::emit_pack` is the one path that
        // has the closure and passes it — see [`GixStore::emit_core`].
        self.emit_core(oids, caps, out, workers, None)
    }

    /// The shared body of every emission (LAW 5): [`GixStore::emit_oids_with`]
    /// passes `held = None`; [`GitServe::emit_pack`] passes the closure of the
    /// client's negotiated `have` tips, so a boundary delta whose base the
    /// client already holds is copied verbatim as a thin `REF_DELTA` rather than
    /// rebuilt whole. The copy-versus-rebuild decision lives in one place —
    /// [`GixStore::plan_emission`] — so clone and fetch cannot disagree about
    /// what is copyable.
    fn emit_core(
        &self,
        oids: &[Oid<'_>],
        caps: &Caps,
        out: &mut dyn std::io::Write,
        workers: Option<usize>,
        held: Option<&HashSet<ObjectId>>,
    ) -> Result<PackStats> {
        // One guard for the whole emission. Every `payload` below is a slice of
        // a pack mapped under it, so it must outlive the last `write_all`, and a
        // `reload_packs` landing mid-clone must not swap the mmap out from under
        // a half-written pack.
        let packs = self
            .packs
            .read()
            .map_err(|_| anyhow!("pack set lock poisoned"))?;

        let plan = self.plan_emission(&packs, oids, caps, workers, held)?;
        let (ordered, missing) = topological_order(plan);
        if !missing.is_empty() {
            bail!(
                "the emit set is not closed: {} delta base(s) absent, first {} — refusing to \
                 emit a pack whose closure does not hold",
                missing.len(),
                missing[0]
            );
        }

        self.write_emission(&ordered, caps, out)
    }

    /// Step 1: turn `oids` into entries. **Nothing is added.**
    ///
    /// The set that comes out is the set that went in, deduplicated. Nothing
    /// walks a commit, a tree or a tag, there is no call into [`crate::graph`] on
    /// this path, and — since 2026-08-14 — a delta base outside the request is
    /// not pulled in either.
    ///
    /// # 🔴 It used to close over the delta bases, and that shipped broken clones
    ///
    /// See the module header for the mechanism and for the two bench arms it was
    /// measured on. In one line: an out-of-set base that happens to be a tree
    /// arrives owing children the pack does not contain, and the clone's own
    /// receiver refuses the pack by naming one of them.
    ///
    /// # What decides copy versus rebuild
    ///
    /// For each requested object, exactly one of:
    ///
    /// * **not a delta** → the stored entry goes out header and all;
    /// * **base inside the request** → the stored entry is copied and only its
    ///   *header* is re-encoded, into `OFS_DELTA` or `REF_DELTA` as the client
    ///   can read. Re-heading a stored `REF_DELTA` is not an optimisation:
    ///   `gix_pack::index::write` refuses *every* `REF_DELTA` outright — *"Ref
    ///   delta objects are not supported as there is no way to look them up"* —
    ///   whether or not the base is right there in the pack, so a clone that
    ///   copied `--fix-thin`'s ref-deltas verbatim served stock git and not
    ///   gitoxide;
    /// * **base outside the request, but in this store** → [`Body::Rebuilt`].
    ///   The object is inflated, resolved through its chain and deflated whole.
    ///   This is the only thing on the serving path that costs CPU, it fires
    ///   only where a request cuts a delta chain, and it is counted in
    ///   `recompressed` rather than being hidden;
    /// * **base outside this store entirely** → a genuinely thin entry. There is
    ///   nothing to rebuild *from*, so the client must supply the base: kept
    ///   verbatim when it advertised `thin-pack`, refused by name when it did
    ///   not.
    ///
    /// A rebuilt entry can itself be some other requested entry's base, which is
    /// why [`GixStore::write_emission`] records the output position of every
    /// entry and not only of the copied ones.
    fn plan_emission<'p>(
        &self,
        packs: &'p PackSet,
        oids: &[Oid<'_>],
        caps: &Caps,
        workers: Option<usize>,
        held: Option<&HashSet<ObjectId>>,
    ) -> Result<Vec<Planned<'p>>> {
        // The request, deduplicated, in the caller's order. A repeated oid must
        // not become a repeated entry — `git index-pack` refuses a duplicate —
        // and the order is kept because a stable, explicable one makes two runs
        // byte-identical, which is worth having for free. Nothing depends on it:
        // the emission is ordered by `topological_order`.
        let mut requested: HashSet<ObjectId> = HashSet::with_capacity(oids.len());
        let mut asked: Vec<ObjectId> = Vec::with_capacity(oids.len());
        for raw in oids {
            let id = self.oid(raw)?;
            if requested.insert(id) {
                asked.push(id);
            }
        }

        let mut plan: Vec<Planned<'p>> = Vec::with_capacity(asked.len());
        // Built on demand, once per pack that actually carries an `OFS_DELTA`
        // whose base has to be named. See [`Pack::offset_to_oid`].
        let mut reverse: HashMap<u64, HashMap<u64, ObjectId>> = HashMap::new();
        // `(index into plan, the entry, the base it names)` for every entry that
        // has to be rebuilt whole. Collected here and resolved by
        // [`GixStore::rebuild_bodies`] after this loop, because the rebuild is
        // the expensive half and it has no ordering dependency at all.
        let mut rebuilds: Vec<(usize, ObjectId, ObjectId)> = Vec::new();

        for id in asked {
            let (pack, offset) = self.locate_for_emission(packs, &id)?;
            let parts = pack.emit_parts_at(offset).ok_or_else(|| {
                anyhow!(
                    "the entry for {id} at offset {offset} of {} could not be read; its pack \
                     disagrees with its index and a verbatim copy of it would put a truncated \
                     deflate stream on the wire",
                    pack.data_path.display()
                )
            })?;

            // Which object this entry names as its base, in the one coordinate
            // system the decision below is taken in. An `OFS_DELTA` knows only
            // where its base sits, so the pack's own index gives it a name; a
            // `REF_DELTA` carries the name already.
            let named: Option<ObjectId> = match parts.base {
                BaseRef::None => None,
                BaseRef::Ofs(base_offset) => {
                    let map = reverse
                        .entry(pack.id)
                        .or_insert_with(|| pack.offset_to_oid());
                    Some(*map.get(&base_offset).ok_or_else(|| {
                        anyhow!(
                            "{id} is an ofs-delta naming a base at offset {base_offset} of {}, \
                             which is not an entry that pack's index knows about — the pack is \
                             corrupt",
                            pack.data_path.display()
                        )
                    })?)
                }
                BaseRef::Ref(base) => Some(base),
            };

            let (body, delta_base) = match named {
                // Not a delta: the stored bytes go out untouched.
                None => (Body::Copied(parts), DeltaBase::None),
                // The base travels with it, so it can be named by position.
                Some(base) if requested.contains(&base) => {
                    (Body::Copied(parts), DeltaBase::InPack(base))
                }
                // The base does not travel with it. This is the whole fix.
                Some(base) => {
                    // **The client already holds this base.** It is not in the
                    // request because the negotiation put it on the client's
                    // side of the cut, and `held` is the closure of the common
                    // tips — so this is exactly the fetch-path twin of the
                    // in-pack copy above: the stored delta stream goes out
                    // verbatim under a `REF_DELTA` naming the base by id, and
                    // the client resolves it out of its own object database. No
                    // inflate, no deflate. Whether this store *also* holds the
                    // base is irrelevant — thinness is a property of the emitted
                    // pack, not of the server — so this is decided before the
                    // `find_entry` rebuild path and rebuilds nothing the client
                    // could name for itself. Reached only with `caps.thin`,
                    // because `held` is only the true client set then (the
                    // caller withholds it for a shallow, filtered or
                    // no-common-history request, where closing over the tips
                    // would vouch for objects the receiver may never have seen).
                    if held.is_some_and(|h| h.contains(&base)) {
                        (Body::Copied(parts), DeltaBase::External(base))
                    } else if packs.find_entry(&base).is_some() {
                        // Here, but not asked for and not vouched for. Rebuilding
                        // the object is the only answer that is both encodable and
                        // connected; adding the base is what shipped broken clones.
                        //
                        // **Deferred, not done here** — see
                        // [`GixStore::rebuild_bodies`]. The rebuild is an
                        // inflate, a delta-chain walk and a whole-object
                        // deflate, and it is the only thing on this path that
                        // costs CPU; leaving it in this loop is what made a full
                        // clone serial. The placeholder carries the base so the
                        // fan-out can name it, and it is replaced before this
                        // function returns.
                        rebuilds.push((plan.len(), id, base));
                        (Body::pending_rebuild(), DeltaBase::None)
                    } else if caps.thin {
                        // Genuinely external — this store does not hold the base
                        // at all, so there is nothing to rebuild from and no
                        // position to name it by. The entry keeps its stored
                        // `REF_DELTA` header, base id and all, and the client
                        // resolves it out of its own object database.
                        (Body::Copied(parts), DeltaBase::None)
                    } else {
                        bail!(
                            "{id} is a ref-delta against {base}, which this store does not hold, \
                             and this client did not advertise thin-pack support; emitting it \
                             anyway would produce a pack the client cannot resolve"
                        );
                    }
                }
            };

            plan.push(Planned {
                oid: id,
                body,
                delta_base,
            });
        }
        self.rebuild_bodies(&mut plan, &rebuilds, workers)?;
        Ok(plan)
    }

    /// **Fill in every deferred [`Body::Rebuilt`], across `gatling` workers.**
    ///
    /// # Why this one and not the addressing pass beside it
    ///
    /// PROFILED on oden 2026-08-14, `bench/examples/stage-probe fetch` with
    /// `GUNNAR_ENGINE=gix` over the `nornir` mirror (12 216 objects, whole-repo
    /// clone, `perf record -F 9999`, 102 ms of process CPU):
    ///
    /// | what | share |
    /// |---|---|
    /// | `zlib_rs::deflate::*` | **28.5 %** |
    /// | `sha1_checked::compress` + `Sha1::update` (the pack trailer) | 24.1 % |
    /// | `zlib_rs::inflate::*` | 7.7 % |
    /// | `HashMap` lookups on `&oid` / `&usize` | 6.0 % |
    /// | `gix_pack::data::Entry::from_bytes` + `index::access::lookup` | **4.2 %** |
    ///
    /// The obvious target — the per-entry addressing that
    /// [`GixStore::locate_for_emission`] and `Pack::emit_parts_at` do, two
    /// binary searches over a mapped `.idx` — is that last row: **4.2 %**.
    /// Fanning it would have been a fan-out over a bounds check. The 36 % that
    /// is inflate + deflate is all *here*, in the entries whose stored delta
    /// names a base the request does not carry, and it was sitting in the middle
    /// of `plan_emission`'s serial `for id in asked` loop one object at a time.
    ///
    /// # Why it is safe to fan, stated as a property and not as an intention
    ///
    /// * The `.pack` mmaps are held open by the caller's `packs.read()` guard
    ///   for the whole emission, and nothing in this crate writes into a mapped
    ///   pack — `put_pack` writes a **new** file and reloads the set. So the
    ///   bytes every worker reads are immutable for the life of the call and
    ///   there is no lock here at all.
    /// * Each unit is one object: [`GixStore::resolved_for_rebuild`] (which goes
    ///   through the odb pool, itself already `Sync`) and a deflate of the
    ///   result. No unit reads another unit's output, and none of them touches
    ///   `plan` except the slot it owns.
    /// * The **output is positional**: `gatling_for_each` returns results in
    ///   index order, and each result is written back into the `plan` index it
    ///   came from. A pack emitted at one worker and at sixteen is therefore
    ///   byte-identical — the worker count decides nothing but who did the
    ///   deflate.
    ///
    /// # The two bounds, for the reason `znippy-plugin-git` states them
    ///
    /// The constellation's fan-out primitive has no reentrancy detection, and
    /// the serving tier admits many transfers at once, so an uncapped fan here
    /// is `admitted × ncores` threads. [`MAX_REBUILD_WORKERS`] caps it at four;
    /// [`FAN_OUT_AT`] means a request with only a handful of rebuilds runs on
    /// the calling thread exactly as it did before, because a thread spawn is
    /// ~10–20 µs and a request that rebuilds three objects should not pay four
    /// of them.
    fn rebuild_bodies(
        &self,
        plan: &mut [Planned<'_>],
        rebuilds: &[(usize, ObjectId, ObjectId)],
        workers: Option<usize>,
    ) -> Result<()> {
        // `Some(n)` is a caller naming the count — see
        // [`GixStore::emit_oids_with`]. `None` is the shipped policy: fan only
        // once the rebuilds are worth the spawns.
        let workers = workers.unwrap_or({
            if rebuilds.len() < FAN_OUT_AT {
                1
            } else {
                rebuild_workers()
            }
        });
        if rebuilds.is_empty() {
            return Ok(());
        }
        let one = |i: usize| -> Result<(usize, ObjType, u64, Vec<u8>)> {
            let (slot, id, base) = &rebuilds[i];
            let (obj_type, data) = self.resolved_for_rebuild(id, base)?;
            let size = data.len() as u64;
            Ok((*slot, obj_type, size, deflate(&data)?))
        };
        let done: Vec<(usize, ObjType, u64, Vec<u8>)> = if workers <= 1 {
            (0..rebuilds.len()).map(one).collect::<Result<Vec<_>>>()?
        } else {
            gatling::gatling_forkjoin::gatling_for_each(rebuilds.len(), workers, one)
                .into_iter()
                .collect::<Result<Vec<_>>>()?
        };
        for (slot, obj_type, size, deflated) in done {
            plan[slot].body = Body::Rebuilt {
                obj_type,
                size,
                deflated,
            };
        }
        Ok(())
    }

    /// The resolved object behind a boundary entry, for [`Body::Rebuilt`].
    ///
    /// [`GitServe::read`] is the one reader in this crate that walks a delta
    /// chain, so it is what this uses (LAW 5) rather than a second resolver. It
    /// goes through the odb pool's retry loop, which matters here: the base of a
    /// cut chain is exactly the kind of object that can sit in a pack written
    /// after this handle's snapshot.
    ///
    /// A `None` is impossible — the entry was located in a pack a moment ago —
    /// so it is reported as the store contradicting itself rather than turned
    /// into a skipped object, which would be the same silent under-send in a
    /// different hat.
    fn resolved_for_rebuild(&self, id: &ObjectId, base: &ObjectId) -> Result<(ObjType, Vec<u8>)> {
        GitServe::read(self, id.as_slice())
            .with_context(|| {
                format!(
                    "{id} is a delta against {base}, which this request does not carry, so it \
                     has to be rebuilt whole"
                )
            })?
            .ok_or_else(|| {
                anyhow!(
                    "{id} has a pack entry in this store and yet resolves to nothing; it cannot \
                     be rebuilt and its stored delta names {base}, which this request does not \
                     carry"
                )
            })
    }

    /// Find the entry to copy for `id`, or say precisely why there is none.
    ///
    /// The two failures are different and a caller can act on only one of them,
    /// so they are not collapsed into "not found":
    ///
    /// * **Loose.** A loose object has no pack entry, so there are no verbatim
    ///   bytes to copy. This backend refuses rather than falling back to a
    ///   whole-object writer, which would inflate and re-deflate — `recompressed`
    ///   would stop being 0 and the `P-001` receipt would stop meaning anything.
    ///   Nothing this crate writes is ever loose (`put_pack` writes packs), so
    ///   this is reachable only for a repository populated by something else.
    /// * **Absent.** The caller named an object that is not here at all, which is
    ///   a selection bug upstream and not something to paper over.
    fn locate_for_emission<'p>(
        &self,
        packs: &'p PackSet,
        id: &ObjectId,
    ) -> Result<(&'p Pack, u64)> {
        if let Some(found) = packs.find_entry(id) {
            return Ok(found);
        }
        use gix_object::Exists as _;
        if self.odb.with_handle(|odb| odb.exists(id)) {
            bail!(
                "{id} is a LOOSE object: it is in this repository but has no pack entry, so there \
                 are no stored bytes to copy. This backend emits packs by copying entries and \
                 refuses rather than inflating and re-deflating an object, which would make the \
                 `copied`/`recompressed` receipt a fiction"
            );
        }
        bail!("{id} was selected for emission and this store does not hold it")
    }

    /// Step 3: assemble the ordered entries into a packfile on `out`.
    ///
    /// # The one place that decides how a delta names its base
    ///
    /// A delta entry is `[header][deflated delta stream]`, and the two spellings
    /// differ **only in the header**: `OBJ_OFS_DELTA` carries a backwards
    /// distance, `OBJ_REF_DELTA` carries the base's raw object id. The stream is
    /// the same bytes either way, so re-heading one as the other is a header
    /// rewrite over an untouched payload — bytes on the wire, no CPU, and
    /// `recompressed` stays 0.
    ///
    /// Which one comes out is a property of the **request**, not of what is on
    /// disk. `ofs-delta` is a client capability; `OBJ_REF_DELTA` needs no
    /// capability at all and is the older of the two forms. So:
    ///
    /// * **`caps.ofs_delta`** — every delta whose base travels here goes out as
    ///   an `OFS_DELTA`, whichever way it was stored. Re-heading a stored
    ///   `REF_DELTA` is not an optimisation: `gix_pack::index::write` refuses
    ///   every `REF_DELTA` outright, base in the pack or not, so a pack that
    ///   copied them verbatim was servable to stock git and not to gitoxide.
    /// * **not `caps.ofs_delta`** — every such delta goes out as a `REF_DELTA`,
    ///   naming the base by id. The alternative is not "send an `OFS_DELTA`
    ///   anyway": it is either that (a pack the client cannot parse) or
    ///   inflating and re-deflating the whole clone, which was measured at
    ///   **18.4×** the wire bytes. Naming the base by id costs one hash width
    ///   per delta entry against a 1-3 byte varint, and nothing else.
    ///
    /// A `REF_DELTA` left deliberately **thin** — `delta_base` is `None` because
    /// this store does not hold the base and the client advertised `thin-pack` —
    /// has no position to be named by and keeps its stored header untouched.
    fn write_emission(
        &self,
        ordered: &[Planned<'_>],
        caps: &Caps,
        out: &mut dyn std::io::Write,
    ) -> Result<PackStats> {
        let count = u32::try_from(ordered.len()).map_err(|_| {
            anyhow!(
                "a pack holds at most u32::MAX entries, was given {}",
                ordered.len()
            )
        })?;

        let mut sink = Trailing::new(out, self.hash_kind);
        sink.put(b"PACK")?;
        sink.put(&2u32.to_be_bytes())?;
        sink.put(&count.to_be_bytes())?;

        // One scratch buffer for entry headers, reused. A header is a handful of
        // bytes and there is one per object, so allocating per entry would be
        // the allocation this whole path exists to not make.
        let mut hdr: Vec<u8> = Vec::with_capacity(32);
        // Where each object landed in the OUTPUT. Built as we go, which is
        // exactly why the order has to be topological.
        let mut placed: HashMap<ObjectId, u64> = HashMap::with_capacity(ordered.len());
        let mut copied = 0u64;
        let mut recompressed = 0u64;

        for p in ordered {
            let here = sink.written();
            // **A rebuilt entry names no base**, by construction in
            // `plan_emission`: it exists precisely because the base it used to
            // name is not in this pack. So it is written first and the
            // copy arm below never sees one.
            if let Body::Rebuilt {
                obj_type,
                size,
                deflated,
            } = &p.body
            {
                hdr.clear();
                // The RESOLVED size, unlike the delta arm below: a base-type
                // header states the inflated length of the object.
                encode_type_and_size(&mut hdr, *obj_type, *size);
                sink.put(&hdr)?;
                sink.put(deflated)?;
                // Recorded like any other entry: a rebuilt object can be the
                // base another requested entry names by position.
                placed.insert(p.oid, here);
                recompressed += 1;
                continue;
            }
            // **The one thing that can reach here and must not.** A
            // `PendingRebuild` means [`GixStore::rebuild_bodies`] did not fill a
            // slot it was given — a fan-out that dropped a unit — and the entry
            // it belongs to has no bytes at all. Refusing by name is the whole
            // reason the placeholder is its own variant rather than an empty
            // `Rebuilt`: an empty deflate stream would go out as a well-formed
            // pack entry that decodes to a zero-length object, and the failure
            // would surface as a client-side connectivity error naming some
            // other oid entirely.
            if matches!(p.body, Body::PendingRebuild) {
                bail!(
                    "{} was planned as a rebuilt entry and its bytes were never produced; the \
                     rebuild fan-out dropped a unit, and emitting the pack anyway would send an \
                     empty object under a real object id",
                    p.oid
                );
            }
            let Body::Copied(parts) = &p.body else {
                unreachable!("the rebuilt and pending arms returned above");
            };
            match p.delta_base {
                DeltaBase::InPack(base) => {
                    hdr.clear();
                    // **The size is the one the ENTRY states, not the resolved
                    // one.** For a delta they differ: the header carries the
                    // length of the delta stream and the index carries the size
                    // after the chain is applied. Re-encoding with the resolved
                    // size produces a stream `index-pack --strict` calls
                    // `inflate returned 1`. It is the same number in both
                    // spellings, which is precisely why one can become the other.
                    if caps.ofs_delta {
                        let base_at = *placed.get(&base).ok_or_else(|| {
                            anyhow!(
                                "{} deltas against {base}, which has not been written yet — the \
                                 set was not ordered by `topological_order`",
                                p.oid
                            )
                        })?;
                        encode_type_and_size(&mut hdr, ObjType::OfsDelta, parts.stated_size);
                        // Backwards distance in OUTPUT coordinates. `here` is
                        // always greater, because the base was written first.
                        encode_ofs_distance(&mut hdr, here - base_at);
                    } else {
                        encode_type_and_size(&mut hdr, ObjType::RefDelta, parts.stated_size);
                        // The raw hash, not hex: a pack entry names its base in
                        // `hash_kind.len_in_bytes()` binary bytes.
                        hdr.extend_from_slice(base.as_slice());
                    }
                    sink.put(&hdr)?;
                    sink.put(parts.payload)?;
                }
                // **A thin delta against a base the client holds.** The base is
                // not in this pack, so an `OFS_DELTA` has no position to name and
                // this goes out as a `REF_DELTA` by id whatever the client's
                // `ofs-delta` capability — the client resolves it out of its own
                // object database. Only the header is rewritten: `parts.payload`
                // is the stored delta stream, copied byte for byte, so this costs
                // no inflate/deflate and counts as `copied`. It re-heads a stored
                // `OFS_DELTA` (whose distance would now be meaningless) or a
                // stored `REF_DELTA` alike, since both carry the same delta
                // stream and differ only in the header.
                DeltaBase::External(base) => {
                    hdr.clear();
                    encode_type_and_size(&mut hdr, ObjType::RefDelta, parts.stated_size);
                    hdr.extend_from_slice(base.as_slice());
                    sink.put(&hdr)?;
                    sink.put(parts.payload)?;
                }
                // A base type carries no back-reference at all, and a `REF_DELTA`
                // left thin names a base this pack does not contain — neither has
                // anything to re-point at, so the stored bytes go out header and
                // all, untouched. That is stronger than re-encoding: it cannot
                // disagree with what the pack actually holds, including a
                // non-canonical size varint git accepts and would not rewrite.
                DeltaBase::None => sink.put(parts.stored)?,
            }
            placed.insert(p.oid, here);
            copied += 1;
        }

        Ok(PackStats {
            bytes: sink.finish()?,
            objects: u64::from(count),
            // Both counted off what happened rather than asserted: a byte count
            // and a wall clock cannot tell a pack-copy from a re-deflate, and
            // both produce a pack that indexes clean. `recompressed` used to be
            // the literal `0`, which stopped being true the moment a boundary
            // entry could be rebuilt — and a receipt that is a literal is not a
            // receipt.
            copied,
            recompressed,
        })
    }
}

/// zlib-deflate one whole object, for [`Body::Rebuilt`].
///
/// `Compression::default()` is level 6, which is zlib's default and therefore
/// git's — `core.compression` is `-1` out of the box and `pack.compression`
/// inherits it — so a rebuilt entry is the size stock `pack-objects` would have
/// made it.
fn deflate(data: &[u8]) -> Result<Vec<u8>> {
    use std::io::Write as _;
    let mut encoder = flate2::write::ZlibEncoder::new(
        // Deflated output is smaller than its input for anything git bothers to
        // store, so this is a ceiling that is never grown past in practice and
        // never over-reserves by much when it is wrong.
        Vec::with_capacity(data.len() / 2 + 64),
        flate2::Compression::default(),
    );
    encoder
        .write_all(data)
        .context("deflate an object being rebuilt for the wire")?;
    encoder
        .finish()
        .context("finish deflating an object being rebuilt for the wire")
}

impl GitServe for GixStore {
    /// **The object, inflated and delta-resolved** — what [`GitOps::get`]
    /// deliberately is not.
    ///
    /// Through the pool's retry loop rather than a bare handle: an error is not
    /// a hit, and `gix-odb` reports an unresolvable ref-delta base as
    /// `DeltaBaseMissing` rather than as `Ok(None)`. A base sitting in a pack
    /// written after this handle's snapshot is precisely what the retry is for,
    /// and a stale `None` on this path is a refused fetch.
    ///
    /// # Exactly one allocation, and it is given away rather than copied
    ///
    /// The contract owns its `Vec<u8>`. `gix_object::Find::try_find` inflates
    /// into a caller-supplied buffer and returns `Data<'a>` borrowed from **that
    /// buffer** — the signature says so, since `'a` comes only from the buffer —
    /// so the buffer *is* the answer and handing it over costs nothing. A
    /// `to_vec()` here would be `P-025` in miniature: one heap allocation and one
    /// memcpy per object read, on the path `gunnar-policy` walks a whole ancestry
    /// with.
    ///
    /// The one thing that is checked rather than assumed is that the slice
    /// covers the whole buffer. It cannot fail today — gix builds the `Data` as
    /// `buffer.as_slice()` — and if a future gix returned a sub-slice this would
    /// say so instead of silently truncating an object to the wrong bytes.
    fn read(&self, oid: Oid<'_>) -> Result<Option<(ObjType, Vec<u8>)>> {
        let id = self.oid(oid)?;
        self.odb
            .lookup(
                |odb| {
                    use gix_object::Find as _;
                    let mut buf = Vec::new();
                    // The borrow of `buf` must end before `buf` can be moved
                    // out, so nothing survives the call but two `Copy` facts.
                    let Some((kind, len)) = odb
                        .try_find(&id, &mut buf)?
                        .map(|data| (data.kind, data.data.len()))
                    else {
                        return Ok(None);
                    };
                    if buf.len() != len {
                        return Err(gix_error::ErrorExt::raise(gix_error::Message::new(format!(
                            "gix answered {id} with {len} bytes out of a {} byte buffer; this \
                             backend hands the buffer over rather than copying it, which is only \
                             sound while the two are the same",
                            buf.len()
                        ))));
                    }
                    Ok(Some((resolved_type(kind), buf)))
                },
                |r: &gix_error::Result<_>| matches!(r, Ok(Some(_))),
            )
            .map_err(|e| anyhow!("read {id}: {e}"))
    }

    /// **Kind and post-resolution size, without the payload.** The type probe.
    ///
    /// `try_header` walks entry headers only and never inflates a payload it
    /// does not need, which is what makes this cheaper than [`GitServe::read`]
    /// rather than merely narrower. The kind it reports is the **resolved** one,
    /// so it is never a delta — see [`resolved_type`].
    fn header(&self, oid: Oid<'_>) -> Result<Option<(ObjType, u64)>> {
        let id = self.oid(oid)?;
        // The inherent `GixStore::header` — the trait method of the same name is
        // this one, and inherent methods win name resolution, so the call is
        // spelled explicitly to say which was meant.
        Ok(GixStore::header(self, &id)?.map(|h| (resolved_type(h.kind()), h.size())))
    }

    /// Bulk [`GitServe::header`], positionally.
    ///
    /// **This arm has no bulk path and loops**, which the contract asks a backend
    /// without one to say in its own docs. The Arrow arm answers `sizes` as a
    /// single index pass, and that is what collapses a measured 34 124
    /// per-object header walks on one clone; here there is no such column, and a
    /// pack index does not carry post-resolution sizes at all — a delta's real
    /// size is only knowable by walking its chain. Inventing a bulk shape over
    /// the same per-object walk would hide that difference from the head-to-head
    /// this crate exists for.
    ///
    /// `None` at a position means **unknown — go ask `header`**, never "fine". A
    /// caller treating an unknown size as under a ceiling would skip the ceiling
    /// for exactly the objects a push had just introduced.
    fn sizes(&self, oids: &[Oid<'_>]) -> Result<Vec<Option<u64>>> {
        let mut out = Vec::with_capacity(oids.len());
        for raw in oids {
            // Positional: a miss occupies its slot. Filtering here would shift
            // every later answer by one and still return a plausible count.
            out.push(GitServe::header(self, raw)?.map(|(_, size)| size));
        }
        Ok(out)
    }

    /// `HEAD`.
    ///
    /// **`HEAD` is not a row in [`GitOps::refs`]** — `all()` walks `refs/` and
    /// *"deliberately excludes the pseudo-refs such as `HEAD`, which is exactly
    /// the contract `RefStore` states and every other backend honours"*. The
    /// reason is a type constraint rather than taste: a name type that admits
    /// `HEAD` also admits `MERGE_HEAD` and `FETCH_HEAD`, so putting pseudo-refs
    /// in the row stream means either widening the name type or filtering at
    /// every consumer. This is half of the accessor pair it gets instead.
    ///
    /// Reported **as stored**, which is the same thing [`GitOps::refs`] does for
    /// every other row: a symbolic `HEAD` answers `symref_target` and no `oid`.
    /// Resolving it here would make this row the one row in the namespace shaped
    /// differently from its neighbours, and the two arms have to agree.
    ///
    /// `None` for a repository that has never pointed one.
    fn head(&self) -> Result<Option<RefRow>> {
        let found = self
            .refs
            .try_find(HEAD)
            .with_context(|| "read HEAD".to_string())?;
        Ok(found.map(|r| refs::row(HEAD.to_string(), &r.target, |id| self.peel(id))))
    }

    /// Point `HEAD`. The other half of the pair; `point_head_at` is the live
    /// consumer.
    ///
    /// # The two shapes of a target, and how they are told apart
    ///
    /// The contract's parameter is one `&str` for both, so — matching the Arrow
    /// arm exactly, because two engines that disagree about what `HEAD` accepts
    /// is the class of defect a conformance harness exists for:
    ///
    /// * a target starting with `refs/` is a **symbolic** ref, written as one;
    /// * anything else must be a hex oid of exactly this store's width, written
    ///   as a direct target.
    ///
    /// The two cannot be confused: a ref name and a 40- or 64-character hex
    /// string are disjoint. Anything that is neither is **refused** rather than
    /// guessed at — a `HEAD` pointing at a name nothing resolves is an unclonable
    /// repository, and it is cheaper to say so here.
    ///
    /// A symbolic target is deliberately **not** checked for existence.
    /// `git init` points `HEAD` at `refs/heads/main` before that branch exists,
    /// and refusing it would make an empty repository unrepresentable.
    fn set_head(&self, target: &str) -> Result<TxId> {
        let update = if target.starts_with("refs/") {
            RefUpdate::symbolic(HEAD, target)
        } else {
            let hex_len = self.hash_kind.len_in_bytes() * 2;
            let is_hex = target.len() == hex_len && target.bytes().all(|b| b.is_ascii_hexdigit());
            if !is_hex {
                bail!(
                    "HEAD can be pointed at a ref name under `refs/` or at a {hex_len}-character \
                     hex oid; `{target}` is neither, and guessing which was meant is how a \
                     repository ends up unclonable"
                );
            }
            RefUpdate::set(HEAD, target.to_ascii_lowercase())
        };
        self.put_refs(&[update])
    }

    /// **Emit a packfile containing exactly `objects`**, and nothing else at all.
    ///
    /// One line of delegation to [`GixStore::emit_oids`], and the absence of a
    /// second line is the contract: **this must not compute a closure.** The
    /// caller is upload-pack holding a selection that is already post-filter,
    /// post-shallow, post-`include-tag` and deliberately not closed. An engine
    /// that treats it as tips serves a `--filter=blob:none` fetch exactly the
    /// objects it asked to be left out, and nothing we own can see it: the
    /// over-sent pack passes `index-pack --strict` *and* `fsck`, the clone
    /// succeeds and the exit code is zero.
    ///
    /// # `have` is thin-pack base selection, and never membership
    ///
    /// `have` is the negotiated common **tips**, *"used for thin-pack base
    /// selection, never to derive membership"*. This arm now takes that
    /// optimisation, matching the Arrow arm and stock `pack-objects --thin`: a
    /// boundary entry whose stored delta names a base the request does not carry
    /// but the **client already holds** stays a byte copy, emitted as a thin
    /// `REF_DELTA` naming that base, instead of being rebuilt whole. On a
    /// repacked server every requested object has a pack entry, so before this
    /// the boundary of a divergent fetch — the deltas whose base sits on the
    /// client's side of the negotiation cut — was the entire recompress count,
    /// paying inflate+deflate for bytes the receiver already has.
    ///
    /// The closure of these tips is what the client holds, and closing over them
    /// is safe *exactly* when `caps.thin` is set: the caller advertises thin
    /// only for a non-shallow, unfiltered fetch with common history, the
    /// conditions under which a `have` tip's closure is truly held (a shallow or
    /// `blob:none` client reaches objects through those tips that it was
    /// deliberately never sent). So the closure is computed only under
    /// `caps.thin`, and [`GixStore::plan_emission`] vouches each individual base
    /// against it — a base outside the closure is still rebuilt whole.
    ///
    /// `have` is deliberately **not** repurposed into an exclusion set. Deriving
    /// membership from it is the opposite mistake: it would *remove* objects the
    /// caller's finished selection had decided to send, an under-send, the
    /// failure that exits zero. It informs only *how* a boundary delta is
    /// encoded, never *whether* an object is emitted.
    fn emit_pack(
        &self,
        objects: &[Oid<'_>],
        have: &[Oid<'_>],
        caps: &Caps,
        out: &mut dyn std::io::Write,
    ) -> Result<PackStats> {
        // The closure of the client's `have` tips — everything it held before
        // this transfer — computed once and vouched against per boundary base in
        // `plan_emission`. Only under `caps.thin`, both because that is the one
        // request shape where the closure is what the client truly holds and
        // because it is the client's consent to receive a thin pack at all.
        let held = if caps.thin && !have.is_empty() {
            let have_ids: Vec<ObjectId> =
                have.iter().map(|raw| self.oid(raw)).collect::<Result<_>>()?;
            Some(self.odb.with_handle(|odb| {
                crate::graph::have_closure(odb, self.hash_kind, &have_ids)
            })?)
        } else {
            None
        };
        self.emit_core(objects, caps, out, None, held.as_ref())
    }

    /// Negotiation: `want` minus `have`, with the two derived facts a caller
    /// cannot recompute cheaply.
    ///
    /// # The bitmap first, the walk always
    ///
    /// This used to say *"a graph **walk**, because gitoxide has no bitmap
    /// support of any kind"*, and the premise is still true — gitoxide has none.
    /// edda's does: [`crate::bitmap::select`] answers from the `.bitmap`
    /// [`GitOps::gc`] wrote, in one `OR` per want and one `AND NOT`, reading no
    /// object at all when every want is bitmapped.
    ///
    /// **The walk is not a legacy path.** A bitmap covers one pack, so anything
    /// pushed since the last `gc` is reached through a bounded walk, and a
    /// request the bitmap cannot answer exactly — no `.bitmap`, a damaged one,
    /// one describing a different pack, a `want` that is not in it — falls all
    /// the way back here. The two are held to the same **object set** by
    /// `tests/bitmap_and_walk_agree.rs`; that equality is the whole safety
    /// argument, and it is why the fallback is a `match` and not a `?`.
    ///
    /// Which one ran is invisible in the answer by construction, so it is
    /// counted: [`crate::GixStore::bitmap_selection_counts`].
    ///
    /// # `Ok(None)` is load-bearing, and this arm reaches it
    ///
    /// [`GitOps::reachable`] treats a `want` it cannot find as contributing
    /// **itself and nothing else** — safe for `live_set`, which over-keeps, and a
    /// silent **under-send** here: a clone served that way exits zero with one
    /// object per branch. So every tip is checked first, and a tip this store
    /// does not hold declines the whole request rather than answering it badly.
    /// That is the same statement the Arrow arm makes when its reachability
    /// projection cannot cover a request, reached by a different route.
    ///
    /// Do not "improve" this into always answering. An engine that always
    /// answers has to answer wrongly somewhere, and this is the shape of wrong
    /// that no exit code and no `fsck` can see.
    fn select(&self, want: &[Oid<'_>], have: &[Oid<'_>]) -> Result<Option<ReachSet>> {
        let to_ids = |s: &[Oid<'_>]| -> Result<Vec<ObjectId>> {
            s.iter().map(|raw| self.oid(raw)).collect()
        };
        let want_ids = to_ids(want)?;
        for id in &want_ids {
            if !self.has(id.as_slice())? {
                return Ok(None);
            }
        }
        // A `have` this store does not hold is NOT a refusal: it is the client
        // telling you about its own commits, which is the ordinary case in a
        // negotiation and the reason `has` deliberately does not buy a `statx`
        // for it. The walk skips what it cannot find, which for the stop set
        // means "stop at nothing", and over-sending is the safe direction here.
        let have_ids = to_ids(have)?;

        let sel = self.selection(&want_ids, &have_ids)?;
        let raw = |v: Vec<ObjectId>| v.into_iter().map(|id| id.as_slice().to_vec()).collect();
        Ok(Some(ReachSet {
            objects: raw(sel.objects),
            commits: raw(sel.commits),
            // **The voucher goes over flat**, not as a `Vec` per oid. It is the
            // one field of the three that is repository-sized on an ordinary
            // fetch — the closure of what the client already held — and a
            // `Vec<u8>` per entry is a heap allocation this engine pays for
            // nothing, exactly as the znippy engine did on the other side of the
            // same contract. See `git_storage_trait::OidList`.
            client_has: sel.client_has.iter().map(ObjectId::as_slice).collect(),
        }))
    }
}

impl GixStore {
    /// `want` minus `have`: the bitmap if it can answer exactly, the walk
    /// otherwise.
    ///
    /// **The single seam.** Both arms return the same
    /// [`crate::graph::Selection`], so nothing downstream can tell them apart —
    /// which is the point (an engine whose two reachability paths disagree
    /// serves a pack that indexes, `fsck`s and applies, and leaves the client's
    /// working tree wrong). Routing every selection through one function is what
    /// makes `tests/bitmap_and_walk_agree.rs` a statement about the shipping
    /// code rather than about two functions a test called by hand.
    ///
    /// Exposed on the store so a test can drive it directly and assert which arm
    /// answered; `GitServe::select` is one line of translation over it.
    pub fn selection(
        &self,
        want: &[ObjectId],
        have: &[ObjectId],
    ) -> Result<crate::graph::Selection> {
        use std::sync::atomic::Ordering;

        if let Some(bm) = self.bitmaps.get(&self.pack_dir(), self.hash_kind) {
            let answered = self
                .odb
                .with_handle(|odb| crate::bitmap::select(&bm, odb, self.hash_kind, want, have))?;
            if let Some((sel, cost)) = answered {
                crate::BITMAP_SELECTIONS.fetch_add(1, Ordering::Relaxed);
                crate::BITMAP_COMMITS_WALKED
                    .fetch_add(cost.commits_walked as u64, Ordering::Relaxed);
                return Ok(sel);
            }
        }
        crate::WALK_SELECTIONS.fetch_add(1, Ordering::Relaxed);
        self.odb
            .with_handle(|odb| crate::graph::select(odb, self.hash_kind, want, have))
    }
}

/// A compile-time assertion that the reading contract is implemented too, so a
/// signature drifting from the frozen `GitServe` is a build failure here rather
/// than a surprise at a call site. The `GitOps` twin is in `lib.rs`.
const _: () = {
    fn assert_impl<T: GitServe>() {}
    fn check() {
        assert_impl::<GixStore>();
    }
    let _ = check;
};

#[cfg(test)]
mod tests {
    use super::*;

    /// **Both encoders are checked against gix's own decoder**, over the
    /// boundaries the varints actually turn on rather than a handful of small
    /// numbers.
    ///
    /// This is the one place in the crate that writes pack bytes by hand, so it
    /// is the one place a silent format bug can start. `git index-pack --strict`
    /// in `tests/serving_the_wire.rs` catches it end to end; this catches it at
    /// the boundary and says which of the two varints was wrong. The oracle is
    /// `gix_pack::data::Entry::from_bytes`, which is the same reader
    /// [`crate::packs`] uses, so an encoder and a decoder that agree with each
    /// other but not with git cannot both pass.
    #[test]
    fn the_entry_encoders_are_read_back_by_gix_itself() {
        // The size varint turns at 4 bits and then every 7: 0x0f/0x10 is the
        // first continuation, 0x7ff/0x800 the second, and so on.
        let sizes = [
            0u64,
            1,
            15,
            16,
            127,
            128,
            2047,
            2048,
            1 << 20,
            u32::MAX as u64,
        ];
        for &size in &sizes {
            for kind in [ObjType::Commit, ObjType::Tree, ObjType::Blob, ObjType::Tag] {
                let mut buf = Vec::new();
                encode_type_and_size(&mut buf, kind, size);
                // A payload the decoder will not read, so a header that claims
                // more bytes than it wrote is visible as a decode failure.
                buf.extend_from_slice(&[0u8; 8]);
                let e = gix_pack::data::Entry::from_bytes(&buf, 0, gix_hash::Kind::Sha1)
                    .expect("gix must read back what we wrote");
                assert_eq!(e.decompressed_size, size, "size round trip for {kind:?}");
                assert_eq!(e.header.as_type_id(), kind.code(), "type round trip");
            }
        }

        // The distance varint is the *other* one: each continuation subtracts
        // one before shifting, so it has no redundant representations, and off
        // by one here produces a pack that resolves a delta against the wrong
        // base.
        let distances = [1u64, 2, 127, 128, 129, 16511, 16512, 1 << 20, 1 << 40];
        for &d in &distances {
            let mut buf = Vec::new();
            encode_type_and_size(&mut buf, ObjType::OfsDelta, 42);
            encode_ofs_distance(&mut buf, d);
            buf.extend_from_slice(&[0u8; 8]);
            // `pack_offset` has to exceed the distance or the entry names a
            // base before the start of the pack.
            let e = gix_pack::data::Entry::from_bytes(&buf, d + 12, gix_hash::Kind::Sha1)
                .expect("gix must read back the distance we wrote");
            assert_eq!(e.decompressed_size, 42);
            match e.header {
                gix_pack::data::entry::Header::OfsDelta { base_distance } => {
                    assert_eq!(base_distance, d, "distance round trip")
                }
                other => panic!("expected an ofs-delta, got {other:?}"),
            }
        }
    }
}