Skip to main content

gunnar_sendpack/
lib.rs

1//! **send-pack**, and the receive-pack grammar under it — the half of git that
2//! gitoxide does not have.
3//!
4//! Verified in gix 0.86: `gix-protocol/src/` carries `fetch/`, `ls_refs.rs` and
5//! `handshake/`, and **nothing for push**. `Direction::Push` exists only as a
6//! refspec direction. So a git client written on gix has to put this on the
7//! wire itself, and a git *server* written on gix has to put the other side of
8//! it on the wire itself, and until this crate existed those two were written
9//! twice — once per direction, in two crates, in two different styles, agreeing
10//! only by inspection.
11//!
12//! # One grammar, both directions
13//!
14//! That is the load-bearing idea, and it is why this crate is not called
15//! `send-pack` alone. Four formats make up the v0 push conversation:
16//!
17//! | format | client | server |
18//! |---|---|---|
19//! | [`capabilities`] | parses the remote's, writes its own | writes its own, parses the client's |
20//! | [`advertisement`] | [`advertisement::parse`] | [`advertisement::lines`] |
21//! | [`command`] | [`command::lines`] | [`command::parse_line`] |
22//! | [`report`] | [`report::parse`] | [`report::lines`] |
23//!
24//! Each row is **one** implementation with two entry points, so an encoder and
25//! its decoder cannot drift: there is nothing for them to drift from. A test
26//! that round-trips one row exercises both ends of a real conversation rather
27//! than one end against a fixture written by the same hand.
28//!
29//! # Payloads, not packets
30//!
31//! Every `lines` function returns payloads **without pkt-line framing, without
32//! a terminating flush-pkt, and without a trailing newline**, and every parser
33//! takes them the same way. git frames these sections as *text* pkt-lines,
34//! `<4-hex-length><payload>\n`, and the newline belongs to the framer.
35//!
36//! That split is what lets a server that already owns a pkt-line framer and a
37//! socket use this crate without paying for a second framing layer: with
38//! `default-features = false` the whole crate is `gix-hash` + `bstr` +
39//! `thiserror`. The `blocking-io` feature — on by default, because a client is
40//! the majority consumer — adds [`Transport`], the pkt-line reader and
41//! [`send_pack`], framed with [`gix_packetline`].
42//!
43//! # Example: what a client does
44//!
45//! ```no_run
46//! # fn main() -> Result<(), gunnar_sendpack::Error> {
47//! use gunnar_sendpack::{read_advertisement, send_pack, IoPair, PushCommand, SendPackOptions};
48//!
49//! // Two pipes to a remote `git-receive-pack`.
50//! # let (stdout, stdin): (std::fs::File, std::fs::File) = unimplemented!();
51//! let mut transport = IoPair::new(stdout, stdin);
52//! let (reader, _) = { use gunnar_sendpack::Transport as _; transport.io() };
53//! let adv = read_advertisement(reader)?;
54//!
55//! let tip = adv.oid_of("refs/heads/main");
56//! # let new_tip = tip;
57//! let commands = vec![PushCommand::update("refs/heads/main", tip, new_tip)];
58//! let report = send_pack(
59//!     &mut transport,
60//!     &adv,
61//!     &commands,
62//!     adv.hash_kind,
63//!     Some(|w: &mut dyn std::io::Write| Ok(w.write_all(b"PACK...")?)),
64//!     &SendPackOptions::default(),
65//! )?;
66//! assert!(report.is_ok(), "{:?}", report.failure_summary());
67//! # Ok(())
68//! # }
69//! ```
70//!
71//! # Licence
72//!
73//! `MIT OR Apache-2.0`, to match gitoxide's.
74
75#![deny(missing_docs)]
76#![forbid(unsafe_code)]
77
78pub mod advertisement;
79pub mod capabilities;
80pub mod command;
81pub mod error;
82pub mod options;
83pub mod report;
84
85#[cfg(feature = "blocking-io")]
86pub mod driver;
87#[cfg(feature = "blocking-io")]
88pub mod transport;
89
90pub use advertisement::{Advertisement, RemoteRef};
91pub use capabilities::Capabilities;
92pub use command::PushCommand;
93pub use error::{Error, Result};
94pub use options::SendPackOptions;
95pub use report::{PushReport, RefStatus};
96
97#[cfg(feature = "blocking-io")]
98pub use driver::{read_advertisement, read_report, send_pack};
99#[cfg(feature = "blocking-io")]
100pub use transport::{IoPair, Transport};