pub struct PushFacts<'a> {
pub objects: &'a dyn GitServe,
pub new_objects: Result<&'a [ObjectId], &'a str>,
pub pack_bytes: Option<u64>,
pub push_options: &'a [String],
}Expand description
Everything receive-pack learned about the push, handed to every gate.
§Why this is a struct and not three arguments
The gate seam used to be (&RefCommand, &dyn ObjectStore) and nothing else,
so a gate could not see which objects the push introduced or how many bytes
the client sent. gunnar-policy’s size and quota gates
(gunnar_policy::gates::limits) are the things standing between a forge and
a client pushing until the disk fills, and neither could be enforced: the
object set was recovered downstream by reading the written .idx back,
which counts a thin pack’s resolved bases as if the client had sent them,
and the byte count was never produced at all.
A struct rather than a wider tuple so the next fact does not re-break
every implementation of PushGate.
Fields§
§objects: &'a dyn GitServeThe store, after the pack has been taken in.
GitServe: a gate reads objects, and reading means inflated and
delta-resolved. gunnar-policy’s ancestry walk and its signature gate
were two of the three errors that crate was blocked on, and they are the
same missing capability the connectivity walk here needs.
new_objects: Result<&'a [ObjectId], &'a str>The objects this push genuinely introduced, or the reason they could not be enumerated.
Err is not “none”: a gate that cannot see what was pushed cannot
decide, and an undecidable gate refuses. Handing an empty slice for
“I do not know” is the shape that lets a configured limit be silently
satisfied, which is what this type exists to prevent.
pack_bytes: Option<u64>Packfile bytes the client sent, counted off the wire by
ReceivePack::serve rather than measured from what landed on disk.
Some(0) for a deletions-only push, which carries no pack: that is a
measured zero, not an absent measurement, and a byte limit must not
refuse it. None only if the count is genuinely unavailable.
push_options: &'a [String]The push-option lines the client sent, in wire order.
§Why this field exists
push-options was advertised by
caps::receive_pack_capabilities,
parsed by ReceivePack::serve and then thrown away: the only
consumer in the whole tree was .len() in one log line. A client that
sent -o ci.skip got the capability acknowledged on the wire and the
option silently ignored, which is a wrong answer on the wire rather than
a missing feature — the gate could not see them, so no policy could act
on one even in principle.
Stock git hands push options to the pre-receive hook. gunnar has no
hooks and no shell; PushGate is what replaces them, so
this is where they have to arrive.
Empty means the client sent none, which is also what a client that never negotiated the capability produces. The two are not told apart here on purpose: a gate that requires an option must refuse both, and a gate that forbids unknown ones must accept both.