Skip to content

Commit 6d05562

Browse files
committed
Now go all in and make it work with the new model
Features: * make `fetch_only()` available in async mode
1 parent d72ff34 commit 6d05562

File tree

13 files changed

+532
-292
lines changed

13 files changed

+532
-292
lines changed

crate-status.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ Check out the [performance discussion][gix-traverse-performance] as well.
233233
* [x] nested traversal
234234
* **commits**
235235
* [x] ancestor graph traversal similar to `git revlog`
236+
* [ ] `commitgraph` support
236237
* [x] API documentation
237238
* [ ] Examples
238239

@@ -671,7 +672,10 @@ See its [README.md](https://github.com/Byron/gitoxide/blob/main/gix-lock/README.
671672
* [x] shallow (remains shallow, options to adjust shallow boundary)
672673
* [ ] a way to auto-explode small packs to avoid them to pile up
673674
* [x] 'ref-in-want'
674-
* [ ] standard negotiation algorithms (right now we only have a 'naive' one)
675+
* [ ] 'wanted-ref'
676+
* [ ] standard negotiation algorithms `consecutive`, `skipping` and `noop`.
677+
* [ ] a more efficient way to deal [with common `have`](https://github.com/git/git/blob/9e49351c3060e1fa6e0d2de64505b7becf157f28/fetch-pack.c#L525)
678+
during negotiation - we would submit known non-common `haves` each round in stateless transports whereas git prunes the set to known common ones.
675679
* [ ] push
676680
* [x] ls-refs
677681
* [x] ls-refs with ref-spec filter

gix/src/clone/fetch/mod.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ impl PrepareFetch {
4444
/// it was newly initialized.
4545
///
4646
/// Note that all data we created will be removed once this instance drops if the operation wasn't successful.
47-
pub fn fetch_only<P>(
47+
///
48+
/// ### Note for users of `async`
49+
///
50+
/// Even though
51+
#[gix_protocol::maybe_async::maybe_async]
52+
pub async fn fetch_only<P>(
4853
&mut self,
4954
mut progress: P,
5055
should_interrupt: &std::sync::atomic::AtomicBool,
@@ -101,17 +106,19 @@ impl PrepareFetch {
101106
.expect("valid")
102107
.to_owned();
103108
let pending_pack: remote::fetch::Prepare<'_, '_, _> = {
104-
let mut connection = remote.connect(remote::Direction::Fetch)?;
109+
let mut connection = remote.connect(remote::Direction::Fetch).await?;
105110
if let Some(f) = self.configure_connection.as_mut() {
106111
f(&mut connection).map_err(|err| Error::RemoteConnection(err))?;
107112
}
108-
connection.prepare_fetch(&mut progress, {
109-
let mut opts = self.fetch_options.clone();
110-
if !opts.extra_refspecs.contains(&head_refspec) {
111-
opts.extra_refspecs.push(head_refspec)
112-
}
113-
opts
114-
})?
113+
connection
114+
.prepare_fetch(&mut progress, {
115+
let mut opts = self.fetch_options.clone();
116+
if !opts.extra_refspecs.contains(&head_refspec) {
117+
opts.extra_refspecs.push(head_refspec)
118+
}
119+
opts
120+
})
121+
.await?
115122
};
116123
if pending_pack.ref_map().object_hash != repo.object_hash() {
117124
unimplemented!("configure repository to expect a different object hash as advertised by the server")
@@ -127,7 +134,8 @@ impl PrepareFetch {
127134
message: reflog_message.clone(),
128135
})
129136
.with_shallow(self.shallow.clone())
130-
.receive(progress, should_interrupt)?;
137+
.receive(progress, should_interrupt)
138+
.await?;
131139

132140
util::append_config_to_repo_config(repo, config);
133141
util::update_head(
@@ -141,6 +149,7 @@ impl PrepareFetch {
141149
}
142150

143151
/// Similar to [`fetch_only()`][Self::fetch_only()`], but passes ownership to a utility type to configure a checkout operation.
152+
#[cfg(feature = "blocking-network-client")]
144153
pub fn fetch_then_checkout<P>(
145154
&mut self,
146155
progress: P,
@@ -155,5 +164,4 @@ impl PrepareFetch {
155164
}
156165
}
157166

158-
#[cfg(feature = "blocking-network-client")]
159167
mod util;

gix/src/clone/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ mod access_feat {
160160
}
161161

162162
///
163-
#[cfg(feature = "blocking-network-client")]
163+
#[cfg(any(feature = "async-network-client-async-std", feature = "blocking-network-client"))]
164164
pub mod fetch;
165165

166166
///

gix/src/config/tree/keys.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,8 @@ pub mod validate {
511511
gix_config::Integer::try_from(value)?
512512
.to_decimal()
513513
.ok_or_else(|| format!("integer {value} cannot be represented as `usize`"))?,
514-
)?;
514+
)
515+
.map_err(|_| "cannot use sign for unsigned integer")?;
515516
Ok(())
516517
}
517518
}

gix/src/config/tree/sections/protocol.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::{
66
impl Protocol {
77
/// The `protocol.allow` key.
88
pub const ALLOW: Allow = Allow::new_with_validate("allow", &config::Tree::PROTOCOL, validate::Allow);
9+
/// The `protocol.version` key.
10+
pub const VERSION: Version = Version::new_with_validate("version", &config::Tree::PROTOCOL, validate::Version);
911

1012
/// The `protocol.<name>` subsection
1113
pub const NAME_PARAMETER: NameParameter = NameParameter;
@@ -14,6 +16,9 @@ impl Protocol {
1416
/// The `protocol.allow` key type.
1517
pub type Allow = keys::Any<validate::Allow>;
1618

19+
/// The `protocol.version` key.
20+
pub type Version = keys::Any<validate::Version>;
21+
1722
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
1823
mod allow {
1924
use std::borrow::Cow;
@@ -39,7 +44,7 @@ mod allow {
3944
pub struct NameParameter;
4045

4146
impl NameParameter {
42-
/// The `credential.<url>.helper` key.
47+
/// The `protocol.<name>.allow` key.
4348
pub const ALLOW: Allow = Allow::new_with_validate("allow", &Protocol::NAME_PARAMETER, validate::Allow);
4449
}
4550

@@ -63,14 +68,46 @@ impl Section for Protocol {
6368
}
6469

6570
fn keys(&self) -> &[&dyn Key] {
66-
&[&Self::ALLOW]
71+
&[&Self::ALLOW, &Self::VERSION]
6772
}
6873

6974
fn sub_sections(&self) -> &[&dyn Section] {
7075
&[&Self::NAME_PARAMETER]
7176
}
7277
}
7378

79+
mod key_impls {
80+
impl super::Version {
81+
/// Convert `value` into the corresponding protocol version, possibly applying the correct default.
82+
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
83+
pub fn try_into_protocol_version(
84+
&'static self,
85+
value: Option<Result<i64, gix_config::value::Error>>,
86+
) -> Result<gix_protocol::transport::Protocol, crate::config::key::GenericErrorWithValue> {
87+
let value = match value {
88+
None => return Ok(gix_protocol::transport::Protocol::V2),
89+
Some(v) => v,
90+
};
91+
Ok(match value {
92+
Ok(0) => gix_protocol::transport::Protocol::V0,
93+
Ok(1) => gix_protocol::transport::Protocol::V1,
94+
Ok(2) => gix_protocol::transport::Protocol::V2,
95+
Ok(other) => {
96+
return Err(crate::config::key::GenericErrorWithValue::from_value(
97+
self,
98+
other.to_string().into(),
99+
))
100+
}
101+
Err(err) => {
102+
return Err(
103+
crate::config::key::GenericErrorWithValue::from_value(self, "unknown".into()).with_source(err),
104+
)
105+
}
106+
})
107+
}
108+
}
109+
}
110+
74111
mod validate {
75112
use crate::{bstr::BStr, config::tree::keys};
76113

@@ -82,4 +119,17 @@ mod validate {
82119
Ok(())
83120
}
84121
}
122+
123+
pub struct Version;
124+
impl keys::Validate for Version {
125+
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
126+
let value = gix_config::Integer::try_from(value)?
127+
.to_decimal()
128+
.ok_or_else(|| format!("integer {value} cannot be represented as integer"))?;
129+
match value {
130+
0 | 1 | 2 => Ok(()),
131+
_ => Err(format!("protocol version {value} is unknown").into()),
132+
}
133+
}
134+
}
85135
}

gix/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub use gix_ignore as ignore;
8181
#[doc(inline)]
8282
pub use gix_index as index;
8383
pub use gix_lock as lock;
84+
pub use gix_negotiate as negotiate;
8485
pub use gix_object as objs;
8586
pub use gix_object::bstr;
8687
pub use gix_odb as odb;

gix/src/remote/connect.rs

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ mod error {
2424
Connect(#[from] gix_protocol::transport::client::connect::Error),
2525
#[error("The {} url was missing - don't know where to establish a connection to", direction.as_str())]
2626
MissingUrl { direction: remote::Direction },
27-
#[error("Protocol named {given:?} is not a valid protocol. Choose between 1 and 2")]
28-
UnknownProtocol { given: BString },
27+
#[error("The given protocol version was invalid. Choose between 1 and 2")]
28+
UnknownProtocol { source: config::key::GenericErrorWithValue },
2929
#[error("Could not verify that \"{}\" url is a valid git directory before attempting to use it", url.to_bstring())]
3030
FileUrl {
3131
source: Box<gix_discover::is_git::Error>,
@@ -128,25 +128,9 @@ impl<'repo> Remote<'repo> {
128128
Ok(url)
129129
}
130130

131-
use gix_protocol::transport::Protocol;
132-
let version = self
133-
.repo
134-
.config
135-
.resolved
136-
.integer("protocol", None, "version")
137-
.unwrap_or(Ok(2))
138-
.map_err(|err| Error::UnknownProtocol { given: err.input })
139-
.and_then(|num| {
140-
Ok(match num {
141-
1 => Protocol::V1,
142-
2 => Protocol::V2,
143-
num => {
144-
return Err(Error::UnknownProtocol {
145-
given: num.to_string().into(),
146-
})
147-
}
148-
})
149-
})?;
131+
let version = crate::config::tree::Protocol::VERSION
132+
.try_into_protocol_version(self.repo.config.resolved.integer("protocol", None, "version"))
133+
.map_err(|err| Error::UnknownProtocol { source: err })?;
150134

151135
let url = self.url(direction).ok_or(Error::MissingUrl { direction })?.to_owned();
152136
if !self.repo.config.url_scheme()?.allow(&url.scheme) {

gix/src/remote/connection/fetch/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,17 @@ impl RefLogMessage {
4343
pub enum Status {
4444
/// Nothing changed as the remote didn't have anything new compared to our tracking branches, thus no pack was received
4545
/// and no new object was added.
46+
///
47+
/// As we could determine that nothing changed without remote interaction, there was no negotiation at all.
4648
NoPackReceived {
4749
/// However, depending on the refspecs, references might have been updated nonetheless to point to objects as
4850
/// reported by the remote.
4951
update_refs: refs::update::Outcome,
5052
},
5153
/// There was at least one tip with a new object which we received.
5254
Change {
55+
/// The number of rounds it took to minimize the pack to contain only the objects we don't have.
56+
negotiation_rounds: usize,
5357
/// Information collected while writing the pack and its index.
5458
write_pack_bundle: gix_pack::bundle::write::Outcome,
5559
/// Information collected while updating references.
@@ -58,6 +62,8 @@ pub enum Status {
5862
/// A dry run was performed which leaves the local repository without any change
5963
/// nor will a pack have been received.
6064
DryRun {
65+
/// The number of rounds it took to minimize the *would-be-sent*-pack to contain only the objects we don't have.
66+
negotiation_rounds: usize,
6167
/// Information about what updates to refs would have been done.
6268
update_refs: refs::update::Outcome,
6369
},

0 commit comments

Comments
 (0)