use gix_refspec::RefSpec;
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
use crate::types::RemoteDetached;
use crate::{Remote, bstr::BStr, remote};
/// Access
impl<'repo> Remote<'repo> {
/// Return the name of this remote or `None` if it wasn't persisted to disk yet.
pub fn name(&self) -> Option<&remote::Name<'static>> {
self.name.as_ref()
}
/// Return our repository reference.
pub fn repo(&self) -> &'repo crate::Repository {
self.repo
}
/// Return the set of ref-specs used for `direction`, which may be empty, in order of occurrence in the configuration.
pub fn refspecs(&self, direction: remote::Direction) -> &[RefSpec] {
match direction {
remote::Direction::Fetch => &self.fetch_specs,
remote::Direction::Push => &self.push_specs,
}
}
/// Return how we handle tags when fetching the remote.
pub fn fetch_tags(&self) -> remote::fetch::Tags {
self.fetch_tags
}
/// Return the first url used for the given `direction` with rewrites from `url..insteadOf|pushInsteadOf`, unless the instance
/// was created with one of the `_without_url_rewrite()` methods.
/// See [`urls()`](Self::urls()) for how rewrite rules differ between fetch URLs, explicit push URLs, and push fallbacks.
/// For pushing, this is the first `remote..pushUrl` or the first `remote..url` used for fetching, and for
/// fetching it's the first `remote..url`. Unlike `git remote get-url`, a missing fetch URL doesn't fall back to a
/// symbolic remote name, but a URL-shaped remote name may itself be used as the fallback 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.
pub fn url(&self, direction: remote::Direction) -> Option<&gix_url::Url> {
self.urls(direction).next()
}
/// Return all urls used for the given `direction` with rewrites from `url..insteadOf|pushInsteadOf`, unless the
/// instance was created with one of the `_without_url_rewrite()` methods.
///
/// Fetch URLs are rewritten with `url..insteadOf`. Explicit `remote..pushUrl` values are also rewritten with
/// `insteadOf`, and `pushInsteadOf` is ignored for them. If no explicit push URL is configured, the fetch URLs are used
/// as push fallbacks: matching `pushInsteadOf` rules take precedence, with `insteadOf` used when none match.
///
/// Values are returned in configuration order.
pub fn urls(&self, direction: remote::Direction) -> impl Iterator- + '_ {
let (urls, aliases) = self.urls_and_aliases(direction);
debug_assert_eq!(
urls.len(),
aliases.len(),
"each URL should have a corresponding rewrite slot"
);
urls.iter()
.zip(aliases)
.map(|(url, alias)| alias.as_ref().unwrap_or(url))
}
fn urls_and_aliases(&self, direction: remote::Direction) -> (&[gix_url::Url], &[Option]) {
match direction {
remote::Direction::Fetch => (&self.urls, &self.url_aliases),
remote::Direction::Push if self.push_urls.is_empty() => (&self.urls, &self.url_push_aliases),
remote::Direction::Push => (&self.push_urls, &self.push_url_aliases),
}
}
/// Return a clone of this remote without its repository reference.
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
pub(crate) fn detached(&self) -> RemoteDetached {
self.clone().into()
}
}
/// Access
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
impl RemoteDetached {
/// Return the name of this remote or `None` if it wasn't persisted to disk yet.
pub(crate) fn name(&self) -> Option<&remote::Name<'static>> {
self.name.as_ref()
}
/// Return the set of ref-specs used for fetching, which may be empty, in order of occurrence in the configuration.
pub(crate) fn fetch_refspecs(&self) -> &[RefSpec] {
&self.fetch_specs
}
}
/// Modification
impl Remote<'_> {
/// Re-read `url..insteadOf|pushInsteadOf` and recompute the effective URLs returned by [`url()`](Self::url()) and
/// [`urls()`](Self::urls()). This may be called repeatedly to refresh rewrite rules after configuration changes.
///
/// Every URL is attempted non-destructively: successful rewrites remain effective if another rewritten URL is malformed,
/// while a failed entry keeps using its original URL. The first error is returned in fetch, push-fallback, explicit-push
/// order. See [`urls()`](Self::urls()) for which rules apply to each category.
pub fn rewrite_urls(&mut self) -> Result<&mut Self, remote::init::Error> {
let (url_aliases, url_err) =
remote::init::rewrite_url_aliases_non_destructive(&self.repo.config, &self.urls, remote::Direction::Fetch);
self.url_aliases = url_aliases;
let url_push_err = if self.push_urls.is_empty() {
let (url_push_aliases, err) = remote::init::rewrite_url_aliases_with_fallback_non_destructive(
&self.repo.config,
&self.urls,
remote::Direction::Push,
remote::Direction::Fetch,
);
self.url_push_aliases = url_push_aliases;
err
} else {
self.url_push_aliases = vec![None; self.urls.len()];
None
};
let (push_url_aliases, push_url_err) = remote::init::rewrite_url_aliases_non_destructive_with_error_kind(
&self.repo.config,
&self.push_urls,
remote::Direction::Fetch,
remote::Direction::Push,
);
self.push_url_aliases = push_url_aliases;
url_err
.or(url_push_err)
.or(push_url_err)
.map(Err::<&mut Self, _>)
.transpose()?;
Ok(self)
}
/// 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.
pub fn replace_refspecs(
&mut self,
specs: impl IntoIterator
- ,
direction: remote::Direction,
) -> Result<(), gix_refspec::parse::Error>
where
Spec: AsRef,
{
use remote::Direction::*;
let specs: Vec<_> = specs
.into_iter()
.map(|spec| {
gix_refspec::parse(
spec.as_ref(),
match direction {
Push => gix_refspec::parse::Operation::Push,
Fetch => gix_refspec::parse::Operation::Fetch,
},
)
.map(|url| url.to_owned())
})
.collect::>()?;
let dst = match direction {
Push => &mut self.push_specs,
Fetch => &mut self.fetch_specs,
};
*dst = specs;
Ok(())
}
}
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
impl From> for RemoteDetached {
fn from(
Remote {
name,
urls,
url_aliases,
url_push_aliases: _,
fetch_specs,
fetch_tags,
push_urls: _,
push_url_aliases: _,
push_specs: _,
repo: _,
}: Remote<'_>,
) -> Self {
RemoteDetached {
name,
urls,
url_aliases,
fetch_specs,
fetch_tags,
}
}
}