diff --git a/gitoxide-core/src/pack/receive.rs b/gitoxide-core/src/pack/receive.rs index 0be29d7afd0..c2c2af08760 100644 --- a/gitoxide-core/src/pack/receive.rs +++ b/gitoxide-core/src/pack/receive.rs @@ -87,17 +87,17 @@ where fetch_refspecs: fetch_refspecs.clone(), extra_refspecs: vec![], }; - let refmap = handshake - .fetch_or_extract_refmap( - &mut progress, - &mut transport.inner, - user_agent.clone(), - trace_packetlines, - true, - context, - ) + + let fetch_refmap = handshake.prepare_lsrefs_or_extract_refmap(user_agent.clone(), true, context)?; + + #[cfg(feature = "async-client")] + let refmap = fetch_refmap + .fetch_async(&mut progress, &mut transport.inner, trace_packetlines) .await?; + #[cfg(feature = "blocking-client")] + let refmap = fetch_refmap.fetch_blocking(&mut progress, &mut transport.inner, trace_packetlines)?; + if refmap.mappings.is_empty() && !refmap.remote_refs.is_empty() { return Err(Error::NoMapping { refspecs: refmap.refspecs.clone(), diff --git a/gix-protocol/src/fetch/negotiate.rs b/gix-protocol/src/fetch/negotiate.rs index 7480aea9960..205c8b36e26 100644 --- a/gix-protocol/src/fetch/negotiate.rs +++ b/gix-protocol/src/fetch/negotiate.rs @@ -111,7 +111,7 @@ pub struct Round { /// * `graph` /// - The commit-graph for use by the `negotiator` - we populate it with tips to initialize the graph traversal. /// * `ref_map` -/// - The references known on the remote, as previously obtained with [`RefMap::fetch()`]. +/// - The references known on the remote, as previously obtained with [`crate::Handshake::prepare_lsrefs_or_extract_refmap()`]. /// * `shallow` /// - How to deal with shallow repositories. It does affect how negotiations are performed. /// * `mapping_is_ignored` diff --git a/gix-protocol/src/fetch/refmap/init.rs b/gix-protocol/src/fetch/refmap/init.rs index abcf027cfb1..ff539fc3a29 100644 --- a/gix-protocol/src/fetch/refmap/init.rs +++ b/gix-protocol/src/fetch/refmap/init.rs @@ -1,13 +1,6 @@ -use std::{borrow::Cow, collections::HashSet}; - -use bstr::{BString, ByteSlice, ByteVec}; -use gix_features::progress::Progress; +use bstr::{BString, ByteSlice}; use gix_transport::client::Capabilities; -#[cfg(feature = "async-client")] -use crate::transport::client::async_io::Transport; -#[cfg(feature = "blocking-client")] -use crate::transport::client::blocking_io::Transport; use crate::{ fetch::{ refmap::{Mapping, Source, SpecIndex}, @@ -16,7 +9,7 @@ use crate::{ handshake::Ref, }; -/// The error returned by [`RefMap::fetch()`]. +/// The error returned by [`crate::Handshake::prepare_lsrefs_or_extract_refmap()`]. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { @@ -28,7 +21,7 @@ pub enum Error { ListRefs(#[from] crate::ls_refs::Error), } -/// For use in [`RefMap::fetch()`]. +/// For use in [`RefMap::from_refs()`]. #[derive(Debug, Clone)] pub struct Context { /// All explicit refspecs to identify references on the remote that you are interested in. @@ -41,7 +34,7 @@ pub struct Context { } impl Context { - fn aggregate_refspecs(&self) -> Vec { + pub(crate) fn aggregate_refspecs(&self) -> Vec { let mut all_refspecs = self.fetch_refspecs.clone(); all_refspecs.extend(self.extra_refspecs.iter().cloned()); all_refspecs @@ -49,50 +42,6 @@ impl Context { } impl RefMap { - /// Create a new instance by obtaining all references on the remote that have been filtered through our remote's specs - /// for _fetching_. - /// - /// * `progress` is used if `ls-refs` is invoked on the remote. Always the case when V2 is used. - /// * `capabilities` are the capabilities of the server, obtained by a [handshake](crate::handshake()). - /// * `transport` is a way to communicate with the server to obtain the reference listing. - /// * `user_agent` is passed to the server. - /// * `trace_packetlines` traces all packet lines if `true`, for debugging primarily. - /// * `prefix_from_spec_as_filter_on_remote` - /// - Use a two-component prefix derived from the ref-spec's source, like `refs/heads/` to let the server pre-filter refs - /// with great potential for savings in traffic and local CPU time. - /// * `context` to provide more [configuration](Context). - #[allow(clippy::result_large_err)] - #[maybe_async::maybe_async] - pub async fn fetch( - mut progress: impl Progress, - capabilities: &Capabilities, - transport: &mut T, - user_agent: (&'static str, Option>), - trace_packetlines: bool, - prefix_from_spec_as_filter_on_remote: bool, - context: Context, - ) -> Result - where - T: Transport, - { - let _span = gix_trace::coarse!("gix_protocol::fetch::RefMap::new()"); - let all_refspecs = context.aggregate_refspecs(); - let remote_refs = crate::ls_refs( - transport, - capabilities, - |_capabilities, arguments| { - push_prefix_arguments(prefix_from_spec_as_filter_on_remote, arguments, &all_refspecs); - Ok(crate::ls_refs::Action::Continue) - }, - &mut progress, - trace_packetlines, - user_agent, - ) - .await?; - - Self::from_refs(remote_refs, capabilities, context) - } - /// Create a ref-map from already obtained `remote_refs`. Use `context` to pass in refspecs. /// `capabilities` are used to determine the object format. pub fn from_refs(remote_refs: Vec, capabilities: &Capabilities, context: Context) -> Result { @@ -161,26 +110,3 @@ impl RefMap { }) } } - -fn push_prefix_arguments( - prefix_from_spec_as_filter_on_remote: bool, - arguments: &mut Vec, - all_refspecs: &[gix_refspec::RefSpec], -) { - if !prefix_from_spec_as_filter_on_remote { - return; - } - - let mut seen = HashSet::new(); - for spec in all_refspecs { - let spec = spec.to_ref(); - if seen.insert(spec.instruction()) { - let mut prefixes = Vec::with_capacity(1); - spec.expand_prefixes(&mut prefixes); - for mut prefix in prefixes { - prefix.insert_str(0, "ref-prefix "); - arguments.push(prefix); - } - } - } -} diff --git a/gix-protocol/src/fetch/types.rs b/gix-protocol/src/fetch/types.rs index 549451f4703..fc0b04de295 100644 --- a/gix-protocol/src/fetch/types.rs +++ b/gix-protocol/src/fetch/types.rs @@ -18,7 +18,7 @@ pub struct Options<'a> { pub reject_shallow_remote: bool, } -/// For use in [`RefMap::fetch()`] and [`fetch`](crate::fetch()). +/// For use in [`crate::Handshake::prepare_lsrefs_or_extract_refmap()`] and [`fetch`](crate::fetch()). #[cfg(feature = "handshake")] pub struct Context<'a, T> { /// The outcome of the handshake performed with the remote. @@ -29,7 +29,7 @@ pub struct Context<'a, T> { /// /// This is always done if the underlying protocol is V2, which is implied by the absence of refs in the `handshake` outcome. pub transport: &'a mut T, - /// How to self-identify during the `ls-refs` call in [`RefMap::fetch()`] or the `fetch` call in [`fetch()`](crate::fetch()). + /// How to self-identify during the `ls-refs` call in [`crate::Handshake::prepare_lsrefs_or_extract_refmap()`] or the `fetch` call in [`fetch()`](crate::fetch()). /// /// This could be read from the `gitoxide.userAgent` configuration variable. pub user_agent: (&'static str, Option>), diff --git a/gix-protocol/src/handshake/mod.rs b/gix-protocol/src/handshake/mod.rs index 31128063a52..dc00f5726b3 100644 --- a/gix-protocol/src/handshake/mod.rs +++ b/gix-protocol/src/handshake/mod.rs @@ -72,45 +72,86 @@ pub(crate) mod hero { #[cfg(feature = "fetch")] mod fetch { #[cfg(feature = "async-client")] - use crate::transport::client::async_io::Transport; + use crate::transport::client::async_io; #[cfg(feature = "blocking-client")] - use crate::transport::client::blocking_io::Transport; - use crate::Handshake; + use crate::transport::client::blocking_io; + use crate::{fetch::RefMap, Handshake}; use gix_features::progress::Progress; use std::borrow::Cow; + /// Intermediate state while potentially fetching a refmap after the handshake. + pub enum ObtainRefMap<'a> { + /// We already got a refmap to use from the V1 handshake, which always sends them. + Existing(RefMap), + /// We need to invoke another `ls-refs` command to retrieve the refmap as part of the V2 protocol. + LsRefsCommand(crate::LsRefsCommand<'a>, crate::fetch::refmap::init::Context), + } + + impl ObtainRefMap<'_> { + /// Fetch the refmap, either by returning the existing one or invoking the `ls-refs` command. + #[cfg(feature = "async-client")] + #[allow(clippy::result_large_err)] + pub async fn fetch_async( + self, + mut progress: impl Progress, + transport: &mut impl async_io::Transport, + trace_packetlines: bool, + ) -> Result { + let (cmd, cx) = match self { + ObtainRefMap::Existing(map) => return Ok(map), + ObtainRefMap::LsRefsCommand(cmd, cx) => (cmd, cx), + }; + + let _span = gix_trace::coarse!("gix_protocol::handshake::ObtainRefMap::fetch_async()"); + let capabilities = cmd.capabilities; + let remote_refs = cmd.invoke_async(transport, &mut progress, trace_packetlines).await?; + RefMap::from_refs(remote_refs, capabilities, cx) + } + + /// Fetch the refmap, either by returning the existing one or invoking the `ls-refs` command. + #[cfg(feature = "blocking-client")] + #[allow(clippy::result_large_err)] + pub fn fetch_blocking( + self, + mut progress: impl Progress, + transport: &mut impl blocking_io::Transport, + trace_packetlines: bool, + ) -> Result { + let (cmd, cx) = match self { + ObtainRefMap::Existing(map) => return Ok(map), + ObtainRefMap::LsRefsCommand(cmd, cx) => (cmd, cx), + }; + + let _span = gix_trace::coarse!("gix_protocol::handshake::ObtainRefMap::fetch_blocking()"); + let capabilities = cmd.capabilities; + let remote_refs = cmd.invoke_blocking(transport, &mut progress, trace_packetlines)?; + RefMap::from_refs(remote_refs, capabilities, cx) + } + } + impl Handshake { - /// Obtain a [refmap](crate::fetch::RefMap) either from this instance, taking it out in the process, if the handshake was - /// created from a V1 connection, or use `transport` to fetch the refmap as a separate command invocation. + /// Prepare fetching a [refmap](RefMap) if not present in the handshake. #[allow(clippy::result_large_err)] - #[maybe_async::maybe_async] - pub async fn fetch_or_extract_refmap( + pub fn prepare_lsrefs_or_extract_refmap( &mut self, - mut progress: impl Progress, - transport: &mut T, user_agent: (&'static str, Option>), - trace_packetlines: bool, prefix_from_spec_as_filter_on_remote: bool, refmap_context: crate::fetch::refmap::init::Context, - ) -> Result - where - T: Transport, - { - Ok(match self.refs.take() { - Some(refs) => crate::fetch::RefMap::from_refs(refs, &self.capabilities, refmap_context)?, - None => { - crate::fetch::RefMap::fetch( - &mut progress, - &self.capabilities, - transport, - user_agent, - trace_packetlines, - prefix_from_spec_as_filter_on_remote, - refmap_context, - ) - .await? - } - }) + ) -> Result, crate::fetch::refmap::init::Error> { + if let Some(refs) = self.refs.take() { + return Ok(ObtainRefMap::Existing(RefMap::from_refs( + refs, + &self.capabilities, + refmap_context, + )?)); + } + + let all_refspecs = refmap_context.aggregate_refspecs(); + let prefix_refspecs = prefix_from_spec_as_filter_on_remote.then_some(&all_refspecs[..]); + Ok(ObtainRefMap::LsRefsCommand( + crate::LsRefsCommand::new(prefix_refspecs, &self.capabilities, user_agent), + refmap_context, + )) } } } @@ -150,6 +191,7 @@ mod error { } } } + #[cfg(feature = "handshake")] pub use error::Error; diff --git a/gix-protocol/src/lib.rs b/gix-protocol/src/lib.rs index 9765b102d61..9f0f070e3fa 100644 --- a/gix-protocol/src/lib.rs +++ b/gix-protocol/src/lib.rs @@ -5,7 +5,7 @@ //! * create a `Transport`, either blocking or async //! * perform a [`handshake()`] //! * execute a [`Command`] -//! - [list references](ls_refs()) +//! - [list references](LsRefsCommand) //! - create a mapping between [refspecs and references](fetch::RefMap) //! - [receive a pack](fetch()) //! @@ -67,7 +67,7 @@ pub use handshake::hero::Handshake; /// pub mod ls_refs; #[cfg(any(feature = "blocking-client", feature = "async-client"))] -pub use ls_refs::function::ls_refs; +pub use ls_refs::function::LsRefsCommand; mod util; pub use util::*; diff --git a/gix-protocol/src/ls_refs.rs b/gix-protocol/src/ls_refs.rs index d9fd8c95c5d..3e710c4b45b 100644 --- a/gix-protocol/src/ls_refs.rs +++ b/gix-protocol/src/ls_refs.rs @@ -2,7 +2,7 @@ mod error { use crate::handshake::refs::parse; - /// The error returned by [`ls_refs()`][crate::ls_refs()]. + /// The error returned by invoking a [`super::function::LsRefsCommand`]. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { @@ -29,94 +29,144 @@ mod error { #[cfg(any(feature = "blocking-client", feature = "async-client"))] pub use error::Error; -/// 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. - Continue, - /// Skip 'ls-refs' entirely. - /// - /// This is useful if the `ref-in-want` capability is taken advantage of. When fetching, one must must then send - /// `want-ref`s during the negotiation phase. - Skip, -} - #[cfg(any(feature = "blocking-client", feature = "async-client"))] pub(crate) mod function { - use std::borrow::Cow; + use std::{borrow::Cow, collections::HashSet}; - use bstr::BString; + use bstr::{BString, ByteVec}; use gix_features::progress::Progress; use gix_transport::client::Capabilities; - use maybe_async::maybe_async; - use super::{Action, Error}; + use super::Error; #[cfg(feature = "async-client")] - use crate::transport::client::async_io::{Transport, TransportV2Ext}; + use crate::transport::client::async_io::{self, TransportV2Ext as _}; #[cfg(feature = "blocking-client")] - use crate::transport::client::blocking_io::{Transport, TransportV2Ext}; + use crate::transport::client::blocking_io::{self, TransportV2Ext as _}; use crate::{ handshake::{refs::from_v2_refs, Ref}, - indicate_end_of_interaction, Command, + Command, }; - /// Invoke a ls-refs V2 command on `transport`, which requires a prior handshake that yielded - /// server `capabilities`. `prepare_ls_refs(capabilities, arguments)` can be used to alter the _ls-refs_. - /// `progress` is used to provide feedback. - /// The `agent` information will be added to the features sent to the server. - /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate. - #[maybe_async] - pub async fn ls_refs( - mut transport: impl Transport, - capabilities: &Capabilities, - prepare_ls_refs: impl FnOnce(&Capabilities, &mut Vec) -> std::io::Result, - progress: &mut impl Progress, - trace: bool, - agent: (&'static str, Option>), - ) -> Result, Error> { - let _span = gix_features::trace::detail!("gix_protocol::ls_refs()", capabilities = ?capabilities); - let ls_refs = Command::LsRefs; - let mut ls_features = ls_refs.default_features(gix_transport::Protocol::V2, capabilities); - ls_features.push(agent); - let mut ls_args = ls_refs.initial_v2_arguments(&ls_features); - if capabilities - .capability("ls-refs") - .and_then(|cap| cap.supports("unborn")) - .unwrap_or_default() - { - ls_args.push("unborn".into()); - } - let refs = match prepare_ls_refs(capabilities, &mut ls_args) { - Ok(Action::Skip) => Vec::new(), - Ok(Action::Continue) => { - ls_refs.validate_argument_prefixes( - gix_transport::Protocol::V2, - capabilities, - &ls_args, - &ls_features, - )?; + /// A command to list references from a remote Git repository. + /// + /// It acts as a utility to separate the invocation into the shared blocking portion, + /// and the one that performs IO either blocking or `async`. + pub struct LsRefsCommand<'a> { + pub(crate) capabilities: &'a Capabilities, + features: Vec<(&'static str, Option>)>, + arguments: Vec, + } + + impl<'a> LsRefsCommand<'a> { + /// Build a command to list refs from the given server `capabilities`, + /// using `agent` information to identify ourselves. + pub fn new( + prefix_refspecs: Option<&[gix_refspec::RefSpec]>, + capabilities: &'a Capabilities, + agent: (&'static str, Option>), + ) -> Self { + let ls_refs = Command::LsRefs; + let mut features = ls_refs.default_features(gix_transport::Protocol::V2, capabilities); + features.push(agent); + let mut arguments = ls_refs.initial_v2_arguments(&features); + if capabilities + .capability("ls-refs") + .and_then(|cap| cap.supports("unborn")) + .unwrap_or_default() + { + arguments.push("unborn".into()); + } - progress.step(); - progress.set_name("list refs".into()); - let mut remote_refs = transport - .invoke( - ls_refs.as_str(), - ls_features.into_iter(), - if ls_args.is_empty() { - None - } else { - Some(ls_args.into_iter()) - }, - trace, - ) - .await?; - from_v2_refs(&mut remote_refs).await? + if let Some(refspecs) = prefix_refspecs { + let mut seen = HashSet::new(); + for spec in refspecs { + let spec = spec.to_ref(); + if seen.insert(spec.instruction()) { + let mut prefixes = Vec::with_capacity(1); + spec.expand_prefixes(&mut prefixes); + for mut prefix in prefixes { + prefix.insert_str(0, "ref-prefix "); + arguments.push(prefix); + } + } + } } - Err(err) => { - indicate_end_of_interaction(transport, trace).await?; - return Err(err.into()); + + Self { + capabilities, + features, + arguments, } - }; - Ok(refs) + } + + /// Invoke a ls-refs V2 command on `transport`. + /// + /// `progress` is used to provide feedback. + /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate. + #[cfg(feature = "async-client")] + pub async fn invoke_async( + self, + mut transport: impl async_io::Transport, + progress: &mut impl Progress, + trace: bool, + ) -> Result, Error> { + let _span = gix_features::trace::detail!("gix_protocol::LsRefsCommand::invoke_async()"); + Command::LsRefs.validate_argument_prefixes( + gix_transport::Protocol::V2, + self.capabilities, + &self.arguments, + &self.features, + )?; + + progress.step(); + progress.set_name("list refs".into()); + let mut remote_refs = transport + .invoke( + Command::LsRefs.as_str(), + self.features.into_iter(), + if self.arguments.is_empty() { + None + } else { + Some(self.arguments.into_iter()) + }, + trace, + ) + .await?; + Ok(from_v2_refs(&mut remote_refs).await?) + } + + /// Invoke a ls-refs V2 command on `transport`. + /// + /// `progress` is used to provide feedback. + /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate. + #[cfg(feature = "blocking-client")] + pub fn invoke_blocking( + self, + mut transport: impl blocking_io::Transport, + progress: &mut impl Progress, + trace: bool, + ) -> Result, Error> { + let _span = gix_features::trace::detail!("gix_protocol::LsRefsCommand::invoke_blocking()"); + Command::LsRefs.validate_argument_prefixes( + gix_transport::Protocol::V2, + self.capabilities, + &self.arguments, + &self.features, + )?; + + progress.step(); + progress.set_name("list refs".into()); + let mut remote_refs = transport.invoke( + Command::LsRefs.as_str(), + self.features.into_iter(), + if self.arguments.is_empty() { + None + } else { + Some(self.arguments.into_iter()) + }, + trace, + )?; + Ok(from_v2_refs(&mut remote_refs)?) + } } } diff --git a/gix-protocol/tests/protocol/fetch/_impl.rs b/gix-protocol/tests/protocol/fetch/_impl.rs index c4484fda920..12e8e98beb1 100644 --- a/gix-protocol/tests/protocol/fetch/_impl.rs +++ b/gix-protocol/tests/protocol/fetch/_impl.rs @@ -1,3 +1,15 @@ +/// What to do after preparing ls-refs. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] +pub enum RefsAction { + /// Continue by sending a 'ls-refs' command. + Continue, + /// Skip 'ls-refs' entirely. + /// + /// This is useful if the `ref-in-want` capability is taken advantage of. When fetching, one + /// must then send `want-ref`s during the negotiation phase. + Skip, +} + mod fetch_fn { use std::borrow::Cow; @@ -5,7 +17,7 @@ mod fetch_fn { use gix_protocol::{ credentials, fetch::{Arguments, Response}, - indicate_end_of_interaction, Command, + indicate_end_of_interaction, Command, LsRefsCommand, }; #[cfg(feature = "async-client")] use gix_transport::client::async_io::{ExtendedBufRead, HandleProgress, Transport}; @@ -13,7 +25,7 @@ mod fetch_fn { use gix_transport::client::blocking_io::{ExtendedBufRead, HandleProgress, Transport}; use maybe_async::maybe_async; - use super::{Action, Delegate}; + use super::{Action, Delegate, RefsAction}; use crate::fetch::Error; /// A way to indicate how to treat the connection underlying the transport, potentially allowing to reuse it. @@ -91,17 +103,26 @@ mod fetch_fn { let agent = gix_protocol::agent(agent); let refs = match refs { Some(refs) => refs, - None => { - gix_protocol::ls_refs( - &mut transport, - &capabilities, - |a, b| delegate.prepare_ls_refs(a, b), - &mut progress, - trace, - ("agent", Some(Cow::Owned(agent.clone()))), - ) - .await? - } + None => match delegate.action() { + Ok(RefsAction::Skip) => Vec::new(), + Ok(RefsAction::Continue) => { + #[cfg(feature = "async-client")] + { + LsRefsCommand::new(None, &capabilities, ("agent", Some(Cow::Owned(agent.clone())))) + .invoke_async(&mut transport, &mut progress, trace) + .await? + } + #[cfg(feature = "blocking-client")] + { + LsRefsCommand::new(None, &capabilities, ("agent", Some(Cow::Owned(agent.clone())))) + .invoke_blocking(&mut transport, &mut progress, trace)? + } + } + Err(err) => { + indicate_end_of_interaction(transport, trace).await?; + return Err(err.into()); + } + }, }; let fetch = Command::Fetch; @@ -195,14 +216,14 @@ mod delegate { ops::{Deref, DerefMut}, }; - use bstr::BString; use gix_protocol::{ fetch::{Arguments, Response}, handshake::Ref, - ls_refs, }; use gix_transport::client::Capabilities; + use super::RefsAction; + /// Defines what to do next after certain [`Delegate`] operations. #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] pub enum Action { @@ -231,15 +252,11 @@ mod delegate { /// 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 [`RefsAction::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( - &mut self, - _server: &Capabilities, - _arguments: &mut Vec, - ) -> std::io::Result { - Ok(ls_refs::Action::Continue) + fn action(&mut self) -> std::io::Result { + Ok(RefsAction::Continue) } /// Called before invoking the 'fetch' interaction with `features` pre-filled for typical use @@ -303,12 +320,8 @@ mod delegate { self.deref().handshake_extra_parameters() } - fn prepare_ls_refs( - &mut self, - _server: &Capabilities, - _arguments: &mut Vec, - ) -> io::Result { - self.deref_mut().prepare_ls_refs(_server, _arguments) + fn action(&mut self) -> io::Result { + self.deref_mut().action() } fn prepare_fetch( @@ -336,12 +349,8 @@ mod delegate { self.deref().handshake_extra_parameters() } - fn prepare_ls_refs( - &mut self, - _server: &Capabilities, - _arguments: &mut Vec, - ) -> io::Result { - self.deref_mut().prepare_ls_refs(_server, _arguments) + fn action(&mut self) -> io::Result { + self.deref_mut().action() } fn prepare_fetch( diff --git a/gix-protocol/tests/protocol/fetch/mod.rs b/gix-protocol/tests/protocol/fetch/mod.rs index 6725959a58f..b26ffc45007 100644 --- a/gix-protocol/tests/protocol/fetch/mod.rs +++ b/gix-protocol/tests/protocol/fetch/mod.rs @@ -3,14 +3,14 @@ use std::{borrow::Cow, io}; use bstr::{BString, ByteSlice}; use gix_protocol::{ fetch::{Arguments, Response}, - handshake, ls_refs, + handshake, }; use gix_transport::client::Capabilities; use crate::fixture_bytes; pub(super) mod _impl; -use _impl::{Action, DelegateBlocking}; +use _impl::{Action, DelegateBlocking, RefsAction}; mod error { use std::io; @@ -102,12 +102,8 @@ pub struct CloneRefInWantDelegate { } impl DelegateBlocking for CloneRefInWantDelegate { - fn prepare_ls_refs( - &mut self, - _server: &Capabilities, - _arguments: &mut Vec, - ) -> io::Result { - Ok(ls_refs::Action::Skip) + fn action(&mut self) -> io::Result { + Ok(RefsAction::Skip) } fn prepare_fetch( @@ -145,14 +141,10 @@ impl DelegateBlocking for LsRemoteDelegate { fn handshake_extra_parameters(&self) -> Vec<(String, Option)> { vec![("value-only".into(), None), ("key".into(), Some("value".into()))] } - fn prepare_ls_refs( - &mut self, - _server: &Capabilities, - _arguments: &mut Vec, - ) -> std::io::Result { + fn action(&mut self) -> std::io::Result { match self.abort_with.take() { Some(err) => Err(err), - None => Ok(ls_refs::Action::Continue), + None => Ok(RefsAction::Continue), } } fn prepare_fetch( diff --git a/gix-protocol/tests/protocol/fetch/v2.rs b/gix-protocol/tests/protocol/fetch/v2.rs index 58ba0fe0d97..97477401cb7 100644 --- a/gix-protocol/tests/protocol/fetch/v2.rs +++ b/gix-protocol/tests/protocol/fetch/v2.rs @@ -1,6 +1,6 @@ use bstr::ByteSlice; use gix_features::progress; -use gix_protocol::{handshake, ls_refs}; +use gix_protocol::handshake; use gix_transport::Protocol; use crate::fetch::{ @@ -143,7 +143,7 @@ async fn ls_remote_abort_in_prep_ls_refs() -> crate::Result { b"0044git-upload-pack does/not/matter\x00\x00version=2\x00value-only\x00key=value\x000000".as_bstr() ); match err { - Error::LsRefs(ls_refs::Error::Io(err)) => { + Error::Io(err) => { assert_eq!(err.kind(), std::io::ErrorKind::Other); assert_eq!(err.get_ref().expect("other error").to_string(), "hello world"); } diff --git a/gix/src/remote/connection/ref_map.rs b/gix/src/remote/connection/ref_map.rs index e149705dc6b..b0ee2881b59 100644 --- a/gix/src/remote/connection/ref_map.rs +++ b/gix/src/remote/connection/ref_map.rs @@ -156,16 +156,21 @@ where fetch_refspecs: self.remote.fetch_specs.clone(), extra_refspecs, }; - let ref_map = handshake - .fetch_or_extract_refmap( - progress, - &mut self.transport.inner, - self.remote.repo.config.user_agent_tuple(), - self.trace, - prefix_from_spec_as_filter_on_remote, - context, - ) + + let fetch_refmap = handshake.prepare_lsrefs_or_extract_refmap( + self.remote.repo.config.user_agent_tuple(), + prefix_from_spec_as_filter_on_remote, + context, + )?; + + #[cfg(feature = "async-network-client")] + let ref_map = fetch_refmap + .fetch_async(progress, &mut self.transport.inner, self.trace) .await?; + + #[cfg(feature = "blocking-network-client")] + let ref_map = fetch_refmap.fetch_blocking(progress, &mut self.transport.inner, self.trace)?; + self.handshake = Some(handshake); Ok(ref_map) }