812 lines · 26.0 KB · Rust
RawDownload
#![allow(clippy::result_large_err)]use std::ffi::OsString; use gix_sec::Permission; use super::{Error, StageOne, interpolate_context, util};use crate::{    bstr::BString,    config,    config::{        Cache,        cache::util::{ApplyLeniency, ApplyLeniencyDefault},        tree::{Author, Committer, Core, Gitoxide, Http, Notes, gitoxide},    },    open,    repository::init::setup_objects,}; /// Initializationimpl Cache {    #[expect(clippy::too_many_arguments)]    pub fn from_stage_one(        StageOne {            git_dir_config,            mut buf,            lossy,            is_bare,            object_hash,            reflog: _,            precompose_unicode: _,            protect_windows: _,        }: 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>,        git_installation_config_path: Option<&std::path::Path>,        system_config_path: Option<&std::path::Path>,        environment: open::permissions::Environment,        attributes: open::permissions::Attributes,        config_permissions: open::permissions::Config,        lenient_config: bool,        api_config_overrides: &[BString],        cli_config_overrides: &[BString],        use_repository_local_environment: bool,    ) -> Result<Self, Error> {        let config = load(            Some(git_dir_config),            &mut buf,            Some(git_dir),            branch_name,            git_install_dir,            home,            git_installation_config_path,            system_config_path,            environment,            config_permissions,            lossy,            lenient_config,            api_config_overrides,            cli_config_overrides,            use_repository_local_environment,        )?;         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 shared_repository_permissions =            config::file_mut::shared_repository_permissions(&config, filter_config_section)                .with_lenient_default(lenient_config)?;        let refs_namespace = util::query_refs_namespace(&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,        )?;        #[cfg(feature = "revision")]        let object_kind_hint = util::disambiguate_hint(&config, lenient_config)?;        let (static_pack_cache_limit_bytes, pack_cache_bytes, object_cache_bytes, alloc_limit_bytes) =            util::parse_object_caches(&config, lenient_config, filter_config_section)?;        let loose_compression = super::access::loose_compression(&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,            #[cfg(feature = "revision")]            object_kind_hint,            static_pack_cache_limit_bytes,            pack_cache_bytes,            object_cache_bytes,            alloc_limit_bytes,            loose_compression,            reflog,            shared_repository_permissions,            refs_namespace,            is_bare,            ignore_case,            hex_len,            filter_config_section,            environment,            lenient_config,            attributes,            user_agent: Default::default(),            personas: Default::default(),            url_rewrite: Default::default(),            #[cfg(feature = "blob-diff")]            diff_renames: Default::default(),            #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]            url_scheme: Default::default(),            #[cfg(feature = "blob-diff")]            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,        )?;         #[cfg(feature = "revision")]        {            let object_kind_hint = util::disambiguate_hint(config, self.lenient_config)?;            self.object_kind_hint = object_kind_hint;        }        let reflog = util::query_refupdates(config, self.lenient_config)?;        let shared_repository_permissions =            config::file_mut::shared_repository_permissions(config, self.filter_config_section)                .with_lenient_default(self.lenient_config)?;        let refs_namespace = util::query_refs_namespace(config, self.lenient_config)?;         self.hex_len = hex_len;        self.ignore_case = ignore_case;        self.reflog = reflog;        self.shared_repository_permissions = shared_repository_permissions;        self.refs_namespace = refs_namespace;         self.user_agent = Default::default();        self.personas = Default::default();        self.url_rewrite = Default::default();        #[cfg(feature = "blob-diff")]        {            self.diff_renames = Default::default();            self.diff_algorithm = Default::default();        }        (            self.static_pack_cache_limit_bytes,            self.pack_cache_bytes,            self.object_cache_bytes,            self.alloc_limit_bytes,        ) = util::parse_object_caches(config, self.lenient_config, self.filter_config_section)?;        let loose_compression =            super::access::loose_compression(config, self.lenient_config, self.filter_config_section)?;        self.loose_compression = loose_compression;        #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]        {            self.url_scheme = Default::default();        }         Ok(())    }     pub(crate) fn make_source_env(        crate::open::permissions::Environment {            xdg_config_home,            git_prefix,            home,            ..        }: open::permissions::Environment,    ) -> impl FnMut(&str) -> Option<OsString> {        move |name| {            match name {                git_ if git_.starts_with("GIT_") => Some(git_prefix),                "XDG_CONFIG_HOME" => Some(xdg_config_home),                "HOME" => {                    return if home.is_allowed() {                        gix_path::env::home_dir().map(Into::into)                    } else {                        None                    };                }                _ => None,            }            .and_then(|perm| perm.check_opt(name).and_then(gix_path::env::var))        }    }} /// Resolve a permitted repository-independent configuration source, including explicit and environment path overrides.pub(crate) fn source_path(    source: gix_config::Source,    git_installation_config_path: Option<&std::path::Path>,    system_config_path: Option<&std::path::Path>,    permissions: open::permissions::Config,    source_env: &mut dyn FnMut(&str) -> Option<OsString>,) -> Option<std::path::PathBuf> {    let permitted = match source {        gix_config::Source::GitInstallation => permissions.git_binary,        gix_config::Source::System => permissions.system,        gix_config::Source::Git => permissions.git,        gix_config::Source::User => permissions.user,        _ => false,    };    if !permitted {        return None;    }    if matches!(source, gix_config::Source::GitInstallation | gix_config::Source::System)        && source_env("GIT_CONFIG_NOSYSTEM")            .and_then(|value| gix_config::Boolean::try_from(value).ok())            .is_some_and(|value| value.0)    {        return None;    }    match source {        gix_config::Source::GitInstallation => git_installation_config_path,        gix_config::Source::System => system_config_path,        _ => None,    }    .map(ToOwned::to_owned)    .or_else(|| source.storage_location(source_env))} /// Load global configuration and optional repository-local configuration using the same ordering and overrides as/// repository opening.#[expect(clippy::too_many_arguments)]pub(crate) fn load(    git_dir_config: Option<gix_config::File>,    buf: &mut Vec<u8>,    git_dir: Option<&std::path::Path>,    branch_name: Option<&gix_ref::FullNameRef>,    git_install_dir: Option<&std::path::Path>,    home: Option<&std::path::Path>,    git_installation_config_path: Option<&std::path::Path>,    system_config_path: Option<&std::path::Path>,    environment @ open::permissions::Environment {        git_prefix,        other,        ssh_prefix: _,        xdg_config_home: _,        home: _,        http_transport,        identity,        objects,    }: open::permissions::Environment,    config_permissions @ open::permissions::Config {        env: use_env,        includes: use_includes,        ..    }: open::permissions::Config,    lossy: bool,    lenient: bool,    api_config_overrides: &[BString],    cli_config_overrides: &[BString],    use_repository_local_environment: bool,) -> Result<gix_config::File, 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, branch_name },            )        } else {            gix_config::file::includes::Options::no_follow()        },        ..util::base_options(lossy, lenient)    };    let git_prefix = &git_prefix;    let mut source_env = Cache::make_source_env(environment);    let mut 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| {        source_path(            source,            git_installation_config_path,            system_config_path,            config_permissions,            &mut source_env,        )        .map(|path| {            gix_config::file::Metadata::from(source)                .at(path)                .with(gix_sec::Trust::Full)        })    });     let err_on_nonexisting_paths = false;    let mut globals = gix_config::File::from_paths_metadata_buf(        &mut metas,        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 { source, path } => Error::Io { source, path },    })?    .unwrap_or_default();     let local_meta = git_dir_config.as_ref().map(gix_config::File::meta_owned);    if let Some(git_dir_config) = git_dir_config {        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,        other,        http_transport,        identity,        objects,        use_repository_local_environment,    )?;    if let Some(local_meta) = local_meta {        globals.set_meta(local_meta);    }    Ok(globals)} 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> {        let (            previous_static_pack_cache_limit_bytes,            previous_pack_cache_bytes,            previous_object_cache_bytes,            previous_loose_compression,        ) = (            self.config.static_pack_cache_limit_bytes,            self.config.pack_cache_bytes,            self.config.object_cache_bytes,            self.config.loose_compression,        );        self.config.reread_values_and_clear_caches_replacing_config(config)?;        self.apply_changed_values();        if previous_static_pack_cache_limit_bytes != self.config.static_pack_cache_limit_bytes            || previous_pack_cache_bytes != self.config.pack_cache_bytes            || previous_object_cache_bytes != self.config.object_cache_bytes        {            setup_objects(&mut self.objects, &self.config);        }        if previous_loose_compression != self.config.loose_compression {            // This will only affect newly opened object databases.            // This is fine for now, it's not expected to be changed at runtime.            self.objects.loose_compression = self.config.loose_compression;        }        Ok(())    }     fn apply_changed_values(&mut self) {        self.refs.write_reflog = util::reflog_or_default(self.config.reflog, self.workdir().is_some());        self.refs.shared_repository_permissions = self.config.shared_repository_permissions;        self.refs.namespace.clone_from(&self.config.refs_namespace);    }} fn apply_environment_overrides(    config: &mut gix_config::File,    git_prefix: Permission,    other: Permission,    http_transport: Permission,    identity: Permission,    objects: Permission,    use_repository_local_environment: bool,) -> 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 [        (            "core",            None,            if use_repository_local_environment {                git_prefix            } else {                Permission::Deny            },            &[                {                    let key = &Core::WORKTREE;                    (env(key), key.name)                },                {                    let key = &Core::NOTES_REF;                    (env(key), key.name)                },                {                    let key = &Core::EDITOR;                    (env(key), key.name)                },            ][..],        ),        (            "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)                },            ][..],        ),        (            "notes",            None,            git_prefix,            &[{                let key = &Notes::DISPLAY_REF;                (env(key), key.name)            }][..],        ),        (            "gitoxide",            None,            other,            &[                {                    let key = &Gitoxide::TERM;                    (env(key), key.name)                },                {                    let key = &Gitoxide::VISUAL;                    (env(key), key.name)                },                {

Lines 1–500 of 812.