diff options
Diffstat (limited to 'vendor/gix/src/repository/config/mod.rs')
-rw-r--r-- | vendor/gix/src/repository/config/mod.rs | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/vendor/gix/src/repository/config/mod.rs b/vendor/gix/src/repository/config/mod.rs new file mode 100644 index 000000000..92b2618cc --- /dev/null +++ b/vendor/gix/src/repository/config/mod.rs @@ -0,0 +1,191 @@ +use std::collections::BTreeSet; + +use crate::{bstr::ByteSlice, config}; + +/// General Configuration +impl crate::Repository { + /// Return a snapshot of the configuration as seen upon opening the repository. + pub fn config_snapshot(&self) -> config::Snapshot<'_> { + config::Snapshot { repo: self } + } + + /// Return a mutable snapshot of the configuration as seen upon opening the repository, starting a transaction. + /// When the returned instance is dropped, it is applied in full, even if the reason for the drop is an error. + /// + /// Note that changes to the configuration are in-memory only and are observed only the this instance + /// of the [`Repository`][crate::Repository]. + pub fn config_snapshot_mut(&mut self) -> config::SnapshotMut<'_> { + let config = self.config.resolved.as_ref().clone(); + config::SnapshotMut { + repo: Some(self), + config, + } + } + + /// The options used to open the repository. + pub fn open_options(&self) -> &crate::open::Options { + &self.options + } + + /// Obtain options for use when connecting via `ssh`. + #[cfg(feature = "blocking-network-client")] + pub fn ssh_connect_options( + &self, + ) -> Result<gix_protocol::transport::client::ssh::connect::Options, config::ssh_connect_options::Error> { + use crate::config::{ + cache::util::ApplyLeniency, + tree::{gitoxide, Core, Ssh}, + }; + + let config = &self.config.resolved; + let mut trusted = self.filter_config_section(); + let mut fallback_active = false; + let ssh_command = config + .string_filter("core", None, Core::SSH_COMMAND.name, &mut trusted) + .or_else(|| { + fallback_active = true; + config.string_filter( + "gitoxide", + Some("ssh".into()), + gitoxide::Ssh::COMMAND_WITHOUT_SHELL_FALLBACK.name, + &mut trusted, + ) + }) + .map(|cmd| gix_path::from_bstr(cmd).into_owned().into()); + let opts = gix_protocol::transport::client::ssh::connect::Options { + disallow_shell: fallback_active, + command: ssh_command, + kind: config + .string_filter_by_key("ssh.variant", &mut trusted) + .and_then(|variant| Ssh::VARIANT.try_into_variant(variant).transpose()) + .transpose() + .with_leniency(self.options.lenient_config)?, + }; + Ok(opts) + } + + /// The kind of object hash the repository is configured to use. + pub fn object_hash(&self) -> gix_hash::Kind { + self.config.object_hash + } +} + +#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] +mod transport; + +mod remote { + use std::{borrow::Cow, collections::BTreeSet}; + + use crate::{bstr::ByteSlice, remote}; + + impl crate::Repository { + /// Returns a sorted list unique of symbolic names of remotes that + /// we deem [trustworthy][crate::open::Options::filter_config_section()]. + // TODO: Use `remote::Name` here + pub fn remote_names(&self) -> BTreeSet<&str> { + self.subsection_names_of("remote") + } + + /// Obtain the branch-independent name for a remote for use in the given `direction`, or `None` if it could not be determined. + /// + /// For _fetching_, use the only configured remote, or default to `origin` if it exists. + /// For _pushing_, use the `remote.pushDefault` trusted configuration key, or fall back to the rules for _fetching_. + /// + /// # Notes + /// + /// It's up to the caller to determine what to do if the current `head` is unborn or detached. + // TODO: use remote::Name here + pub fn remote_default_name(&self, direction: remote::Direction) -> Option<Cow<'_, str>> { + let name = (direction == remote::Direction::Push) + .then(|| { + self.config + .resolved + .string_filter("remote", None, "pushDefault", &mut self.filter_config_section()) + .and_then(|s| match s { + Cow::Borrowed(s) => s.to_str().ok().map(Cow::Borrowed), + Cow::Owned(s) => s.to_str().ok().map(|s| Cow::Owned(s.into())), + }) + }) + .flatten(); + name.or_else(|| { + let names = self.remote_names(); + match names.len() { + 0 => None, + 1 => names.iter().next().copied().map(Cow::Borrowed), + _more_than_one => names.get("origin").copied().map(Cow::Borrowed), + } + }) + } + } +} + +mod branch { + use std::{borrow::Cow, collections::BTreeSet, convert::TryInto}; + + use gix_ref::FullNameRef; + use gix_validate::reference::name::Error as ValidateNameError; + + use crate::bstr::BStr; + + impl crate::Repository { + /// Return a set of unique short branch names for which custom configuration exists in the configuration, + /// if we deem them [trustworthy][crate::open::Options::filter_config_section()]. + pub fn branch_names(&self) -> BTreeSet<&str> { + self.subsection_names_of("branch") + } + + /// Returns the validated reference on the remote associated with the given `short_branch_name`, + /// always `main` instead of `refs/heads/main`. + /// + /// The returned reference is the one we track on the remote side for merging and pushing. + /// Returns `None` if the remote reference was not found. + /// May return an error if the reference is invalid. + pub fn branch_remote_ref<'a>( + &self, + short_branch_name: impl Into<&'a BStr>, + ) -> Option<Result<Cow<'_, FullNameRef>, ValidateNameError>> { + self.config + .resolved + .string("branch", Some(short_branch_name.into()), "merge") + .map(crate::config::tree::branch::Merge::try_into_fullrefname) + } + + /// Returns the unvalidated name of the remote associated with the given `short_branch_name`, + /// typically `main` instead of `refs/heads/main`. + /// In some cases, the returned name will be an URL. + /// Returns `None` if the remote was not found or if the name contained illformed UTF-8. + /// + /// See also [Reference::remote_name()][crate::Reference::remote_name()] for a more typesafe version + /// to be used when a `Reference` is available. + pub fn branch_remote_name<'a>( + &self, + short_branch_name: impl Into<&'a BStr>, + ) -> Option<crate::remote::Name<'_>> { + self.config + .resolved + .string("branch", Some(short_branch_name.into()), "remote") + .and_then(|name| name.try_into().ok()) + } + } +} + +impl crate::Repository { + pub(crate) fn filter_config_section(&self) -> fn(&gix_config::file::Metadata) -> bool { + self.options + .filter_config_section + .unwrap_or(config::section::is_trusted) + } + + fn subsection_names_of<'a>(&'a self, header_name: &'a str) -> BTreeSet<&'a str> { + self.config + .resolved + .sections_by_name(header_name) + .map(|it| { + let filter = self.filter_config_section(); + it.filter(move |s| filter(s.meta())) + .filter_map(|section| section.header().subsection_name().and_then(|b| b.to_str().ok())) + .collect() + }) + .unwrap_or_default() + } +} |