485 lines · 16.6 KB · Rust
RawDownload
#![allow(clippy::result_large_err)]use std::borrow::Cow; use git_sec::Permission; use super::{interpolate_context, util, Error, StageOne};use crate::{    bstr::BString,    config,    config::{        cache::util::ApplyLeniency,        tree::{gitoxide, Core, Http},        Cache,    },    repository,}; /// Initializationimpl 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<&git_ref::FullNameRef>,        filter_config_section: fn(&git_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 = git_config::file::init::Options {            includes: if use_includes {                git_config::file::includes::Options::follow(                    interpolate_context(git_install_dir, home),                    git_config::file::includes::conditional::Context {                        git_dir: git_dir.into(),                        branch_name,                    },                )            } else {                git_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 = [                git_config::source::Kind::GitInstallation,                git_config::source::Kind::System,                git_config::source::Kind::Global,            ]            .iter()            .flat_map(|kind| kind.sources())            .filter_map(|source| {                match source {                    git_config::Source::GitInstallation if !use_installation => return None,                    git_config::Source::System if !use_system => return None,                    git_config::Source::Git if !use_git => return None,                    git_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)| git_config::file::Metadata {                path: Some(path),                source: *source,                level: 0,                trust: git_sec::Trust::Full,            });             let err_on_nonexisting_paths = false;            let mut globals = git_config::File::from_paths_metadata_buf(                metas,                &mut buf,                err_on_nonexisting_paths,                git_config::file::init::Options {                    includes: git_config::file::includes::Options::no_follow(),                    ..options                },            )            .map_err(|err| match err {                git_config::file::init::from_paths::Error::Init(err) => Error::from(err),                git_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(git_config::File::from_env(options)?.unwrap_or_default());            }            if !cli_config_overrides.is_empty() {                config::overrides::append(&mut globals, cli_config_overrides, git_config::Source::Cli, |_| None)                    .map_err(|err| Error::ConfigOverrides {                        err,                        source: git_config::Source::Cli,                    })?;            }            if !api_config_overrides.is_empty() {                config::overrides::append(&mut globals, api_config_overrides, git_config::Source::Api, |_| None)                    .map_err(|err| Error::ConfigOverrides {                        err,                        source: git_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 git_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| git_path::os_string_into_bstring(val).ok())    }     let mut env_override = git_config::File::new(git_config::file::Metadata::from(git_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(())}