145 lines · 6.0 KB · Rust
RawDownload
use std::path::PathBuf; /// A repository path which either points to a work tree or the `.git` repository itself.#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]pub enum Path {    /// The currently checked out linked worktree along with its connected and existing git directory, or the worktree checkout of a    /// submodule.    LinkedWorkTree {        /// The base of the work tree.        work_dir: PathBuf,        /// The worktree-private git dir, located within the main git directory which holds most of the information.        git_dir: PathBuf,    },    /// The currently checked out or nascent work tree of a git repository    WorkTree(PathBuf),    /// The git repository itself, typically bare and without known worktree.    ///    /// Note that it might still have linked work-trees which can be accessed later, weather bare or not, or it might be a    /// submodule git directory in the `.git/modules/**/<name>` directory of the parent repository.    Repository(PathBuf),} mod path {    use std::path::PathBuf;     use crate::{        path::without_dot_git_dir,        repository::{Kind, Path},        DOT_GIT_DIR,    };     impl AsRef<std::path::Path> for Path {        fn as_ref(&self) -> &std::path::Path {            match self {                Path::WorkTree(path)                | Path::Repository(path)                | Path::LinkedWorkTree {                    work_dir: _,                    git_dir: path,                } => path,            }        }    }     impl Path {        /// Instantiate a new path from `dir` which is expected to be the `.git` directory, with `kind` indicating        /// whether it's a bare repository or not, with `current_dir` being used to absolutize relative paths        /// as needed.        ///        /// `None` is returned if `dir` could not be resolved due to being relative and trying to reach outside of the filesystem root.        pub fn from_dot_git_dir(            dir: impl Into<PathBuf>,            kind: Kind,            current_dir: impl AsRef<std::path::Path>,        ) -> Option<Self> {            let cwd = current_dir.as_ref();            let absolutize_on_trailing_dot_dot = |dir: PathBuf| -> Option<PathBuf> {                if !matches!(dir.components().rev().next(), Some(std::path::Component::ParentDir)) {                    dir                } else {                    git_path::absolutize(&dir, cwd)?.into_owned()                }                .into()            };             let dir = dir.into();            match kind {                Kind::Submodule { git_dir } => Path::LinkedWorkTree {                    git_dir: git_path::absolutize(git_dir, cwd)?.into_owned(),                    work_dir: without_dot_git_dir(absolutize_on_trailing_dot_dot(dir)?),                },                Kind::SubmoduleGitDir => Path::Repository(dir),                Kind::WorkTreeGitDir { work_dir } => Path::LinkedWorkTree { git_dir: dir, work_dir },                Kind::WorkTree { linked_git_dir } => match linked_git_dir {                    Some(git_dir) => Path::LinkedWorkTree {                        git_dir,                        work_dir: without_dot_git_dir(absolutize_on_trailing_dot_dot(dir)?),                    },                    None => {                        let mut dir = absolutize_on_trailing_dot_dot(dir)?;                        dir.pop(); // ".git" suffix                        let work_dir = dir.as_os_str().is_empty().then(|| PathBuf::from(".")).unwrap_or(dir);                        Path::WorkTree(work_dir)                    }                },                Kind::Bare => Path::Repository(dir),            }            .into()        }        /// Returns the [kind][Kind] of this repository path.        pub fn kind(&self) -> Kind {            match self {                Path::LinkedWorkTree { work_dir: _, git_dir } => Kind::WorkTree {                    linked_git_dir: Some(git_dir.to_owned()),                },                Path::WorkTree(_) => Kind::WorkTree { linked_git_dir: None },                Path::Repository(_) => Kind::Bare,            }        }         /// Consume and split this path into the location of the `.git` directory as well as an optional path to the work tree.        pub fn into_repository_and_work_tree_directories(self) -> (PathBuf, Option<PathBuf>) {            match self {                Path::LinkedWorkTree { work_dir, git_dir } => (git_dir, Some(work_dir)),                Path::WorkTree(working_tree) => (working_tree.join(DOT_GIT_DIR), Some(working_tree)),                Path::Repository(repository) => (repository, None),            }        }    }} /// The kind of repository path.#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]pub enum Kind {    /// A bare repository does not have a work tree, that is files on disk beyond the `git` repository itself.    ///    /// Note that this is merely a guess at this point as we didn't read the configuration yet.    Bare,    /// A `git` repository along with a checked out files in a work tree.    WorkTree {        /// If set, this is the git dir associated with this _linked_ worktree.        /// If `None`, the git_dir is the `.git` directory inside the _main_ worktree we represent.        linked_git_dir: Option<PathBuf>,    },    /// A worktree's git directory in the common`.git` directory in `worktrees/<name>`.    WorkTreeGitDir {        /// Path to the worktree directory.        work_dir: PathBuf,    },    /// The directory is a `.git` dir file of a submodule worktree.    Submodule {        /// The git repository itself that is referenced by the `.git` dir file, typically in the `.git/modules/**/<name>` directory of the parent        /// repository.        git_dir: PathBuf,    },    /// The git directory in the `.git/modules/**/<name>` directory tree of the parent repository    SubmoduleGitDir,} impl Kind {    /// Returns true if this is a bare repository, one without a work tree.    pub fn is_bare(&self) -> bool {        matches!(self, Kind::Bare)    }}