Struct git_repository::remote::Connection
source · pub struct Connection<'a, 'repo, T, P> { /* private fields */ }
async-network-client
or blocking-network-client
only.Expand description
A type to represent an ongoing connection to a remote host, typically with the connection already established.
It can be used to perform a variety of operations with the remote without worrying about protocol details, much like a remote procedure call.
Implementations§
source§impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P>
impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P>
Builder
sourcepub fn with_credentials(self, helper: impl FnMut(Action) -> Result + 'a) -> Self
pub fn with_credentials(self, helper: impl FnMut(Action) -> Result + 'a) -> Self
Set a custom credentials callback to provide credentials if the remotes require authentication.
Otherwise we will use the git configuration to perform the same task as the git credential
helper program,
which is calling other helper programs in succession while resorting to a prompt to obtain credentials from the
user.
A custom function may also be used to prevent accessing resources with authentication.
Use the configured_credentials() method to obtain the implementation that would otherwise be used, which can be useful to proxy the default configuration and obtain information about the URLs to authenticate with.
sourcepub fn with_transport_options(self, config: Box<dyn Any>) -> Self
pub fn with_transport_options(self, config: Box<dyn Any>) -> Self
Provide configuration to be used before the first handshake is conducted.
It’s typically created by initializing it with Repository::transport_options()
, which
is also the default if this isn’t set explicitly. Note that all of the default configuration is created from git
configuration, which can also be manipulated through overrides to affect the default configuration.
Use this method to provide transport configuration with custom backend configuration that is not configurable by other means and custom to the application at hand.
source§impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P>
impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P>
Access
sourcepub fn configured_credentials(
&self,
url: Url
) -> Result<AuthenticateFn<'static>, Error>
pub fn configured_credentials(
&self,
url: Url
) -> Result<AuthenticateFn<'static>, Error>
A utility to return a function that will use this repository’s configuration to obtain credentials, similar to
what git credential
is doing.
It’s meant to be used by users of the with_credentials()
builder to gain access to the
default way of handling credentials, which they can call as fallback.
Examples found in repository?
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
async fn fetch_refs(
&mut self,
filter_by_prefix: bool,
extra_parameters: Vec<(String, Option<String>)>,
refspecs: &[git_refspec::RefSpec],
) -> Result<HandshakeWithRefs, Error> {
let mut credentials_storage;
let url = self.transport.to_url();
let authenticate = match self.authenticate.as_mut() {
Some(f) => f,
None => {
let url = self
.remote
.url(Direction::Fetch)
.map(ToOwned::to_owned)
.unwrap_or_else(|| git_url::parse(url.as_ref()).expect("valid URL to be provided by transport"));
credentials_storage = self.configured_credentials(url)?;
&mut credentials_storage
}
};
if self.transport_options.is_none() {
self.transport_options = self
.remote
.repo
.transport_options(url.as_ref(), self.remote.name().map(|n| n.as_bstr()))
.map_err(|err| Error::GatherTransportConfig {
source: err,
url: url.into_owned(),
})?;
}
if let Some(config) = self.transport_options.as_ref() {
self.transport.configure(&**config)?;
}
let mut outcome =
git_protocol::fetch::handshake(&mut self.transport, authenticate, extra_parameters, &mut self.progress)
.await?;
let refs = match outcome.refs.take() {
Some(refs) => refs,
None => {
let agent_feature = self.remote.repo.config.user_agent_tuple();
git_protocol::ls_refs(
&mut self.transport,
&outcome.capabilities,
move |_capabilities, arguments, features| {
features.push(agent_feature);
if filter_by_prefix {
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);
}
}
}
}
Ok(git_protocol::ls_refs::Action::Continue)
},
&mut self.progress,
)
.await?
}
};
Ok(HandshakeWithRefs { outcome, refs })
}
sourcepub fn remote(&self) -> &Remote<'repo>
pub fn remote(&self) -> &Remote<'repo>
Return the underlying remote that instantiate this connection.
sourcepub fn transport_mut(&mut self) -> &mut T
pub fn transport_mut(&mut self) -> &mut T
Provide a mutable transport to allow interacting with it according to its actual type.
Note that the caller should not call [configure()
][git_protocol::transport::client::TransportWithoutIO::configure()]
as we will call it automatically before performing the handshake. Instead, to bring in custom configuration,
call with_transport_options()
.
source§impl<'remote, 'repo, T, P> Connection<'remote, 'repo, T, P>where
T: Transport,
P: Progress,
impl<'remote, 'repo, T, P> Connection<'remote, 'repo, T, P>where
T: Transport,
P: Progress,
sourcepub fn ref_map(self, options: Options) -> Result<RefMap, Error>
pub fn ref_map(self, options: Options) -> Result<RefMap, Error>
List all references on the remote that have been filtered through our remote’s refspecs
for fetching.
This comes in the form of all matching tips on the remote and the object they point to, along with with the local tracking branch of these tips (if available).
Note that this doesn’t fetch the objects mentioned in the tips nor does it make any change to underlying repository.
Consumption
Due to management of the transport, it’s cleanest to only use it for a single interaction. Thus it’s consumed along with the connection.
Configuration
gitoxide.userAgent
is read to obtain the application user agent for git servers and for HTTP servers as well.
source§impl<'remote, 'repo, T, P> Connection<'remote, 'repo, T, P>where
T: Transport,
P: Progress,
impl<'remote, 'repo, T, P> Connection<'remote, 'repo, T, P>where
T: Transport,
P: Progress,
sourcepub fn prepare_fetch(
self,
options: Options
) -> Result<Prepare<'remote, 'repo, T, P>, Error>
pub fn prepare_fetch(
self,
options: Options
) -> Result<Prepare<'remote, 'repo, T, P>, Error>
Perform a handshake with the remote and obtain a ref-map with options
, and from there one
Note that at this point, the transport
should already be configured using the transport_mut()
method, as it will be consumed here.
From there additional properties of the fetch can be adjusted to override the defaults that are configured via git-config.
Async Experimental
Note that this implementation is currently limited correctly in blocking mode only as it relies on Drop semantics to close the connection should the fetch not be performed. Furthermore, there the code doing the fetch is inherently blocking and it’s not offloaded to a thread, making this call block the executor. It’s best to unblock it by placing it into its own thread or offload it should usage in an async context be truly required.
Examples found in repository?
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
pub fn fetch_only<P>(
&mut self,
progress: P,
should_interrupt: &std::sync::atomic::AtomicBool,
) -> Result<(Repository, crate::remote::fetch::Outcome), Error>
where
P: crate::Progress,
P::SubProgress: 'static,
{
use crate::remote;
use crate::{bstr::ByteVec, remote::fetch::RefLogMessage};
let repo = self
.repo
.as_mut()
.expect("user error: multiple calls are allowed only until it succeeds");
let remote_name = match self.remote_name.as_ref() {
Some(name) => name.to_owned(),
None => repo
.config
.resolved
.string_by_key("clone.defaultRemoteName")
.map(|n| remote::name::validated(n.into_owned()))
.unwrap_or_else(|| Ok("origin".into()))?,
};
let mut remote = repo
.remote_at(self.url.clone())?
.with_refspecs(
Some(format!("+refs/heads/*:refs/remotes/{remote_name}/*").as_str()),
remote::Direction::Fetch,
)
.expect("valid static spec");
let mut clone_fetch_tags = None;
if let Some(f) = self.configure_remote.as_mut() {
remote = f(remote).map_err(|err| Error::RemoteConfiguration(err))?;
} else {
clone_fetch_tags = remote::fetch::Tags::All.into();
}
let config = util::write_remote_to_local_config_file(&mut remote, remote_name.clone())?;
// Now we are free to apply remote configuration we don't want to be written to disk.
if let Some(fetch_tags) = clone_fetch_tags {
remote = remote.with_fetch_tags(fetch_tags);
}
// Add HEAD after the remote was written to config, we need it to know what to checkout later, and assure
// the ref that HEAD points to is present no matter what.
let head_refspec = git_refspec::parse(
format!("HEAD:refs/remotes/{remote_name}/HEAD").as_str().into(),
git_refspec::parse::Operation::Fetch,
)
.expect("valid")
.to_owned();
let pending_pack: remote::fetch::Prepare<'_, '_, _, _> =
remote.connect(remote::Direction::Fetch, progress)?.prepare_fetch({
let mut opts = self.fetch_options.clone();
if !opts.extra_refspecs.contains(&head_refspec) {
opts.extra_refspecs.push(head_refspec)
}
opts
})?;
if pending_pack.ref_map().object_hash != repo.object_hash() {
unimplemented!("configure repository to expect a different object hash as advertised by the server")
}
let reflog_message = {
let mut b = self.url.to_bstring();
b.insert_str(0, "clone: from ");
b
};
let outcome = pending_pack
.with_write_packed_refs_only(true)
.with_reflog_message(RefLogMessage::Override {
message: reflog_message.clone(),
})
.receive(should_interrupt)?;
util::replace_changed_local_config_file(repo, config);
util::update_head(
repo,
&outcome.ref_map.remote_refs,
reflog_message.as_ref(),
remote_name.as_ref(),
)?;
Ok((self.repo.take().expect("still present"), outcome))
}