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:
| format | client | server |
|---|---|---|
capabilities | parses the remote’s, writes its own | writes its own, parses the client’s |
advertisement | advertisement::parse | advertisement::lines |
command | command::lines | command::parse_line |
report | report::parse | report::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-packsends 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_packdriver 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-statusandreport-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.