Skip to main content

Crate gunnar_sendpack

Crate gunnar_sendpack 

Source
Expand description

send-pack, and the receive-pack grammar under it — the half of git that gitoxide does not have.

Verified in gix 0.86: gix-protocol/src/ carries fetch/, ls_refs.rs and handshake/, and nothing for push. Direction::Push exists only as a refspec direction. So a git client written on gix has to put this on the wire itself, and a git server written on gix has to put the other side of it on the wire itself, and until this crate existed those two were written twice — once per direction, in two crates, in two different styles, agreeing only by inspection.

§One grammar, both directions

That is the load-bearing idea, and it is why this crate is not called send-pack alone. Four formats make up the v0 push conversation:

formatclientserver
capabilitiesparses the remote’s, writes its ownwrites its own, parses the client’s
advertisementadvertisement::parseadvertisement::lines
commandcommand::linescommand::parse_line
reportreport::parsereport::lines

Each row is one implementation with two entry points, so an encoder and its decoder cannot drift: there is nothing for them to drift from. A test that round-trips one row exercises both ends of a real conversation rather than one end against a fixture written by the same hand.

§Payloads, not packets

Every lines function returns payloads without pkt-line framing, without a terminating flush-pkt, and without a trailing newline, and every parser takes them the same way. git frames these sections as text pkt-lines, <4-hex-length><payload>\n, and the newline belongs to the framer.

That split is what lets a server that already owns a pkt-line framer and a socket use this crate without paying for a second framing layer: with default-features = false the whole crate is gix-hash + bstr + thiserror. The blocking-io feature — on by default, because a client is the majority consumer — adds Transport, the pkt-line reader and send_pack, framed with gix_packetline.

§Example: what a client does

use gunnar_sendpack::{read_advertisement, send_pack, IoPair, PushCommand, SendPackOptions};

// Two pipes to a remote `git-receive-pack`.
let mut transport = IoPair::new(stdout, stdin);
let (reader, _) = { use gunnar_sendpack::Transport as _; transport.io() };
let adv = read_advertisement(reader)?;

let tip = adv.oid_of("refs/heads/main");
let commands = vec![PushCommand::update("refs/heads/main", tip, new_tip)];
let report = send_pack(
    &mut transport,
    &adv,
    &commands,
    adv.hash_kind,
    Some(|w: &mut dyn std::io::Write| Ok(w.write_all(b"PACK...")?)),
    &SendPackOptions::default(),
)?;
assert!(report.is_ok(), "{:?}", report.failure_summary());

§Licence

MIT OR Apache-2.0, to match gitoxide’s.

Re-exports§

pub use advertisement::Advertisement;
pub use advertisement::RemoteRef;
pub use capabilities::Capabilities;
pub use command::PushCommand;
pub use error::Error;
pub use error::Result;
pub use options::SendPackOptions;
pub use report::PushReport;
pub use report::RefStatus;
pub use driver::read_advertisement;
pub use driver::read_report;
pub use driver::send_pack;
pub use transport::IoPair;
pub use transport::Transport;

Modules§

advertisement
The reference advertisement git-receive-pack sends before the client speaks — parsed for the client, rendered for the server.
capabilities
The capability list, which both ends parse and both ends write.
command
The command list: <old-oid> <new-oid> <refname>, written by the client and read by the server.
driver
The client half: pkt-line framing over a blocking stream, and the send_pack driver that runs one whole push.
error
One error type for the whole conversation.
options
What the client asks for, and what it does when the remote will not give it.
report
report-status and report-status-v2: written by the server, read by the client.
transport
The seam every wire conversation in this crate runs over: a blocking reader and a blocking writer.