Struct git_repository::Remote
source · pub struct Remote<'repo> { /* private fields */ }
Expand description
A remote which represents a way to interact with hosts for remote clones of the parent repository.
Implementations§
source§impl Remote<'_>
impl Remote<'_>
Builder methods
sourcepub fn push_url<Url, E>(self, url: Url) -> Result<Self, Error>where
Url: TryInto<Url, Error = E>,
Error: From<E>,
pub fn push_url<Url, E>(self, url: Url) -> Result<Self, Error>where
Url: TryInto<Url, Error = E>,
Error: From<E>,
Set the url
to be used when pushing data to a remote.
sourcepub fn push_url_without_url_rewrite<Url, E>(
self,
url: Url
) -> Result<Self, Error>where
Url: TryInto<Url, Error = E>,
Error: From<E>,
pub fn push_url_without_url_rewrite<Url, E>(
self,
url: Url
) -> Result<Self, Error>where
Url: TryInto<Url, Error = E>,
Error: From<E>,
Set the url
to be used when pushing data to a remote, without applying rewrite rules in case these could be faulty,
eliminating one failure mode.
Configure how tags should be handled when fetching from the remote.
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))
}
sourcepub fn with_refspecs<Spec>(
self,
specs: impl IntoIterator<Item = Spec>,
direction: Direction
) -> Result<Self, Error>where
Spec: AsRef<BStr>,
pub fn with_refspecs<Spec>(
self,
specs: impl IntoIterator<Item = Spec>,
direction: Direction
) -> Result<Self, Error>where
Spec: AsRef<BStr>,
Add specs
as refspecs for direction
to our list if they are unique, or ignore them otherwise.
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))
}
source§impl<'repo> Remote<'repo>
impl<'repo> Remote<'repo>
Establishing connections to remote hosts (without performing a git-handshake).
sourcepub fn to_connection_with_transport<T, P>(
&self,
transport: T,
progress: P
) -> Connection<'_, 'repo, T, P>where
T: Transport,
P: Progress,
Available on crate features async-network-client
or blocking-network-client
only.
pub fn to_connection_with_transport<T, P>(
&self,
transport: T,
progress: P
) -> Connection<'_, 'repo, T, P>where
T: Transport,
P: Progress,
async-network-client
or blocking-network-client
only.Create a new connection using transport
to communicate, with progress
to indicate changes.
Note that this method expects the transport
to be created by the user, which would involve the url()
.
It’s meant to be used when async operation is needed with runtimes of the user’s choice.
Examples found in repository?
75 76 77 78 79 80 81 82 83 84 85 86
pub async fn connect<P>(
&self,
direction: crate::remote::Direction,
progress: P,
) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>, P>, Error>
where
P: Progress,
{
let (url, version) = self.sanitized_url_and_version(direction)?;
let transport = git_protocol::transport::connect(url, version).await?;
Ok(self.to_connection_with_transport(transport, progress))
}
sourcepub fn connect<P>(
&self,
direction: Direction,
progress: P
) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>, P>, Error>where
P: Progress,
Available on (crate features async-network-client
or blocking-network-client
) and (crate features blocking-network-client
or async-network-client-async-std
) only.
pub fn connect<P>(
&self,
direction: Direction,
progress: P
) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>, P>, Error>where
P: Progress,
async-network-client
or blocking-network-client
) and (crate features blocking-network-client
or async-network-client-async-std
) only.Connect to the url suitable for direction
and return a handle through which operations can be performed.
Note that the protocol.version
configuration key affects the transport protocol used to connect,
with 2
being the default.
The transport used for connection can be configured via transport_mut().configure()
assuming the actually
used transport is well known. If that’s not the case, the transport can be created by hand and passed to
to_connection_with_transport().
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))
}
sourcepub fn sanitized_url_and_version(
&self,
direction: Direction
) -> Result<(Url, Protocol), Error>
Available on crate features async-network-client
or blocking-network-client
only.
pub fn sanitized_url_and_version(
&self,
direction: Direction
) -> Result<(Url, Protocol), Error>
async-network-client
or blocking-network-client
only.Produce the sanitized URL and protocol version to use as obtained by querying the repository configuration.
This can be useful when using custom transports to allow additional configuration.
Examples found in repository?
75 76 77 78 79 80 81 82 83 84 85 86
pub async fn connect<P>(
&self,
direction: crate::remote::Direction,
progress: P,
) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>, P>, Error>
where
P: Progress,
{
let (url, version) = self.sanitized_url_and_version(direction)?;
let transport = git_protocol::transport::connect(url, version).await?;
Ok(self.to_connection_with_transport(transport, progress))
}
source§impl Remote<'_>
impl Remote<'_>
Serialize into git-config.
sourcepub fn save_to(&self, config: &mut File<'static>) -> Result<(), Error>
pub fn save_to(&self, config: &mut File<'static>) -> Result<(), Error>
Save ourselves to the given config
if we are a named remote or fail otherwise.
Note that all sections named remote "<name>"
will be cleared of all values we are about to write,
and the last remote "<name>"
section will be containing all relevant values so that reloading the remote
from config
would yield the same in-memory state.
Examples found in repository?
104 105 106 107 108 109 110 111 112 113 114 115 116
pub fn save_as_to(
&mut self,
name: impl Into<BString>,
config: &mut git_config::File<'static>,
) -> Result<(), AsError> {
let name = crate::remote::name::validated(name)?;
let prev_name = self.name.take();
self.name = Some(name.into());
self.save_to(config).map_err(|err| {
self.name = prev_name;
err.into()
})
}
sourcepub fn save_as_to(
&mut self,
name: impl Into<BString>,
config: &mut File<'static>
) -> Result<(), AsError>
pub fn save_as_to(
&mut self,
name: impl Into<BString>,
config: &mut File<'static>
) -> Result<(), AsError>
Forcefully set our name to name
and write our state to config
similar to save_to()
.
Note that this sets a name for anonymous remotes, but overwrites the name for those who were named before. If this name is different from the current one, the git configuration will still contain the previous name, and the caller should account for that.
Examples found in repository?
15 16 17 18 19 20 21 22 23 24 25 26 27
pub fn write_remote_to_local_config_file(
remote: &mut crate::Remote<'_>,
remote_name: BString,
) -> Result<git_config::File<'static>, Error> {
let mut metadata = git_config::file::Metadata::from(git_config::Source::Local);
let config_path = remote.repo.git_dir().join("config");
metadata.path = Some(config_path.clone());
let mut config =
git_config::File::from_paths_metadata(Some(metadata), Default::default())?.expect("one file to load");
remote.save_as_to(remote_name, &mut config)?;
std::fs::write(config_path, config.to_bstring())?;
Ok(config)
}
source§impl<'repo> Remote<'repo>
impl<'repo> Remote<'repo>
Access
sourcepub fn name(&self) -> Option<&Name<'static>>
pub fn name(&self) -> Option<&Name<'static>>
Return the name of this remote or None
if it wasn’t persisted to disk yet.
Examples found in repository?
33 34 35 36 37 38 39 40 41 42 43 44 45 46 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
pub fn save_to(&self, config: &mut git_config::File<'static>) -> Result<(), Error> {
fn as_key(name: &str) -> git_config::parse::section::Key<'_> {
name.try_into().expect("valid")
}
let name = self.name().ok_or_else(|| Error::NameMissing {
url: self
.url
.as_ref()
.or(self.push_url.as_ref())
.expect("one url is always set")
.to_owned(),
})?;
if let Some(section_ids) = config.sections_and_ids_by_name("remote").map(|it| {
it.filter_map(|(s, id)| (s.header().subsection_name() == Some(name.as_bstr())).then(|| id))
.collect::<Vec<_>>()
}) {
let mut sections_to_remove = Vec::new();
const KEYS_TO_REMOVE: &[&str] = &["url", "pushurl", "fetch", "push", "tagOpt"];
for id in section_ids {
let mut section = config.section_mut_by_id(id).expect("just queried");
let was_empty = section.num_values() == 0;
for key in KEYS_TO_REMOVE {
while section.remove(key).is_some() {}
}
let is_empty_after_deletions_of_values_to_be_written = section.num_values() == 0;
if !was_empty && is_empty_after_deletions_of_values_to_be_written {
sections_to_remove.push(id);
}
}
for id in sections_to_remove {
config.remove_section_by_id(id);
}
}
let mut section = config
.section_mut_or_create_new("remote", Some(name.as_ref()))
.expect("section name is validated and 'remote' is acceptable");
if let Some(url) = self.url.as_ref() {
section.push(as_key("url"), Some(url.to_bstring().as_ref()))
}
if let Some(url) = self.push_url.as_ref() {
section.push(as_key("pushurl"), Some(url.to_bstring().as_ref()))
}
if self.fetch_tags != Default::default() {
section.push(
as_key("tagOpt"),
BStr::new(match self.fetch_tags {
remote::fetch::Tags::All => "--tags",
remote::fetch::Tags::None => "--no-tags",
remote::fetch::Tags::Included => unreachable!("BUG: the default shouldn't be written and we try"),
})
.into(),
)
}
for (key, spec) in self
.fetch_specs
.iter()
.map(|spec| ("fetch", spec))
.chain(self.push_specs.iter().map(|spec| ("push", spec)))
{
section.push(as_key(key), Some(spec.to_ref().to_bstring().as_ref()))
}
Ok(())
}
More examples
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 repo(&self) -> &'repo Repository
pub fn repo(&self) -> &'repo Repository
Return our repository reference.
sourcepub fn refspecs(&self, direction: Direction) -> &[RefSpec]
pub fn refspecs(&self, direction: Direction) -> &[RefSpec]
Return the set of ref-specs used for direction
, which may be empty, in order of occurrence in the configuration.
Examples found in repository?
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
pub async fn prepare_fetch(
mut self,
options: ref_map::Options,
) -> Result<Prepare<'remote, 'repo, T, P>, prepare::Error> {
if self.remote.refspecs(remote::Direction::Fetch).is_empty() {
return Err(prepare::Error::MissingRefSpecs);
}
let ref_map = self.ref_map_inner(options).await?;
Ok(Prepare {
con: Some(self),
ref_map,
dry_run: DryRun::No,
reflog_message: None,
write_packed_refs: WritePackedRefs::Never,
})
}
More examples
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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 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
pub async fn receive(mut self, should_interrupt: &AtomicBool) -> Result<Outcome, Error> {
let mut con = self.con.take().expect("receive() can only be called once");
let handshake = &self.ref_map.handshake;
let protocol_version = handshake.server_protocol_version;
let fetch = git_protocol::Command::Fetch;
let progress = &mut con.progress;
let repo = con.remote.repo;
let fetch_features = {
let mut f = fetch.default_features(protocol_version, &handshake.capabilities);
f.push(repo.config.user_agent_tuple());
f
};
git_protocol::fetch::Response::check_required_features(protocol_version, &fetch_features)?;
let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all");
let mut arguments = git_protocol::fetch::Arguments::new(protocol_version, fetch_features);
if matches!(con.remote.fetch_tags, crate::remote::fetch::Tags::Included) {
if !arguments.can_use_include_tag() {
unimplemented!("we expect servers to support 'include-tag', otherwise we have to implement another pass to fetch attached tags separately");
}
arguments.use_include_tag();
}
let mut previous_response = None::<git_protocol::fetch::Response>;
let mut round = 1;
if self.ref_map.object_hash != repo.object_hash() {
return Err(Error::IncompatibleObjectHash {
local: repo.object_hash(),
remote: self.ref_map.object_hash,
});
}
let reader = 'negotiation: loop {
progress.step();
progress.set_name(format!("negotiate (round {})", round));
let is_done = match negotiate::one_round(
negotiate::Algorithm::Naive,
round,
repo,
&self.ref_map,
con.remote.fetch_tags,
&mut arguments,
previous_response.as_ref(),
) {
Ok(_) if arguments.is_empty() => {
git_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
let update_refs = refs::update(
repo,
self.reflog_message
.take()
.unwrap_or_else(|| RefLogMessage::Prefixed { action: "fetch".into() }),
&self.ref_map.mappings,
con.remote.refspecs(remote::Direction::Fetch),
&self.ref_map.extra_refspecs,
con.remote.fetch_tags,
self.dry_run,
self.write_packed_refs,
)?;
return Ok(Outcome {
ref_map: std::mem::take(&mut self.ref_map),
status: Status::NoPackReceived { update_refs },
});
}
Ok(is_done) => is_done,
Err(err) => {
git_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
return Err(err.into());
}
};
round += 1;
let mut reader = arguments.send(&mut con.transport, is_done).await?;
if sideband_all {
setup_remote_progress(progress, &mut reader);
}
let response = git_protocol::fetch::Response::from_line_reader(protocol_version, &mut reader).await?;
if response.has_pack() {
progress.step();
progress.set_name("receiving pack");
if !sideband_all {
setup_remote_progress(progress, &mut reader);
}
break 'negotiation reader;
} else {
previous_response = Some(response);
}
};
let options = git_pack::bundle::write::Options {
thread_limit: config::index_threads(repo)?,
index_version: config::pack_index_version(repo)?,
iteration_mode: git_pack::data::input::Mode::Verify,
object_hash: con.remote.repo.object_hash(),
};
let mut write_pack_bundle = if matches!(self.dry_run, fetch::DryRun::No) {
Some(git_pack::Bundle::write_to_directory(
#[cfg(feature = "async-network-client")]
{
git_protocol::futures_lite::io::BlockOn::new(reader)
},
#[cfg(not(feature = "async-network-client"))]
{
reader
},
Some(repo.objects.store_ref().path().join("pack")),
con.progress,
should_interrupt,
Some(Box::new({
let repo = repo.clone();
move |oid, buf| repo.objects.find(oid, buf).ok()
})),
options,
)?)
} else {
drop(reader);
None
};
if matches!(protocol_version, git_protocol::transport::Protocol::V2) {
git_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
}
let update_refs = refs::update(
repo,
self.reflog_message
.take()
.unwrap_or_else(|| RefLogMessage::Prefixed { action: "fetch".into() }),
&self.ref_map.mappings,
con.remote.refspecs(remote::Direction::Fetch),
&self.ref_map.extra_refspecs,
con.remote.fetch_tags,
self.dry_run,
self.write_packed_refs,
)?;
if let Some(bundle) = write_pack_bundle.as_mut() {
if !update_refs.edits.is_empty() || bundle.index.num_objects == 0 {
if let Some(path) = bundle.keep_path.take() {
std::fs::remove_file(&path).map_err(|err| Error::RemovePackKeepFile { path, source: err })?;
}
}
}
Ok(Outcome {
ref_map: std::mem::take(&mut self.ref_map),
status: match write_pack_bundle {
Some(write_pack_bundle) => Status::Change {
write_pack_bundle,
update_refs,
},
None => Status::DryRun { update_refs },
},
})
}
Return how we handle tags when fetching the remote.
sourcepub fn url(&self, direction: Direction) -> Option<&Url>
pub fn url(&self, direction: Direction) -> Option<&Url>
Return the url used for the given direction
with rewrites from url.<base>.insteadOf|pushInsteadOf
, unless the instance
was created with one of the _without_url_rewrite()
methods.
For pushing, this is the remote.<name>.pushUrl
or the remote.<name>.url
used for fetching, and for fetching it’s
the remote.<name>.url
.
Note that it’s possible to only have the push url set, in which case there will be no way to fetch from the remote as
the push-url isn’t used for that.
Examples found in repository?
More examples
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 136 137 138 139 140 141 142 143 144 145 146 147 148 149
pub fn sanitized_url_and_version(
&self,
direction: crate::remote::Direction,
) -> Result<(git_url::Url, git_protocol::transport::Protocol), Error> {
fn sanitize(mut url: git_url::Url) -> Result<git_url::Url, Error> {
if url.scheme == git_url::Scheme::File {
let mut dir = git_path::to_native_path_on_windows(url.path.as_ref());
let kind = git_discover::is_git(dir.as_ref())
.or_else(|_| {
dir.to_mut().push(git_discover::DOT_GIT_DIR);
git_discover::is_git(dir.as_ref())
})
.map_err(|err| Error::FileUrl {
source: err.into(),
url: url.clone(),
})?;
let (git_dir, _work_dir) = git_discover::repository::Path::from_dot_git_dir(
dir.clone().into_owned(),
kind,
std::env::current_dir()?,
)
.ok_or_else(|| Error::InvalidRemoteRepositoryPath {
directory: dir.into_owned(),
})?
.into_repository_and_work_tree_directories();
url.path = git_path::into_bstr(git_dir).into_owned();
}
Ok(url)
}
use git_protocol::transport::Protocol;
let version = self
.repo
.config
.resolved
.integer("protocol", None, "version")
.unwrap_or(Ok(2))
.map_err(|err| Error::UnknownProtocol { given: err.input })
.and_then(|num| {
Ok(match num {
1 => Protocol::V1,
2 => Protocol::V2,
num => {
return Err(Error::UnknownProtocol {
given: num.to_string().into(),
})
}
})
})?;
let url = self.url(direction).ok_or(Error::MissingUrl { direction })?.to_owned();
if !self.repo.config.url_scheme()?.allow(&url.scheme) {
return Err(Error::ProtocolDenied {
url: url.to_bstring(),
scheme: url.scheme,
});
}
Ok((sanitize(url)?, version))
}
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 })
}
source§impl Remote<'_>
impl Remote<'_>
Modification
sourcepub fn rewrite_urls(&mut self) -> Result<&mut Self, Error>
pub fn rewrite_urls(&mut self) -> Result<&mut Self, Error>
Read url.<base>.insteadOf|pushInsteadOf
configuration variables and apply them to our urls, changing them in place.
This happens only once, and one if them may be changed even when reporting an error. If both urls fail, only the first error (for fetch urls) is reported.
sourcepub fn replace_refspecs<Spec>(
&mut self,
specs: impl IntoIterator<Item = Spec>,
direction: Direction
) -> Result<(), Error>where
Spec: AsRef<BStr>,
pub fn replace_refspecs<Spec>(
&mut self,
specs: impl IntoIterator<Item = Spec>,
direction: Direction
) -> Result<(), Error>where
Spec: AsRef<BStr>,
Replace all currently set refspecs, typically from configuration, with the given specs
for direction
,
or None
if one of the input specs could not be parsed.