use gix_refspec::RefSpec; #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]use crate::types::RemoteDetached;use crate::{Remote, bstr::BStr, remote}; /// Accessimpl<'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.<base>.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.<name>.pushUrl` or the first `remote.<name>.url` used for fetching, and for /// fetching it's the first `remote.<name>.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.<base>.insteadOf|pushInsteadOf`, unless the /// instance was created with one of the `_without_url_rewrite()` methods. /// /// Fetch URLs are rewritten with `url.<base>.insteadOf`. Explicit `remote.<name>.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<Item = &gix_url::Url> + '_ { 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<gix_url::Url>]) { 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 }} /// Modificationimpl Remote<'_> { /// Re-read `url.<base>.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<Spec>( &mut self, specs: impl IntoIterator<Item = Spec>, direction: remote::Direction, ) -> Result<(), gix_refspec::parse::Error> where Spec: AsRef<BStr>, { 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::<Result<_, _>>()?; 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<Remote<'_>> 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, } }}
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.<base>.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.<name>.pushUrl` or the first `remote.<name>.url` used for fetching, and for
/// fetching it's the first `remote.<name>.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.<base>.insteadOf|pushInsteadOf`, unless the
/// instance was created with one of the `_without_url_rewrite()` methods.
///
/// Fetch URLs are rewritten with `url.<base>.insteadOf`. Explicit `remote.<name>.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<Item = &gix_url::Url> + '_ {
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<gix_url::Url>]) {
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.<base>.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<Spec>(
&mut self,
specs: impl IntoIterator<Item = Spec>,
direction: remote::Direction,
) -> Result<(), gix_refspec::parse::Error>
where
Spec: AsRef<BStr>,
{
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::<Result<_, _>>()?;
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<Remote<'_>> 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,
}
}
}