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, } } }