616 lines · 24.1 KB · Rust
RawDownload
#![allow(clippy::result_large_err)]use std::{path::PathBuf, time::Duration}; use gix_config::file::Metadata;use gix_lock::acquire::Fail; use crate::{    config,    config::{        Cache, boolean,        cache::util::{ApplyLeniency, ApplyLeniencyDefaultValue},        tree::{Core, Key},    },    remote,    repository::identity,}; /// Accessimpl Cache {    /// Returns `true` if the configuration value isn't set, so we assume bare for safety.    pub(crate) fn is_bare_but_assume_bare_if_unconfigured(&self) -> bool {        self.is_bare.unwrap_or(true)    }     #[cfg(feature = "blob-diff")]    pub(crate) fn diff_algorithm(&self) -> Result<gix_diff::blob::Algorithm, config::diff::algorithm::Error> {        use crate::config::{cache::util::ApplyLeniencyDefault, diff::algorithm::Error, tree::Diff};        self.diff_algorithm            .get_or_try_init(|| {                let name = self.resolved.string(Diff::ALGORITHM).unwrap_or_else(|| "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()    }     #[cfg(feature = "blob-diff")]    pub(crate) fn diff_drivers(&self) -> Result<Vec<gix_diff::blob::Driver>, config::diff::drivers::Error> {        use crate::config::cache::util::ApplyLeniencyDefault;        let mut out = Vec::<gix_diff::blob::Driver>::new();        for section in self            .resolved            .sections_by_name("diff")            .into_iter()            .flatten()            .filter(|s| (self.filter_config_section)(s.meta()))        {            let Some(name) = section.header().subsection_name().filter(|n| !n.is_empty()) else {                continue;            };             let driver = match out.iter_mut().find(|d| d.name == name) {                Some(existing) => existing,                None => {                    out.push(gix_diff::blob::Driver {                        name: name.into(),                        ..Default::default()                    });                    out.last_mut().expect("just pushed")                }            };             if let Some(binary) = section.value_implicit("binary") {                driver.is_binary = config::tree::Diff::DRIVER_BINARY                    .try_into_binary(binary)                    .with_leniency(self.lenient_config)                    .map_err(|err| config::diff::drivers::Error {                        name: driver.name.clone(),                        attribute: "binary",                        source: Box::new(err),                    })?;            }            if let Some(command) = section.value(config::tree::Diff::DRIVER_COMMAND.name) {                driver.command = command.into();            }            if let Some(textconv) = section.value(config::tree::Diff::DRIVER_TEXTCONV.name) {                driver.binary_to_text_command = textconv.into();            }            if let Some(algorithm) = section.value("algorithm") {                driver.algorithm = config::tree::Diff::DRIVER_ALGORITHM                    .try_into_algorithm(algorithm)                    .or_else(|err| match err {                        config::diff::algorithm::Error::Unimplemented { .. } if self.lenient_config => {                            Ok(gix_diff::blob::Algorithm::Histogram)                        }                        err => Err(err),                    })                    .with_lenient_default(self.lenient_config)                    .map_err(|err| config::diff::drivers::Error {                        name: driver.name.clone(),                        attribute: "algorithm",                        source: Box::new(err),                    })?                    .into();            }        }        Ok(out)    }     #[cfg(feature = "merge")]    pub(crate) fn merge_drivers(&self) -> Result<Vec<gix_merge::blob::Driver>, config::merge::drivers::Error> {        let mut out = Vec::<gix_merge::blob::Driver>::new();        for section in self            .resolved            .sections_by_name("merge")            .into_iter()            .flatten()            .filter(|s| (self.filter_config_section)(s.meta()))        {            let Some(name) = section.header().subsection_name().filter(|n| !n.is_empty()) else {                continue;            };             let driver = match out.iter_mut().find(|d| d.name == name) {                Some(existing) => existing,                None => {                    out.push(gix_merge::blob::Driver {                        name: name.into(),                        display_name: name.into(),                        ..Default::default()                    });                    out.last_mut().expect("just pushed")                }            };             if let Some(command) = section.value(config::tree::Merge::DRIVER_COMMAND.name) {                driver.command = command;            }            if let Some(recursive_name) = section.value(config::tree::Merge::DRIVER_RECURSIVE.name) {                driver.recursive = Some(recursive_name);            }        }        Ok(out)    }     #[cfg(feature = "merge")]    pub(crate) fn merge_pipeline_options(        &self,    ) -> Result<gix_merge::blob::pipeline::Options, config::merge::pipeline_options::Error> {        Ok(gix_merge::blob::pipeline::Options {            large_file_threshold_bytes: self.big_file_threshold()?,        })    }     #[cfg(feature = "blob-diff")]    pub(crate) fn diff_pipeline_options(        &self,    ) -> Result<gix_diff::blob::pipeline::Options, config::diff::pipeline_options::Error> {        Ok(gix_diff::blob::pipeline::Options {            large_file_threshold_bytes: self.big_file_threshold()?,            fs: self.fs_capabilities()?,        })    }     #[cfg(feature = "blob-diff")]    pub(crate) fn diff_renames(&self) -> Result<(Option<gix_diff::Rewrites>, bool), crate::diff::new_rewrites::Error> {        self.diff_renames            .get_or_try_init(|| crate::diff::new_rewrites(&self.resolved, self.lenient_config))            .copied()    }     pub(crate) fn big_file_threshold(&self) -> Result<u64, config::unsigned_integer::Error> {        Ok(Core::BIG_FILE_THRESHOLD            .try_into_u64(self.resolved.integer("core.bigFileThreshold"))            .with_leniency(self.lenient_config)?            .unwrap_or(512 * 1024 * 1024))    }     /// 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<String>) {        use config::tree::Gitoxide;        let agent = self            .user_agent            .get_or_init(|| {                self.resolved                    .string(Gitoxide::USER_AGENT)                    .map_or_else(|| crate::env::agent().into(), |s| s.to_string())            })            .to_owned();        ("agent", Some(gix_protocol::agent(agent)))    }     /// Return `true` if packet-tracing is enabled. Lenient and defaults to `false`.    #[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]    pub(crate) fn trace_packet(&self) -> bool {        use config::tree::Gitoxide;        self.resolved            .boolean(Gitoxide::TRACE_PACKET)            .ok()            .flatten()            .unwrap_or_default()    }     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 may_use_commit_graph(&self) -> Result<bool, config::boolean::Error> {        const DEFAULT: bool = true;        Ok(Core::COMMIT_GRAPH            .enrich_error(self.resolved.boolean("core.commitGraph"))            .with_lenient_default_value(self.lenient_config, Some(DEFAULT))?            .unwrap_or(DEFAULT))    }     /// 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] = key                .try_into_lock_timeout(                    self.resolved                        .integer_filter(key, &mut self.filter_config_section.clone()),                )                .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.    #[cfg(feature = "excludes")]    pub(crate) fn excludes_file(&self) -> Result<Option<PathBuf>, gix_config::path::interpolate::Error> {        self.trusted_file_path(Core::EXCLUDES_FILE)    }     /// 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,        key: impl gix_config::AsKey,    ) -> Result<Option<PathBuf>, gix_config::path::interpolate::Error> {        trusted_file_path(            &self.resolved,            key,            &mut self.filter_config_section.clone(),            self.lenient_config,            self.environment,        )    }     pub(crate) fn apply_leniency<T, E>(&self, res: Result<Option<T>, E>) -> Result<Option<T>, E> {        res.with_leniency(self.lenient_config)    }     pub(crate) fn fs_capabilities(&self) -> Result<gix_fs::Capabilities, boolean::Error> {        Ok(gix_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)?,        })    }     #[cfg(feature = "index")]    pub(crate) fn stat_options(&self) -> Result<gix_index::entry::stat::Options, config::stat_options::Error> {        use crate::config::tree::gitoxide;        Ok(gix_index::entry::stat::Options {            trust_ctime: boolean(self, "core.trustCTime", &Core::TRUST_C_TIME, true)?,            use_nsec: boolean(self, "gitoxide.core.useNsec", &gitoxide::Core::USE_NSEC, false)?,            use_stdev: boolean(self, "gitoxide.core.useStdev", &gitoxide::Core::USE_STDEV, false)?,            check_stat: self                .apply_leniency(                    self.resolved                        .string(Core::CHECK_STAT)                        .map(|v| Core::CHECK_STAT.try_into_checkstat(v))                        .transpose(),                )?                .unwrap_or(true),        })    }     #[cfg(any(feature = "index", feature = "tree-editor"))]    pub(crate) fn protect_options(&self) -> Result<gix_validate::path::component::Options, config::boolean::Error> {        const IS_WINDOWS: bool = cfg!(windows);        const IS_MACOS: bool = cfg!(target_os = "macos");        const ALWAYS_ON_FOR_SAFETY: bool = true;        Ok(gix_validate::path::component::Options {            protect_windows: config::tree::gitoxide::Core::PROTECT_WINDOWS                .enrich_error(self.resolved.boolean(config::tree::gitoxide::Core::PROTECT_WINDOWS))                .with_lenient_default_value(self.lenient_config, Some(IS_WINDOWS))?                .unwrap_or(IS_WINDOWS),            protect_hfs: config::tree::Core::PROTECT_HFS                .enrich_error(self.resolved.boolean(config::tree::Core::PROTECT_HFS))                .with_lenient_default_value(self.lenient_config, Some(IS_MACOS))?                .unwrap_or(IS_MACOS),            protect_ntfs: config::tree::Core::PROTECT_NTFS                .enrich_error(self.resolved.boolean(config::tree::Core::PROTECT_NTFS))                .with_lenient_default_value(self.lenient_config, Some(ALWAYS_ON_FOR_SAFETY))?                .unwrap_or(ALWAYS_ON_FOR_SAFETY),        })    }     /// 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.    #[cfg(feature = "worktree-mutation")]    pub(crate) fn checkout_options(        &self,        repo: &crate::Repository,        attributes_source: gix_worktree::stack::state::attributes::Source,    ) -> Result<gix_worktree_state::checkout::Options, config::checkout_options::Error> {        use crate::config::tree::gitoxide;        let git_dir = repo.git_dir();        let thread_limit = self.apply_leniency(            crate::config::tree::Checkout::WORKERS.try_from_workers(                self.resolved                    .integer_filter("checkout.workers", &mut self.filter_config_section.clone()),            ),        )?;        let capabilities = self.fs_capabilities()?;        let filters = {            let mut filters =                gix_filter::Pipeline::new(repo.command_context()?, crate::filter::Pipeline::options(repo)?);            if let Ok(mut head) = repo.head() {                let ctx = filters.driver_context_mut();                ctx.ref_name = head.referent_name().map(|name| name.as_bstr().to_owned());                ctx.treeish = head.peel_to_commit().ok().map(|commit| commit.id);            }            filters        };        let filter_process_delay = if boolean(            self,            "gitoxide.core.filterProcessDelay",            &gitoxide::Core::FILTER_PROCESS_DELAY,            true,        )? {            gix_filter::driver::apply::Delay::Allow        } else {            gix_filter::driver::apply::Delay::Forbid        };        Ok(gix_worktree_state::checkout::Options {            filter_process_delay,            validate: self.protect_options()?,            filters,            attributes: self                .assemble_attribute_globals(git_dir, attributes_source, self.attributes)?                .0,            fs: capabilities,            thread_limit,            destination_is_initially_empty: false,            overwrite_existing: false,            keep_going: false,            stat_options: self.stat_options().map_err(|err| match err {                config::stat_options::Error::ConfigCheckStat(err) => {                    config::checkout_options::Error::ConfigCheckStat(err)                }                config::stat_options::Error::ConfigBoolean(err) => config::checkout_options::Error::ConfigBoolean(err),            })?,        })    }     #[cfg(feature = "excludes")]    pub(crate) fn ignore_pattern_parser(&self) -> Result<gix_ignore::search::Ignore, config::boolean::Error> {        Ok(gix_ignore::search::Ignore {            support_precious: boolean(                self,                "gitoxide.parsePrecious",                &config::tree::Gitoxide::PARSE_PRECIOUS,                false,            )?,        })    }     #[cfg(feature = "excludes")]    pub(crate) fn assemble_exclude_globals(        &self,        git_dir: &std::path::Path,        overrides: Option<gix_ignore::Search>,        source: gix_worktree::stack::state::ignore::Source,        buf: &mut Vec<u8>,    ) -> Result<gix_worktree::stack::state::Ignore, config::exclude_stack::Error> {        let excludes_file = match self.excludes_file()? {            Some(user_path) => Some(user_path),            None => self.xdg_config_path("ignore")?,        };        let parse_ignore = self.ignore_pattern_parser()?;        Ok(gix_worktree::stack::state::Ignore::new(            overrides.unwrap_or_default(),            gix_ignore::Search::from_git_dir(git_dir, excludes_file, buf, parse_ignore)?,            None,            source,            parse_ignore,        ))    }    // TODO: at least one test, maybe related to core.attributesFile configuration.    #[cfg(feature = "attributes")]    pub(crate) fn assemble_attribute_globals(        &self,        git_dir: &std::path::Path,        source: gix_worktree::stack::state::attributes::Source,        attributes: crate::open::permissions::Attributes,    ) -> Result<(gix_worktree::stack::state::Attributes, Vec<u8>), config::attribute_stack::Error> {        use gix_attributes::Source;        let configured_or_user_attributes = match self.trusted_file_path(Core::ATTRIBUTES_FILE)? {            Some(attributes) => Some(attributes),            None => {                if attributes.git {                    self.xdg_config_path("attributes").ok().flatten()                } else {                    None                }            }        };        let attribute_files = [gix_attributes::Source::GitInstallation, gix_attributes::Source::System]            .into_iter()            .filter(|source| match source {                Source::GitInstallation => attributes.git_binary,                Source::System => attributes.system,                Source::Git | Source::Local => unreachable!("we don't offer turning this off right now"),            })            .filter_map(|source| source.storage_location(&mut Self::make_source_env(self.environment)))            .chain(configured_or_user_attributes);        let info_attributes_path = git_dir.join("info").join("attributes");        let mut buf = Vec::new();        let mut collection = gix_attributes::search::MetadataCollection::default();        let state = gix_worktree::stack::state::Attributes::new(            gix_attributes::Search::new_globals(attribute_files, &mut buf, &mut collection)?,            Some(info_attributes_path),            source,            collection,        );        Ok((state, buf))    }     #[cfg(feature = "attributes")]    pub(crate) fn pathspec_defaults(        &self,    ) -> Result<gix_pathspec::Defaults, gix_pathspec::defaults::from_environment::Error> {        use crate::config::tree::gitoxide;        let res = gix_pathspec::Defaults::from_environment(&mut |name| {            let key = [                &gitoxide::Pathspec::ICASE,                &gitoxide::Pathspec::GLOB,                &gitoxide::Pathspec::NOGLOB,                &gitoxide::Pathspec::LITERAL,            ]            .iter()            .find(|key| key.environment_override().expect("set") == name)            .expect("we must know all possible input variable names");             let val = self.resolved.string(key).map(gix_path::from_bstr)?;            Some(val.into_owned().into())        });        if res.is_err() && self.lenient_config {            Ok(gix_pathspec::Defaults::default())        } else {            res        }    }     #[cfg(any(feature = "attributes", feature = "excludes"))]    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.environment.xdg_config_home))            .or_else(|| {                gix_path::env::home_dir().map(|mut p| {                    (                        {                            p.push(".config");                            p                        },                        &self.environment.home,                    )                })            })            .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.

Lines 1–500 of 616.