diff options
Diffstat (limited to 'vendor/gix/src/config/cache')
-rw-r--r-- | vendor/gix/src/config/cache/access.rs | 233 | ||||
-rw-r--r-- | vendor/gix/src/config/cache/incubate.rs | 111 | ||||
-rw-r--r-- | vendor/gix/src/config/cache/init.rs | 485 | ||||
-rw-r--r-- | vendor/gix/src/config/cache/mod.rs | 18 | ||||
-rw-r--r-- | vendor/gix/src/config/cache/util.rs | 143 |
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), + } + } +} |