summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/config/cache
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix/src/config/cache
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix/src/config/cache')
-rw-r--r--vendor/gix/src/config/cache/access.rs233
-rw-r--r--vendor/gix/src/config/cache/incubate.rs111
-rw-r--r--vendor/gix/src/config/cache/init.rs485
-rw-r--r--vendor/gix/src/config/cache/mod.rs18
-rw-r--r--vendor/gix/src/config/cache/util.rs143
5 files changed, 990 insertions, 0 deletions
diff --git a/vendor/gix/src/config/cache/access.rs b/vendor/gix/src/config/cache/access.rs
new file mode 100644
index 000000000..8244eaf27
--- /dev/null
+++ b/vendor/gix/src/config/cache/access.rs
@@ -0,0 +1,233 @@
+#![allow(clippy::result_large_err)]
+use std::{borrow::Cow, path::PathBuf, time::Duration};
+
+use gix_lock::acquire::Fail;
+
+use crate::{
+ bstr::BStr,
+ config,
+ config::{
+ cache::util::{ApplyLeniency, ApplyLeniencyDefault},
+ checkout_options,
+ tree::{Checkout, Core, Key},
+ Cache,
+ },
+ remote,
+ repository::identity,
+};
+
+/// Access
+impl Cache {
+ pub(crate) fn diff_algorithm(&self) -> Result<gix_diff::blob::Algorithm, config::diff::algorithm::Error> {
+ use crate::config::diff::algorithm::Error;
+ self.diff_algorithm
+ .get_or_try_init(|| {
+ let name = self
+ .resolved
+ .string("diff", None, "algorithm")
+ .unwrap_or_else(|| Cow::Borrowed("myers".into()));
+ config::tree::Diff::ALGORITHM
+ .try_into_algorithm(name)
+ .or_else(|err| match err {
+ Error::Unimplemented { .. } if self.lenient_config => Ok(gix_diff::blob::Algorithm::Histogram),
+ err => Err(err),
+ })
+ .with_lenient_default(self.lenient_config)
+ })
+ .copied()
+ }
+
+ /// Returns a user agent for use with servers.
+ #[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
+ pub(crate) fn user_agent_tuple(&self) -> (&'static str, Option<Cow<'static, str>>) {
+ use config::tree::Gitoxide;
+ let agent = self
+ .user_agent
+ .get_or_init(|| {
+ self.resolved
+ .string_by_key(Gitoxide::USER_AGENT.logical_name().as_str())
+ .map(|s| s.to_string())
+ .unwrap_or_else(|| crate::env::agent().into())
+ })
+ .to_owned();
+ ("agent", Some(gix_protocol::agent(agent).into()))
+ }
+
+ pub(crate) fn personas(&self) -> &identity::Personas {
+ self.personas
+ .get_or_init(|| identity::Personas::from_config_and_env(&self.resolved))
+ }
+
+ pub(crate) fn url_rewrite(&self) -> &remote::url::Rewrite {
+ self.url_rewrite
+ .get_or_init(|| remote::url::Rewrite::from_config(&self.resolved, self.filter_config_section))
+ }
+
+ #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
+ pub(crate) fn url_scheme(&self) -> Result<&remote::url::SchemePermission, config::protocol::allow::Error> {
+ self.url_scheme
+ .get_or_try_init(|| remote::url::SchemePermission::from_config(&self.resolved, self.filter_config_section))
+ }
+
+ pub(crate) fn diff_renames(
+ &self,
+ ) -> Result<Option<crate::object::tree::diff::Rewrites>, crate::object::tree::diff::rewrites::Error> {
+ self.diff_renames
+ .get_or_try_init(|| {
+ crate::object::tree::diff::Rewrites::try_from_config(&self.resolved, self.lenient_config)
+ })
+ .copied()
+ }
+
+ /// Returns (file-timeout, pack-refs timeout)
+ pub(crate) fn lock_timeout(
+ &self,
+ ) -> Result<(gix_lock::acquire::Fail, gix_lock::acquire::Fail), config::lock_timeout::Error> {
+ let mut out: [gix_lock::acquire::Fail; 2] = Default::default();
+ for (idx, (key, default_ms)) in [(&Core::FILES_REF_LOCK_TIMEOUT, 100), (&Core::PACKED_REFS_TIMEOUT, 1000)]
+ .into_iter()
+ .enumerate()
+ {
+ out[idx] = self
+ .resolved
+ .integer_filter("core", None, key.name, &mut self.filter_config_section.clone())
+ .map(|res| key.try_into_lock_timeout(res))
+ .transpose()
+ .with_leniency(self.lenient_config)?
+ .unwrap_or_else(|| Fail::AfterDurationWithBackoff(Duration::from_millis(default_ms)));
+ }
+ Ok((out[0], out[1]))
+ }
+
+ /// The path to the user-level excludes file to ignore certain files in the worktree.
+ pub(crate) fn excludes_file(&self) -> Option<Result<PathBuf, gix_config::path::interpolate::Error>> {
+ self.trusted_file_path("core", None, Core::EXCLUDES_FILE.name)?
+ .map(|p| p.into_owned())
+ .into()
+ }
+
+ /// A helper to obtain a file from trusted configuration at `section_name`, `subsection_name`, and `key`, which is interpolated
+ /// if present.
+ pub(crate) fn trusted_file_path(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Option<Result<Cow<'_, std::path::Path>, gix_config::path::interpolate::Error>> {
+ let path = self.resolved.path_filter(
+ section_name,
+ subsection_name,
+ key,
+ &mut self.filter_config_section.clone(),
+ )?;
+
+ let install_dir = crate::path::install_dir().ok();
+ let home = self.home_dir();
+ let ctx = crate::config::cache::interpolate_context(install_dir.as_deref(), home.as_deref());
+ Some(path.interpolate(ctx))
+ }
+
+ pub(crate) fn apply_leniency<T, E>(&self, res: Option<Result<T, E>>) -> Result<Option<T>, E> {
+ res.transpose().with_leniency(self.lenient_config)
+ }
+
+ /// Collect everything needed to checkout files into a worktree.
+ /// Note that some of the options being returned will be defaulted so safe settings, the caller might have to override them
+ /// depending on the use-case.
+ pub(crate) fn checkout_options(
+ &self,
+ git_dir: &std::path::Path,
+ ) -> Result<gix_worktree::index::checkout::Options, checkout_options::Error> {
+ fn boolean(
+ me: &Cache,
+ full_key: &str,
+ key: &'static config::tree::keys::Boolean,
+ default: bool,
+ ) -> Result<bool, checkout_options::Error> {
+ debug_assert_eq!(
+ full_key,
+ key.logical_name(),
+ "BUG: key name and hardcoded name must match"
+ );
+ Ok(me
+ .apply_leniency(me.resolved.boolean_by_key(full_key).map(|v| key.enrich_error(v)))?
+ .unwrap_or(default))
+ }
+
+ fn assemble_attribute_globals(
+ me: &Cache,
+ _git_dir: &std::path::Path,
+ ) -> Result<gix_attributes::MatchGroup, checkout_options::Error> {
+ let _attributes_file = match me
+ .trusted_file_path("core", None, Core::ATTRIBUTES_FILE.name)
+ .transpose()?
+ {
+ Some(attributes) => Some(attributes.into_owned()),
+ None => me.xdg_config_path("attributes").ok().flatten(),
+ };
+ // TODO: implement gix_attributes::MatchGroup::<gix_attributes::Attributes>::from_git_dir(), similar to what's done for `Ignore`.
+ Ok(Default::default())
+ }
+
+ let thread_limit = self.apply_leniency(
+ self.resolved
+ .integer_filter_by_key("checkout.workers", &mut self.filter_config_section.clone())
+ .map(|value| Checkout::WORKERS.try_from_workers(value)),
+ )?;
+ Ok(gix_worktree::index::checkout::Options {
+ fs: gix_worktree::fs::Capabilities {
+ precompose_unicode: boolean(self, "core.precomposeUnicode", &Core::PRECOMPOSE_UNICODE, false)?,
+ ignore_case: boolean(self, "core.ignoreCase", &Core::IGNORE_CASE, false)?,
+ executable_bit: boolean(self, "core.fileMode", &Core::FILE_MODE, true)?,
+ symlink: boolean(self, "core.symlinks", &Core::SYMLINKS, true)?,
+ },
+ thread_limit,
+ destination_is_initially_empty: false,
+ overwrite_existing: false,
+ keep_going: false,
+ trust_ctime: boolean(self, "core.trustCTime", &Core::TRUST_C_TIME, true)?,
+ check_stat: self
+ .apply_leniency(
+ self.resolved
+ .string("core", None, "checkStat")
+ .map(|v| Core::CHECK_STAT.try_into_checkstat(v)),
+ )?
+ .unwrap_or(true),
+ attribute_globals: assemble_attribute_globals(self, git_dir)?,
+ })
+ }
+ pub(crate) fn xdg_config_path(
+ &self,
+ resource_file_name: &str,
+ ) -> Result<Option<PathBuf>, gix_sec::permission::Error<PathBuf>> {
+ std::env::var_os("XDG_CONFIG_HOME")
+ .map(|path| (PathBuf::from(path), &self.xdg_config_home_env))
+ .or_else(|| {
+ std::env::var_os("HOME").map(|path| {
+ (
+ {
+ let mut p = PathBuf::from(path);
+ p.push(".config");
+ p
+ },
+ &self.home_env,
+ )
+ })
+ })
+ .and_then(|(base, permission)| {
+ let resource = base.join("git").join(resource_file_name);
+ permission.check(resource).transpose()
+ })
+ .transpose()
+ }
+
+ /// Return the home directory if we are allowed to read it and if it is set in the environment.
+ ///
+ /// We never fail for here even if the permission is set to deny as we `gix-config` will fail later
+ /// if it actually wants to use the home directory - we don't want to fail prematurely.
+ pub(crate) fn home_dir(&self) -> Option<PathBuf> {
+ std::env::var_os("HOME")
+ .map(PathBuf::from)
+ .and_then(|path| self.home_env.check_opt(path))
+ }
+}
diff --git a/vendor/gix/src/config/cache/incubate.rs b/vendor/gix/src/config/cache/incubate.rs
new file mode 100644
index 000000000..047f2132b
--- /dev/null
+++ b/vendor/gix/src/config/cache/incubate.rs
@@ -0,0 +1,111 @@
+#![allow(clippy::result_large_err)]
+use super::{util, Error};
+use crate::config::tree::{Core, Extensions};
+
+/// A utility to deal with the cyclic dependency between the ref store and the configuration. The ref-store needs the
+/// object hash kind, and the configuration needs the current branch name to resolve conditional includes with `onbranch`.
+pub(crate) struct StageOne {
+ pub git_dir_config: gix_config::File<'static>,
+ pub buf: Vec<u8>,
+
+ pub is_bare: bool,
+ pub lossy: Option<bool>,
+ pub object_hash: gix_hash::Kind,
+ pub reflog: Option<gix_ref::store::WriteReflog>,
+}
+
+/// Initialization
+impl StageOne {
+ pub fn new(
+ common_dir: &std::path::Path,
+ git_dir: &std::path::Path,
+ git_dir_trust: gix_sec::Trust,
+ lossy: Option<bool>,
+ lenient: bool,
+ ) -> Result<Self, Error> {
+ let mut buf = Vec::with_capacity(512);
+ let mut config = load_config(
+ common_dir.join("config"),
+ &mut buf,
+ gix_config::Source::Local,
+ git_dir_trust,
+ lossy,
+ )?;
+
+ // Note that we assume the repo is bare by default unless we are told otherwise. This is relevant if
+ // the repo doesn't have a configuration file.
+ let is_bare = util::config_bool(&config, &Core::BARE, "core.bare", true, lenient)?;
+ let repo_format_version = config
+ .integer_by_key("core.repositoryFormatVersion")
+ .map(|version| Core::REPOSITORY_FORMAT_VERSION.try_into_usize(version))
+ .transpose()?
+ .unwrap_or_default();
+ let object_hash = (repo_format_version != 1)
+ .then_some(Ok(gix_hash::Kind::Sha1))
+ .or_else(|| {
+ config
+ .string("extensions", None, "objectFormat")
+ .map(|format| Extensions::OBJECT_FORMAT.try_into_object_format(format))
+ })
+ .transpose()?
+ .unwrap_or(gix_hash::Kind::Sha1);
+
+ let extension_worktree = util::config_bool(
+ &config,
+ &Extensions::WORKTREE_CONFIG,
+ "extensions.worktreeConfig",
+ false,
+ lenient,
+ )?;
+ if extension_worktree {
+ let worktree_config = load_config(
+ git_dir.join("config.worktree"),
+ &mut buf,
+ gix_config::Source::Worktree,
+ git_dir_trust,
+ lossy,
+ )?;
+ config.append(worktree_config);
+ };
+
+ let reflog = util::query_refupdates(&config, lenient)?;
+ Ok(StageOne {
+ git_dir_config: config,
+ buf,
+ is_bare,
+ lossy,
+ object_hash,
+ reflog,
+ })
+ }
+}
+
+fn load_config(
+ config_path: std::path::PathBuf,
+ buf: &mut Vec<u8>,
+ source: gix_config::Source,
+ git_dir_trust: gix_sec::Trust,
+ lossy: Option<bool>,
+) -> Result<gix_config::File<'static>, Error> {
+ buf.clear();
+ let metadata = gix_config::file::Metadata::from(source)
+ .at(&config_path)
+ .with(git_dir_trust);
+ let mut file = match std::fs::File::open(&config_path) {
+ Ok(f) => f,
+ Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(gix_config::File::new(metadata)),
+ Err(err) => return Err(err.into()),
+ };
+ std::io::copy(&mut file, buf)?;
+
+ let config = gix_config::File::from_bytes_owned(
+ buf,
+ metadata,
+ gix_config::file::init::Options {
+ includes: gix_config::file::includes::Options::no_follow(),
+ ..util::base_options(lossy)
+ },
+ )?;
+
+ Ok(config)
+}
diff --git a/vendor/gix/src/config/cache/init.rs b/vendor/gix/src/config/cache/init.rs
new file mode 100644
index 000000000..dc76f78bb
--- /dev/null
+++ b/vendor/gix/src/config/cache/init.rs
@@ -0,0 +1,485 @@
+#![allow(clippy::result_large_err)]
+use std::borrow::Cow;
+
+use gix_sec::Permission;
+
+use super::{interpolate_context, util, Error, StageOne};
+use crate::{
+ bstr::BString,
+ config,
+ config::{
+ cache::util::ApplyLeniency,
+ tree::{gitoxide, Core, Http},
+ Cache,
+ },
+ repository,
+};
+
+/// Initialization
+impl Cache {
+ #[allow(clippy::too_many_arguments)]
+ pub fn from_stage_one(
+ StageOne {
+ git_dir_config,
+ mut buf,
+ lossy,
+ is_bare,
+ object_hash,
+ reflog: _,
+ }: StageOne,
+ git_dir: &std::path::Path,
+ branch_name: Option<&gix_ref::FullNameRef>,
+ filter_config_section: fn(&gix_config::file::Metadata) -> bool,
+ git_install_dir: Option<&std::path::Path>,
+ home: Option<&std::path::Path>,
+ repository::permissions::Environment {
+ git_prefix,
+ home: home_env,
+ xdg_config_home: xdg_config_home_env,
+ ssh_prefix: _,
+ http_transport,
+ identity,
+ objects,
+ }: repository::permissions::Environment,
+ repository::permissions::Config {
+ git_binary: use_installation,
+ system: use_system,
+ git: use_git,
+ user: use_user,
+ env: use_env,
+ includes: use_includes,
+ }: repository::permissions::Config,
+ lenient_config: bool,
+ api_config_overrides: &[BString],
+ cli_config_overrides: &[BString],
+ ) -> Result<Self, Error> {
+ let options = gix_config::file::init::Options {
+ includes: if use_includes {
+ gix_config::file::includes::Options::follow(
+ interpolate_context(git_install_dir, home),
+ gix_config::file::includes::conditional::Context {
+ git_dir: git_dir.into(),
+ branch_name,
+ },
+ )
+ } else {
+ gix_config::file::includes::Options::no_follow()
+ },
+ ..util::base_options(lossy)
+ };
+
+ let config = {
+ let home_env = &home_env;
+ let xdg_config_home_env = &xdg_config_home_env;
+ let git_prefix = &git_prefix;
+ let metas = [
+ gix_config::source::Kind::GitInstallation,
+ gix_config::source::Kind::System,
+ gix_config::source::Kind::Global,
+ ]
+ .iter()
+ .flat_map(|kind| kind.sources())
+ .filter_map(|source| {
+ match source {
+ gix_config::Source::GitInstallation if !use_installation => return None,
+ gix_config::Source::System if !use_system => return None,
+ gix_config::Source::Git if !use_git => return None,
+ gix_config::Source::User if !use_user => return None,
+ _ => {}
+ }
+ source
+ .storage_location(&mut |name| {
+ match name {
+ git_ if git_.starts_with("GIT_") => Some(git_prefix),
+ "XDG_CONFIG_HOME" => Some(xdg_config_home_env),
+ "HOME" => Some(home_env),
+ _ => None,
+ }
+ .and_then(|perm| perm.check_opt(name).and_then(std::env::var_os))
+ })
+ .map(|p| (source, p.into_owned()))
+ })
+ .map(|(source, path)| gix_config::file::Metadata {
+ path: Some(path),
+ source: *source,
+ level: 0,
+ trust: gix_sec::Trust::Full,
+ });
+
+ let err_on_nonexisting_paths = false;
+ let mut globals = gix_config::File::from_paths_metadata_buf(
+ metas,
+ &mut buf,
+ err_on_nonexisting_paths,
+ gix_config::file::init::Options {
+ includes: gix_config::file::includes::Options::no_follow(),
+ ..options
+ },
+ )
+ .map_err(|err| match err {
+ gix_config::file::init::from_paths::Error::Init(err) => Error::from(err),
+ gix_config::file::init::from_paths::Error::Io(err) => err.into(),
+ })?
+ .unwrap_or_default();
+
+ let local_meta = git_dir_config.meta_owned();
+ globals.append(git_dir_config);
+ globals.resolve_includes(options)?;
+ if use_env {
+ globals.append(gix_config::File::from_env(options)?.unwrap_or_default());
+ }
+ if !cli_config_overrides.is_empty() {
+ config::overrides::append(&mut globals, cli_config_overrides, gix_config::Source::Cli, |_| None)
+ .map_err(|err| Error::ConfigOverrides {
+ err,
+ source: gix_config::Source::Cli,
+ })?;
+ }
+ if !api_config_overrides.is_empty() {
+ config::overrides::append(&mut globals, api_config_overrides, gix_config::Source::Api, |_| None)
+ .map_err(|err| Error::ConfigOverrides {
+ err,
+ source: gix_config::Source::Api,
+ })?;
+ }
+ apply_environment_overrides(&mut globals, *git_prefix, http_transport, identity, objects)?;
+ globals.set_meta(local_meta);
+ globals
+ };
+
+ let hex_len = util::parse_core_abbrev(&config, object_hash).with_leniency(lenient_config)?;
+
+ use util::config_bool;
+ let reflog = util::query_refupdates(&config, lenient_config)?;
+ let ignore_case = config_bool(&config, &Core::IGNORE_CASE, "core.ignoreCase", false, lenient_config)?;
+ let use_multi_pack_index = config_bool(
+ &config,
+ &Core::MULTIPACK_INDEX,
+ "core.multiPackIndex",
+ true,
+ lenient_config,
+ )?;
+ let object_kind_hint = util::disambiguate_hint(&config, lenient_config)?;
+ let (pack_cache_bytes, object_cache_bytes) =
+ util::parse_object_caches(&config, lenient_config, filter_config_section)?;
+ // NOTE: When adding a new initial cache, consider adjusting `reread_values_and_clear_caches()` as well.
+ Ok(Cache {
+ resolved: config.into(),
+ use_multi_pack_index,
+ object_hash,
+ object_kind_hint,
+ pack_cache_bytes,
+ object_cache_bytes,
+ reflog,
+ is_bare,
+ ignore_case,
+ hex_len,
+ filter_config_section,
+ xdg_config_home_env,
+ home_env,
+ lenient_config,
+ user_agent: Default::default(),
+ personas: Default::default(),
+ url_rewrite: Default::default(),
+ diff_renames: Default::default(),
+ #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
+ url_scheme: Default::default(),
+ diff_algorithm: Default::default(),
+ })
+ }
+
+ /// Call this with new `config` to update values and clear caches. Note that none of the values will be applied if a single
+ /// one is invalid.
+ /// However, those that are lazily read won't be re-evaluated right away and might thus pass now but fail later.
+ ///
+ /// Note that we unconditionally re-read all values.
+ pub fn reread_values_and_clear_caches_replacing_config(&mut self, config: crate::Config) -> Result<(), Error> {
+ let prev = std::mem::replace(&mut self.resolved, config);
+ match self.reread_values_and_clear_caches() {
+ Err(err) => {
+ drop(std::mem::replace(&mut self.resolved, prev));
+ Err(err)
+ }
+ Ok(()) => Ok(()),
+ }
+ }
+
+ /// Similar to `reread_values_and_clear_caches_replacing_config()`, but works on the existing configuration instead of a passed
+ /// in one that it them makes the default.
+ pub fn reread_values_and_clear_caches(&mut self) -> Result<(), Error> {
+ let config = &self.resolved;
+ let hex_len = util::parse_core_abbrev(config, self.object_hash).with_leniency(self.lenient_config)?;
+
+ use util::config_bool;
+ let ignore_case = config_bool(
+ config,
+ &Core::IGNORE_CASE,
+ "core.ignoreCase",
+ false,
+ self.lenient_config,
+ )?;
+ let object_kind_hint = util::disambiguate_hint(config, self.lenient_config)?;
+ let reflog = util::query_refupdates(config, self.lenient_config)?;
+
+ self.hex_len = hex_len;
+ self.ignore_case = ignore_case;
+ self.object_kind_hint = object_kind_hint;
+ self.reflog = reflog;
+
+ self.user_agent = Default::default();
+ self.personas = Default::default();
+ self.url_rewrite = Default::default();
+ self.diff_renames = Default::default();
+ self.diff_algorithm = Default::default();
+ (self.pack_cache_bytes, self.object_cache_bytes) =
+ util::parse_object_caches(config, self.lenient_config, self.filter_config_section)?;
+ #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
+ {
+ self.url_scheme = Default::default();
+ }
+
+ Ok(())
+ }
+}
+
+impl crate::Repository {
+ /// Replace our own configuration with `config` and re-read all cached values, and apply them to select in-memory instances.
+ pub(crate) fn reread_values_and_clear_caches_replacing_config(
+ &mut self,
+ config: crate::Config,
+ ) -> Result<(), Error> {
+ self.config.reread_values_and_clear_caches_replacing_config(config)?;
+ self.apply_changed_values();
+ Ok(())
+ }
+
+ fn apply_changed_values(&mut self) {
+ self.refs.write_reflog = util::reflog_or_default(self.config.reflog, self.work_dir().is_some());
+ }
+}
+
+fn apply_environment_overrides(
+ config: &mut gix_config::File<'static>,
+ git_prefix: Permission,
+ http_transport: Permission,
+ identity: Permission,
+ objects: Permission,
+) -> Result<(), Error> {
+ fn env(key: &'static dyn config::tree::Key) -> &'static str {
+ key.the_environment_override()
+ }
+ fn var_as_bstring(var: &str, perm: Permission) -> Option<BString> {
+ perm.check_opt(var)
+ .and_then(std::env::var_os)
+ .and_then(|val| gix_path::os_string_into_bstring(val).ok())
+ }
+
+ let mut env_override = gix_config::File::new(gix_config::file::Metadata::from(gix_config::Source::EnvOverride));
+ for (section_name, subsection_name, permission, data) in [
+ (
+ "http",
+ None,
+ http_transport,
+ &[
+ ("GIT_HTTP_LOW_SPEED_LIMIT", "lowSpeedLimit"),
+ ("GIT_HTTP_LOW_SPEED_TIME", "lowSpeedTime"),
+ ("GIT_HTTP_USER_AGENT", "userAgent"),
+ {
+ let key = &Http::SSL_CA_INFO;
+ (env(key), key.name)
+ },
+ {
+ let key = &Http::SSL_VERSION;
+ (env(key), key.name)
+ },
+ ][..],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("https".into())),
+ http_transport,
+ &[
+ ("HTTPS_PROXY", gitoxide::Https::PROXY.name),
+ ("https_proxy", gitoxide::Https::PROXY.name),
+ ],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("http".into())),
+ http_transport,
+ &[
+ ("ALL_PROXY", "allProxy"),
+ {
+ let key = &gitoxide::Http::ALL_PROXY;
+ (env(key), key.name)
+ },
+ ("NO_PROXY", "noProxy"),
+ {
+ let key = &gitoxide::Http::NO_PROXY;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Http::PROXY;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Http::VERBOSE;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Http::PROXY_AUTH_METHOD;
+ (env(key), key.name)
+ },
+ ],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("committer".into())),
+ identity,
+ &[
+ {
+ let key = &gitoxide::Committer::NAME_FALLBACK;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Committer::EMAIL_FALLBACK;
+ (env(key), key.name)
+ },
+ ],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("author".into())),
+ identity,
+ &[
+ {
+ let key = &gitoxide::Author::NAME_FALLBACK;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Author::EMAIL_FALLBACK;
+ (env(key), key.name)
+ },
+ ],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("commit".into())),
+ git_prefix,
+ &[
+ {
+ let key = &gitoxide::Commit::COMMITTER_DATE;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Commit::AUTHOR_DATE;
+ (env(key), key.name)
+ },
+ ],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("allow".into())),
+ http_transport,
+ &[("GIT_PROTOCOL_FROM_USER", "protocolFromUser")],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("user".into())),
+ identity,
+ &[{
+ let key = &gitoxide::User::EMAIL_FALLBACK;
+ (env(key), key.name)
+ }],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("objects".into())),
+ objects,
+ &[
+ {
+ let key = &gitoxide::Objects::NO_REPLACE;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Objects::REPLACE_REF_BASE;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Objects::CACHE_LIMIT;
+ (env(key), key.name)
+ },
+ ],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("ssh".into())),
+ git_prefix,
+ &[{
+ let key = &gitoxide::Ssh::COMMAND_WITHOUT_SHELL_FALLBACK;
+ (env(key), key.name)
+ }],
+ ),
+ (
+ "ssh",
+ None,
+ git_prefix,
+ &[{
+ let key = &config::tree::Ssh::VARIANT;
+ (env(key), key.name)
+ }],
+ ),
+ ] {
+ let mut section = env_override
+ .new_section(section_name, subsection_name)
+ .expect("statically known valid section name");
+ for (var, key) in data {
+ if let Some(value) = var_as_bstring(var, permission) {
+ section.push_with_comment(
+ (*key).try_into().expect("statically known to be valid"),
+ Some(value.as_ref()),
+ format!("from {var}").as_str(),
+ );
+ }
+ }
+ if section.num_values() == 0 {
+ let id = section.id();
+ env_override.remove_section_by_id(id);
+ }
+ }
+
+ {
+ let mut section = env_override
+ .new_section("core", None)
+ .expect("statically known valid section name");
+
+ for (var, key, permission) in [
+ {
+ let key = &Core::DELTA_BASE_CACHE_LIMIT;
+ (env(key), key.name, objects)
+ },
+ {
+ let key = &Core::SSH_COMMAND;
+ (env(key), key.name, git_prefix)
+ },
+ ] {
+ if let Some(value) = var_as_bstring(var, permission) {
+ section.push_with_comment(
+ key.try_into().expect("statically known to be valid"),
+ Some(value.as_ref()),
+ format!("from {var}").as_str(),
+ );
+ }
+ }
+
+ if section.num_values() == 0 {
+ let id = section.id();
+ env_override.remove_section_by_id(id);
+ }
+ }
+
+ if !env_override.is_void() {
+ config.append(env_override);
+ }
+ Ok(())
+}
diff --git a/vendor/gix/src/config/cache/mod.rs b/vendor/gix/src/config/cache/mod.rs
new file mode 100644
index 000000000..1904c5ea9
--- /dev/null
+++ b/vendor/gix/src/config/cache/mod.rs
@@ -0,0 +1,18 @@
+use super::{Cache, Error};
+
+mod incubate;
+pub(crate) use incubate::StageOne;
+
+mod init;
+
+impl std::fmt::Debug for Cache {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Cache").finish_non_exhaustive()
+ }
+}
+
+mod access;
+
+pub(crate) mod util;
+
+pub(crate) use util::interpolate_context;
diff --git a/vendor/gix/src/config/cache/util.rs b/vendor/gix/src/config/cache/util.rs
new file mode 100644
index 000000000..c12f850e6
--- /dev/null
+++ b/vendor/gix/src/config/cache/util.rs
@@ -0,0 +1,143 @@
+#![allow(clippy::result_large_err)]
+use super::Error;
+use crate::{
+ config,
+ config::tree::{gitoxide, Core},
+ revision::spec::parse::ObjectKindHint,
+};
+
+pub(crate) fn interpolate_context<'a>(
+ git_install_dir: Option<&'a std::path::Path>,
+ home_dir: Option<&'a std::path::Path>,
+) -> gix_config::path::interpolate::Context<'a> {
+ gix_config::path::interpolate::Context {
+ git_install_dir,
+ home_dir,
+ home_for_user: Some(gix_config::path::interpolate::home_for_user), // TODO: figure out how to configure this
+ }
+}
+
+pub(crate) fn base_options(lossy: Option<bool>) -> gix_config::file::init::Options<'static> {
+ gix_config::file::init::Options {
+ lossy: lossy.unwrap_or(!cfg!(debug_assertions)),
+ ..Default::default()
+ }
+}
+
+pub(crate) fn config_bool(
+ config: &gix_config::File<'_>,
+ key: &'static config::tree::keys::Boolean,
+ key_str: &str,
+ default: bool,
+ lenient: bool,
+) -> Result<bool, Error> {
+ use config::tree::Key;
+ debug_assert_eq!(
+ key_str,
+ key.logical_name(),
+ "BUG: key name and hardcoded name must match"
+ );
+ config
+ .boolean_by_key(key_str)
+ .map(|res| key.enrich_error(res))
+ .unwrap_or(Ok(default))
+ .map_err(Error::from)
+ .with_lenient_default(lenient)
+}
+
+pub(crate) fn query_refupdates(
+ config: &gix_config::File<'static>,
+ lenient_config: bool,
+) -> Result<Option<gix_ref::store::WriteReflog>, Error> {
+ let key = "core.logAllRefUpdates";
+ Core::LOG_ALL_REF_UPDATES
+ .try_into_ref_updates(config.boolean_by_key(key), || config.string_by_key(key))
+ .with_leniency(lenient_config)
+ .map_err(Into::into)
+}
+
+pub(crate) fn reflog_or_default(
+ config_reflog: Option<gix_ref::store::WriteReflog>,
+ has_worktree: bool,
+) -> gix_ref::store::WriteReflog {
+ config_reflog.unwrap_or(if has_worktree {
+ gix_ref::store::WriteReflog::Normal
+ } else {
+ gix_ref::store::WriteReflog::Disable
+ })
+}
+
+/// Return `(pack_cache_bytes, object_cache_bytes)` as parsed from gix-config
+pub(crate) fn parse_object_caches(
+ config: &gix_config::File<'static>,
+ lenient: bool,
+ mut filter_config_section: fn(&gix_config::file::Metadata) -> bool,
+) -> Result<(Option<usize>, usize), Error> {
+ let pack_cache_bytes = config
+ .integer_filter_by_key("core.deltaBaseCacheLimit", &mut filter_config_section)
+ .map(|res| Core::DELTA_BASE_CACHE_LIMIT.try_into_usize(res))
+ .transpose()
+ .with_leniency(lenient)?;
+ let object_cache_bytes = config
+ .integer_filter_by_key("gitoxide.objects.cacheLimit", &mut filter_config_section)
+ .map(|res| gitoxide::Objects::CACHE_LIMIT.try_into_usize(res))
+ .transpose()
+ .with_leniency(lenient)?
+ .unwrap_or_default();
+ Ok((pack_cache_bytes, object_cache_bytes))
+}
+
+pub(crate) fn parse_core_abbrev(
+ config: &gix_config::File<'static>,
+ object_hash: gix_hash::Kind,
+) -> Result<Option<usize>, Error> {
+ Ok(config
+ .string_by_key("core.abbrev")
+ .map(|abbrev| Core::ABBREV.try_into_abbreviation(abbrev, object_hash))
+ .transpose()?
+ .flatten())
+}
+
+pub(crate) fn disambiguate_hint(
+ config: &gix_config::File<'static>,
+ lenient_config: bool,
+) -> Result<Option<ObjectKindHint>, config::key::GenericErrorWithValue> {
+ match config.string_by_key("core.disambiguate") {
+ None => Ok(None),
+ Some(value) => Core::DISAMBIGUATE
+ .try_into_object_kind_hint(value)
+ .with_leniency(lenient_config),
+ }
+}
+
+// TODO: Use a specialization here once trait specialization is stabilized. Would be perfect here for `T: Default`.
+pub trait ApplyLeniency {
+ fn with_leniency(self, is_lenient: bool) -> Self;
+}
+
+pub trait ApplyLeniencyDefault {
+ fn with_lenient_default(self, is_lenient: bool) -> Self;
+}
+
+impl<T, E> ApplyLeniency for Result<Option<T>, E> {
+ fn with_leniency(self, is_lenient: bool) -> Self {
+ match self {
+ Ok(v) => Ok(v),
+ Err(_) if is_lenient => Ok(None),
+ Err(err) => Err(err),
+ }
+ }
+}
+
+impl<T, E> ApplyLeniencyDefault for Result<T, E>
+where
+ T: Default,
+{
+ fn with_lenient_default(self, is_lenient: bool) -> Self {
+ match self {
+ Ok(v) => Ok(v),
+ Err(_) if is_lenient => Ok(T::default()),
+ Err(err) => Err(err),
+ }
+ }
+}