1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use std::{convert::TryInto, path::PathBuf, time::Duration};

use git_lock::acquire::Fail;

use crate::{config::Cache, remote, repository::identity};

/// Access
impl Cache {
    pub(crate) fn personas(&self) -> &identity::Personas {
        self.personas
            .get_or_init(|| identity::Personas::from_config_and_env(&self.resolved, self.git_prefix))
    }

    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, remote::url::scheme_permission::init::Error> {
        self.url_scheme.get_or_try_init(|| {
            remote::url::SchemePermission::from_config(&self.resolved, self.git_prefix, self.filter_config_section)
        })
    }

    /// Returns (file-timeout, pack-refs timeout)
    pub(crate) fn lock_timeout(
        &self,
    ) -> Result<(git_lock::acquire::Fail, git_lock::acquire::Fail), git_config::value::Error> {
        enum Kind {
            RefFiles,
            RefPackFile,
        }
        let mut out: [git_lock::acquire::Fail; 2] = Default::default();

        for (idx, kind) in [Kind::RefFiles, Kind::RefPackFile].iter().enumerate() {
            let (key, default_ms) = match kind {
                Kind::RefFiles => ("filesRefLockTimeout", 100),
                Kind::RefPackFile => ("packedRefsTimeout", 1000),
            };
            let mk_default = || Fail::AfterDurationWithBackoff(Duration::from_millis(default_ms));
            let mut fnp = self.filter_config_section;

            let lock_mode = match self.resolved.integer_filter("core", None, key, &mut fnp) {
                Some(Ok(val)) if val < 0 => Fail::AfterDurationWithBackoff(Duration::from_secs(u64::MAX)),
                Some(Ok(val)) if val == 0 => Fail::Immediately,
                Some(Ok(val)) => Fail::AfterDurationWithBackoff(Duration::from_millis(
                    val.try_into().expect("i64 can be repsented by u64"),
                )),
                Some(Err(_)) if self.lenient_config => mk_default(),
                Some(Err(err)) => return Err(err),
                None => mk_default(),
            };
            out[idx] = lock_mode;
        }
        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) -> Result<Option<PathBuf>, git_config::path::interpolate::Error> {
        let home = self.home_dir();
        let install_dir = crate::path::install_dir().ok();
        let ctx = crate::config::cache::interpolate_context(install_dir.as_deref(), home.as_deref());
        match self
            .resolved
            .path_filter("core", None, "excludesFile", &mut self.filter_config_section.clone())
            .map(|p| p.interpolate(ctx).map(|p| p.into_owned()))
            .transpose()
        {
            Ok(f) => Ok(f),
            Err(_err) if self.lenient_config => Ok(None),
            Err(err) => Err(err),
        }
    }

    /// Return a path by using the `$XDF_CONFIG_HOME` or `$HOME/.config/…` environment variables locations.
    pub fn xdg_config_path(
        &self,
        resource_file_name: &str,
    ) -> Result<Option<PathBuf>, git_sec::permission::Error<PathBuf>> {
        std::env::var_os("XDG_CONFIG_HOME")
            .map(|path| (path, &self.xdg_config_home_env))
            .or_else(|| std::env::var_os("HOME").map(|path| (path, &self.home_env)))
            .and_then(|(base, permission)| {
                let resource = std::path::PathBuf::from(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 `git-config` will fail later
    /// if it actually wants to use the home directory - we don't want to fail prematurely.
    pub fn home_dir(&self) -> Option<PathBuf> {
        std::env::var_os("HOME")
            .map(PathBuf::from)
            .and_then(|path| self.home_env.check_opt(path))
    }
}