summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/repository/worktree.rs
blob: 2de31bc86c391a0dd2e73c9d0c0f7c96a6600f06 (plain)
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::{worktree, Worktree};

/// Worktree iteration
impl crate::Repository {
    /// Return a list of all _linked_ worktrees sorted by private git dir path as a lightweight proxy.
    ///
    /// Note that these need additional processing to become usable, but provide a first glimpse a typical worktree information.
    pub fn worktrees(&self) -> std::io::Result<Vec<worktree::Proxy<'_>>> {
        let mut res = Vec::new();
        let iter = match std::fs::read_dir(self.common_dir().join("worktrees")) {
            Ok(iter) => iter,
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(res),
            Err(err) => return Err(err),
        };
        for entry in iter {
            let entry = entry?;
            let worktree_git_dir = entry.path();
            if worktree_git_dir.join("gitdir").is_file() {
                res.push(worktree::Proxy {
                    parent: self,
                    git_dir: worktree_git_dir,
                })
            }
        }
        res.sort_by(|a, b| a.git_dir.cmp(&b.git_dir));
        Ok(res)
    }
}

/// Interact with individual worktrees and their information.
impl crate::Repository {
    /// Return the repository owning the main worktree, typically from a linked worktree.
    ///
    /// Note that it might be the one that is currently open if this repository doesn't point to a linked worktree.
    /// Also note that the main repo might be bare.
    #[allow(clippy::result_large_err)]
    pub fn main_repo(&self) -> Result<crate::Repository, crate::open::Error> {
        crate::ThreadSafeRepository::open_opts(self.common_dir(), self.options.clone()).map(Into::into)
    }

    /// Return the currently set worktree if there is one, acting as platform providing a validated worktree base path.
    ///
    /// Note that there would be `None` if this repository is `bare` and the parent [`Repository`][crate::Repository] was instantiated without
    /// registered worktree in the current working dir.
    pub fn worktree(&self) -> Option<Worktree<'_>> {
        self.work_dir().map(|path| Worktree { parent: self, path })
    }

    /// Return true if this repository is bare, and has no main work tree.
    ///
    /// This is not to be confused with the [`worktree()`][crate::Repository::worktree()] worktree, which may exists if this instance
    /// was opened in a worktree that was created separately.
    pub fn is_bare(&self) -> bool {
        self.config.is_bare && self.work_dir().is_none()
    }

    /// Open a new copy of the index file and decode it entirely.
    ///
    /// It will use the `index.threads` configuration key to learn how many threads to use.
    /// Note that it may fail if there is no index.
    // TODO: test
    pub fn open_index(&self) -> Result<gix_index::File, worktree::open_index::Error> {
        let thread_limit = self
            .config
            .resolved
            .boolean("index", None, "threads")
            .map(|res| {
                res.map(|value| usize::from(!value)).or_else(|err| {
                    gix_config::Integer::try_from(err.input.as_ref())
                        .map_err(|err| worktree::open_index::Error::ConfigIndexThreads {
                            value: err.input.clone(),
                            err,
                        })
                        .map(|value| value.to_decimal().and_then(|v| v.try_into().ok()).unwrap_or(1))
                })
            })
            .transpose()?;
        gix_index::File::at(
            self.index_path(),
            self.object_hash(),
            gix_index::decode::Options {
                thread_limit,
                min_extension_block_in_bytes_for_threading: 0,
                expected_checksum: None,
            },
        )
        .map_err(Into::into)
    }

    /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file
    /// on disk has changed.
    ///
    /// The index file is shared across all clones of this repository.
    pub fn index(&self) -> Result<worktree::Index, worktree::open_index::Error> {
        self.index
            .recent_snapshot(
                || self.index_path().metadata().and_then(|m| m.modified()).ok(),
                || {
                    self.open_index().map(Some).or_else(|err| match err {
                        worktree::open_index::Error::IndexFile(gix_index::file::init::Error::Io(err))
                            if err.kind() == std::io::ErrorKind::NotFound =>
                        {
                            Ok(None)
                        }
                        err => Err(err),
                    })
                },
            )
            .and_then(|opt| match opt {
                Some(index) => Ok(index),
                None => Err(worktree::open_index::Error::IndexFile(
                    gix_index::file::init::Error::Io(std::io::Error::new(
                        std::io::ErrorKind::NotFound,
                        format!("Could not find index file at {:?} for opening.", self.index_path()),
                    )),
                )),
            })
    }
}