summaryrefslogtreecommitdiffstats
path: root/vendor/gix-protocol/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-protocol/src')
-rw-r--r--vendor/gix-protocol/src/command/mod.rs6
-rw-r--r--vendor/gix-protocol/src/fetch/arguments/async_io.rs2
-rw-r--r--vendor/gix-protocol/src/fetch/arguments/blocking_io.rs2
-rw-r--r--vendor/gix-protocol/src/fetch/arguments/mod.rs36
-rw-r--r--vendor/gix-protocol/src/fetch/delegate.rs4
-rw-r--r--vendor/gix-protocol/src/fetch/response/async_io.rs18
-rw-r--r--vendor/gix-protocol/src/fetch/response/blocking_io.rs18
-rw-r--r--vendor/gix-protocol/src/fetch/response/mod.rs2
-rw-r--r--vendor/gix-protocol/src/fetch/tests.rs28
-rw-r--r--vendor/gix-protocol/src/fetch_fn.rs7
-rw-r--r--vendor/gix-protocol/src/handshake/function.rs10
-rw-r--r--vendor/gix-protocol/src/handshake/refs/shared.rs4
-rw-r--r--vendor/gix-protocol/src/ls_refs.rs4
13 files changed, 111 insertions, 30 deletions
diff --git a/vendor/gix-protocol/src/command/mod.rs b/vendor/gix-protocol/src/command/mod.rs
index d560220d0..1216ba625 100644
--- a/vendor/gix-protocol/src/command/mod.rs
+++ b/vendor/gix-protocol/src/command/mod.rs
@@ -60,7 +60,7 @@ mod with_io {
match self {
Command::LsRefs => &[],
Command::Fetch => match version {
- gix_transport::Protocol::V1 => &[
+ gix_transport::Protocol::V0 | gix_transport::Protocol::V1 => &[
"multi_ack",
"thin-pack",
"side-band",
@@ -120,7 +120,7 @@ mod with_io {
) -> Vec<Feature> {
match self {
Command::Fetch => match version {
- gix_transport::Protocol::V1 => {
+ gix_transport::Protocol::V0 | gix_transport::Protocol::V1 => {
let has_multi_ack_detailed = server_capabilities.contains("multi_ack_detailed");
let has_sideband_64k = server_capabilities.contains("side-band-64k");
self.all_features(version)
@@ -173,7 +173,7 @@ mod with_io {
panic!("{}: argument {} is not known or allowed", self.as_str(), arg);
}
match version {
- gix_transport::Protocol::V1 => {
+ gix_transport::Protocol::V0 | gix_transport::Protocol::V1 => {
for (feature, _) in features {
if server
.iter()
diff --git a/vendor/gix-protocol/src/fetch/arguments/async_io.rs b/vendor/gix-protocol/src/fetch/arguments/async_io.rs
index 3984ec610..fc876d02c 100644
--- a/vendor/gix-protocol/src/fetch/arguments/async_io.rs
+++ b/vendor/gix-protocol/src/fetch/arguments/async_io.rs
@@ -14,7 +14,7 @@ impl Arguments {
assert!(add_done_argument, "If there are no haves, is_done must be true.");
}
match self.version {
- gix_transport::Protocol::V1 => {
+ gix_transport::Protocol::V0 | gix_transport::Protocol::V1 => {
let (on_into_read, retained_state) = self.prepare_v1(
transport.connection_persists_across_multiple_requests(),
add_done_argument,
diff --git a/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs b/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs
index b49d1a1ba..571792148 100644
--- a/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs
+++ b/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs
@@ -15,7 +15,7 @@ impl Arguments {
assert!(add_done_argument, "If there are no haves, is_done must be true.");
}
match self.version {
- gix_transport::Protocol::V1 => {
+ gix_transport::Protocol::V0 | gix_transport::Protocol::V1 => {
let (on_into_read, retained_state) = self.prepare_v1(
transport.connection_persists_across_multiple_requests(),
add_done_argument,
diff --git a/vendor/gix-protocol/src/fetch/arguments/mod.rs b/vendor/gix-protocol/src/fetch/arguments/mod.rs
index 39c9eee3a..1adf993bb 100644
--- a/vendor/gix-protocol/src/fetch/arguments/mod.rs
+++ b/vendor/gix-protocol/src/fetch/arguments/mod.rs
@@ -49,20 +49,20 @@ impl Arguments {
pub fn can_use_deepen(&self) -> bool {
self.shallow
}
- /// Return true if the 'deepen_since' capability is supported.
+ /// Return true if the '`deepen_since`' capability is supported.
///
/// This is relevant for partial clones when using `--depth X` and retrieving additional history
/// based on a date beyond which all history should be present.
pub fn can_use_deepen_since(&self) -> bool {
self.deepen_since
}
- /// Return true if the 'deepen_not' capability is supported.
+ /// Return true if the '`deepen_not`' capability is supported.
///
/// This is relevant for partial clones when using `--depth X`.
pub fn can_use_deepen_not(&self) -> bool {
self.deepen_not
}
- /// Return true if the 'deepen_relative' capability is supported.
+ /// Return true if the '`deepen_relative`' capability is supported.
///
/// This is relevant for partial clones when using `--depth X`.
pub fn can_use_deepen_relative(&self) -> bool {
@@ -78,6 +78,18 @@ impl Arguments {
pub fn can_use_include_tag(&self) -> bool {
self.supports_include_tag
}
+ /// Return true if we will use a stateless mode of operation, which can be decided in conjunction with `transport_is_stateless`.
+ ///
+ /// * we are always stateless if the transport is stateless, i.e. doesn't support multiple interactions with a single connection.
+ /// * we are always stateless if the protocol version is `2`
+ /// * otherwise we may be stateful.
+ pub fn is_stateless(&self, transport_is_stateless: bool) -> bool {
+ #[cfg(any(feature = "async-client", feature = "blocking-client"))]
+ let res = transport_is_stateless || self.version == gix_transport::Protocol::V2;
+ #[cfg(not(any(feature = "async-client", feature = "blocking-client")))]
+ let res = transport_is_stateless;
+ res
+ }
/// Add the given `id` pointing to a commit to the 'want' list.
///
@@ -153,7 +165,18 @@ impl Arguments {
pub fn use_include_tag(&mut self) {
debug_assert!(self.supports_include_tag, "'include-tag' feature required");
if self.supports_include_tag {
- self.args.push("include-tag".into());
+ match self.version {
+ gix_transport::Protocol::V0 | gix_transport::Protocol::V1 => {
+ let features = self
+ .features_for_first_want
+ .as_mut()
+ .expect("call use_include_tag before want()");
+ features.push("include-tag".into())
+ }
+ gix_transport::Protocol::V2 => {
+ self.args.push("include-tag".into());
+ }
+ }
}
}
fn prefixed(&mut self, prefix: &str, value: impl fmt::Display) {
@@ -173,13 +196,16 @@ impl Arguments {
let mut deepen_relative = shallow;
let supports_include_tag;
let (initial_arguments, features_for_first_want) = match version {
- gix_transport::Protocol::V1 => {
+ gix_transport::Protocol::V0 | gix_transport::Protocol::V1 => {
deepen_since = has("deepen-since");
deepen_not = has("deepen-not");
deepen_relative = has("deepen-relative");
supports_include_tag = has("include-tag");
let baked_features = features
.iter()
+ .filter(
+ |(f, _)| *f != "include-tag", /* not a capability in that sense, needs to be turned on by caller later */
+ )
.map(|(n, v)| match v {
Some(v) => format!("{n}={v}"),
None => n.to_string(),
diff --git a/vendor/gix-protocol/src/fetch/delegate.rs b/vendor/gix-protocol/src/fetch/delegate.rs
index d4b900f66..e90022d41 100644
--- a/vendor/gix-protocol/src/fetch/delegate.rs
+++ b/vendor/gix-protocol/src/fetch/delegate.rs
@@ -40,7 +40,7 @@ pub trait DelegateBlocking {
/// Note that some arguments are preset based on typical use, and `features` are preset to maximize options.
/// The `server` capabilities can be used to see which additional capabilities the server supports as per the handshake which happened prior.
///
- /// If the delegate returns [`ls_refs::Action::Skip`], no 'ls-refs` command is sent to the server.
+ /// If the delegate returns [`ls_refs::Action::Skip`], no `ls-refs` command is sent to the server.
///
/// Note that this is called only if we are using protocol version 2.
fn prepare_ls_refs(
@@ -55,7 +55,7 @@ pub trait DelegateBlocking {
/// Called before invoking the 'fetch' interaction with `features` pre-filled for typical use
/// and to maximize capabilities to allow aborting an interaction early.
///
- /// `refs` is a list of known references on the remote based on the handshake or a prior call to ls_refs.
+ /// `refs` is a list of known references on the remote based on the handshake or a prior call to `ls_refs`.
/// These can be used to abort early in case the refs are already known here.
///
/// As there will be another call allowing to post arguments conveniently in the correct format, i.e. `want hex-oid`,
diff --git a/vendor/gix-protocol/src/fetch/response/async_io.rs b/vendor/gix-protocol/src/fetch/response/async_io.rs
index 550ed46b6..c510a1ad4 100644
--- a/vendor/gix-protocol/src/fetch/response/async_io.rs
+++ b/vendor/gix-protocol/src/fetch/response/async_io.rs
@@ -32,15 +32,22 @@ async fn parse_v2_section<T>(
impl Response {
/// Parse a response of the given `version` of the protocol from `reader`.
+ ///
+ /// `client_expects_pack` is only relevant for V1 stateful connections, and if `false`, causes us to stop parsing when seeing `NAK`,
+ /// and if `true` we will keep parsing until we get a pack as the client already signalled to the server that it's done.
+ /// This way of doing things allows us to exploit knowledge about more recent versions of the protocol, which keeps code easier
+ /// and more localized without having to support all the cruft that there is.
pub async fn from_line_reader(
version: Protocol,
reader: &mut (impl client::ExtendedBufRead + Unpin),
+ client_expects_pack: bool,
) -> Result<Response, response::Error> {
match version {
- Protocol::V1 => {
+ Protocol::V0 | Protocol::V1 => {
let mut line = String::new();
let mut acks = Vec::<Acknowledgement>::new();
let mut shallows = Vec::<ShallowUpdate>::new();
+ let mut saw_ready = false;
let has_pack = 'lines: loop {
line.clear();
let peeked_line = match reader.peek_data_line().await {
@@ -48,8 +55,8 @@ impl Response {
// This special case (hang/block forever) deals with a single NAK being a legitimate EOF sometimes
// Note that this might block forever in stateful connections as there it's not really clear
// if something will be following or not by just looking at the response. Instead you have to know
- // the arguments sent to the server and count response lines based on intricate knowledge on how the
- // server works.
+ // [a lot](https://github.com/git/git/blob/9e49351c3060e1fa6e0d2de64505b7becf157f28/fetch-pack.c#L583-L594)
+ // to deal with this correctly.
// For now this is acceptable, as V2 can be used as a workaround, which also is the default.
Some(Err(err)) if err.kind() == io::ErrorKind::UnexpectedEof => break 'lines false,
Some(Err(err)) => return Err(err.into()),
@@ -80,6 +87,11 @@ impl Response {
0,
"consuming a peeked line works"
);
+ // When the server sends ready, we know there is going to be a pack so no need to stop early.
+ saw_ready |= matches!(acks.last(), Some(Acknowledgement::Ready));
+ if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack && !saw_ready) {
+ break 'lines false;
+ }
};
Ok(Response {
acks,
diff --git a/vendor/gix-protocol/src/fetch/response/blocking_io.rs b/vendor/gix-protocol/src/fetch/response/blocking_io.rs
index 7a3f2deb3..309f5a7c5 100644
--- a/vendor/gix-protocol/src/fetch/response/blocking_io.rs
+++ b/vendor/gix-protocol/src/fetch/response/blocking_io.rs
@@ -32,15 +32,22 @@ fn parse_v2_section<T>(
impl Response {
/// Parse a response of the given `version` of the protocol from `reader`.
+ ///
+ /// `client_expects_pack` is only relevant for V1 stateful connections, and if `false`, causes us to stop parsing when seeing `NAK`,
+ /// and if `true` we will keep parsing until we get a pack as the client already signalled to the server that it's done.
+ /// This way of doing things allows us to exploit knowledge about more recent versions of the protocol, which keeps code easier
+ /// and more localized without having to support all the cruft that there is.
pub fn from_line_reader(
version: Protocol,
reader: &mut impl client::ExtendedBufRead,
+ client_expects_pack: bool,
) -> Result<Response, response::Error> {
match version {
- Protocol::V1 => {
+ Protocol::V0 | Protocol::V1 => {
let mut line = String::new();
let mut acks = Vec::<Acknowledgement>::new();
let mut shallows = Vec::<ShallowUpdate>::new();
+ let mut saw_ready = false;
let has_pack = 'lines: loop {
line.clear();
let peeked_line = match reader.peek_data_line() {
@@ -48,8 +55,8 @@ impl Response {
// This special case (hang/block forever) deals with a single NAK being a legitimate EOF sometimes
// Note that this might block forever in stateful connections as there it's not really clear
// if something will be following or not by just looking at the response. Instead you have to know
- // the arguments sent to the server and count response lines based on intricate knowledge on how the
- // server works.
+ // [a lot](https://github.com/git/git/blob/9e49351c3060e1fa6e0d2de64505b7becf157f28/fetch-pack.c#L583-L594)
+ // to deal with this correctly.
// For now this is acceptable, as V2 can be used as a workaround, which also is the default.
Some(Err(err)) if err.kind() == io::ErrorKind::UnexpectedEof => break 'lines false,
Some(Err(err)) => return Err(err.into()),
@@ -76,6 +83,11 @@ impl Response {
break 'lines true;
}
assert_ne!(reader.readline_str(&mut line)?, 0, "consuming a peeked line works");
+ // When the server sends ready, we know there is going to be a pack so no need to stop early.
+ saw_ready |= matches!(acks.last(), Some(Acknowledgement::Ready));
+ if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack && !saw_ready) {
+ break 'lines false;
+ }
};
Ok(Response {
acks,
diff --git a/vendor/gix-protocol/src/fetch/response/mod.rs b/vendor/gix-protocol/src/fetch/response/mod.rs
index bfb4beb83..5f2f7f007 100644
--- a/vendor/gix-protocol/src/fetch/response/mod.rs
+++ b/vendor/gix-protocol/src/fetch/response/mod.rs
@@ -170,7 +170,7 @@ impl Response {
/// make it easy to maintain all versions with a single code base that aims to be and remain maintainable.
pub fn check_required_features(version: Protocol, features: &[Feature]) -> Result<(), Error> {
match version {
- Protocol::V1 => {
+ Protocol::V0 | Protocol::V1 => {
let has = |name: &str| features.iter().any(|f| f.0 == name);
// Let's focus on V2 standards, and simply not support old servers to keep our code simpler
if !has("multi_ack_detailed") {
diff --git a/vendor/gix-protocol/src/fetch/tests.rs b/vendor/gix-protocol/src/fetch/tests.rs
index 5a1902ad2..80dc752dd 100644
--- a/vendor/gix-protocol/src/fetch/tests.rs
+++ b/vendor/gix-protocol/src/fetch/tests.rs
@@ -163,7 +163,7 @@ mod arguments {
async fn include_tag() {
let mut out = Vec::new();
let mut t = transport(&mut out, true);
- let mut arguments = arguments_v1(["include-tag", "feature-b"].iter().cloned());
+ let mut arguments = arguments_v1(["include-tag", "feature-b"].iter().copied());
assert!(arguments.can_use_include_tag());
arguments.use_include_tag();
@@ -171,8 +171,7 @@ mod arguments {
arguments.send(&mut t, true).await.expect("sending to buffer to work");
assert_eq!(
out.as_bstr(),
- b"0048want ff333369de1221f9bfbbe03a3a13e9a09bc1ffff include-tag feature-b
-0010include-tag
+ b"0048want ff333369de1221f9bfbbe03a3a13e9a09bc1ffff feature-b include-tag
00000009done
"
.as_bstr()
@@ -180,10 +179,29 @@ mod arguments {
}
#[maybe_async::test(feature = "blocking-client", async(feature = "async-client", async_std::test))]
+ async fn no_include_tag() {
+ let mut out = Vec::new();
+ let mut t = transport(&mut out, true);
+ let mut arguments = arguments_v1(["include-tag", "feature-b"].iter().copied());
+ assert!(arguments.can_use_include_tag());
+
+ arguments.want(id("ff333369de1221f9bfbbe03a3a13e9a09bc1ffff"));
+ arguments.send(&mut t, true).await.expect("sending to buffer to work");
+ assert_eq!(
+ out.as_bstr(),
+ b"003cwant ff333369de1221f9bfbbe03a3a13e9a09bc1ffff feature-b
+00000009done
+"
+ .as_bstr(),
+ "it's possible to not have it enabled, even though it's advertised by the server"
+ );
+ }
+
+ #[maybe_async::test(feature = "blocking-client", async(feature = "async-client", async_std::test))]
async fn haves_and_wants_for_clone() {
let mut out = Vec::new();
let mut t = transport(&mut out, true);
- let mut arguments = arguments_v1(["feature-a", "feature-b"].iter().cloned());
+ let mut arguments = arguments_v1(["feature-a", "feature-b"].iter().copied());
assert!(
!arguments.can_use_include_tag(),
"needs to be enabled by features in V1"
@@ -298,6 +316,8 @@ mod arguments {
let mut out = Vec::new();
let mut t = transport(&mut out, true);
let mut arguments = arguments_v2(["feature-a", "shallow"].iter().copied());
+ assert!(arguments.is_stateless(true), "V2 is stateless…");
+ assert!(arguments.is_stateless(false), "…in all cases");
arguments.deepen(1);
arguments.deepen_relative();
diff --git a/vendor/gix-protocol/src/fetch_fn.rs b/vendor/gix-protocol/src/fetch_fn.rs
index 5899ed95f..64d457711 100644
--- a/vendor/gix-protocol/src/fetch_fn.rs
+++ b/vendor/gix-protocol/src/fetch_fn.rs
@@ -128,7 +128,12 @@ where
if sideband_all {
setup_remote_progress(&mut progress, &mut reader);
}
- let response = Response::from_line_reader(protocol_version, &mut reader).await?;
+ let response = Response::from_line_reader(
+ protocol_version,
+ &mut reader,
+ true, /* hack, telling us we don't want this delegate approach anymore */
+ )
+ .await?;
previous_response = if response.has_pack() {
progress.step();
progress.set_name("receiving pack");
diff --git a/vendor/gix-protocol/src/handshake/function.rs b/vendor/gix-protocol/src/handshake/function.rs
index 1206ee363..6324fb3e1 100644
--- a/vendor/gix-protocol/src/handshake/function.rs
+++ b/vendor/gix-protocol/src/handshake/function.rs
@@ -77,10 +77,12 @@ where
let parsed_refs = match refs {
Some(mut refs) => {
- assert_eq!(
- actual_protocol,
- gix_transport::Protocol::V1,
- "Only V1 auto-responds with refs"
+ assert!(
+ matches!(
+ actual_protocol,
+ gix_transport::Protocol::V0 | gix_transport::Protocol::V1
+ ),
+ "Only V(0|1) auto-responds with refs"
);
Some(
refs::from_v1_refs_received_as_part_of_handshake_and_capabilities(&mut refs, capabilities.iter())
diff --git a/vendor/gix-protocol/src/handshake/refs/shared.rs b/vendor/gix-protocol/src/handshake/refs/shared.rs
index 1d0dfc256..046a2a1b1 100644
--- a/vendor/gix-protocol/src/handshake/refs/shared.rs
+++ b/vendor/gix-protocol/src/handshake/refs/shared.rs
@@ -123,6 +123,10 @@ pub(in crate::handshake::refs) fn parse_v1(
}
match path.strip_suffix(b"^{}") {
Some(stripped) => {
+ if hex_hash.iter().all(|b| *b == b'0') && stripped == b"capabilities" {
+ // this is a special dummy-ref just for the sake of getting capabilities across in a repo that is empty.
+ return Ok(());
+ }
let (previous_path, tag) =
out_refs
.pop()
diff --git a/vendor/gix-protocol/src/ls_refs.rs b/vendor/gix-protocol/src/ls_refs.rs
index a31588894..d0b2e9ba0 100644
--- a/vendor/gix-protocol/src/ls_refs.rs
+++ b/vendor/gix-protocol/src/ls_refs.rs
@@ -1,7 +1,7 @@
mod error {
use crate::handshake::refs::parse;
- /// The error returned by [ls_refs()][crate::ls_refs()].
+ /// The error returned by [`ls_refs()`][crate::ls_refs()].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
@@ -25,7 +25,7 @@ mod error {
}
pub use error::Error;
-/// What to do after preparing ls-refs in [ls_refs()][crate::ls_refs()].
+/// What to do after preparing ls-refs in [`ls_refs()`][crate::ls_refs()].
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
pub enum Action {
/// Continue by sending a 'ls-refs' command.