diff options
Diffstat (limited to 'vendor/gix-discover/src/repository.rs')
-rw-r--r-- | vendor/gix-discover/src/repository.rs | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/vendor/gix-discover/src/repository.rs b/vendor/gix-discover/src/repository.rs new file mode 100644 index 000000000..0c0318cdc --- /dev/null +++ b/vendor/gix-discover/src/repository.rs @@ -0,0 +1,145 @@ +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 normalize 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 normalize_on_trailing_dot_dot = |dir: PathBuf| -> Option<PathBuf> { + if !matches!(dir.components().rev().next(), Some(std::path::Component::ParentDir)) { + dir + } else { + gix_path::normalize(&dir, cwd)?.into_owned() + } + .into() + }; + + let dir = dir.into(); + match kind { + Kind::Submodule { git_dir } => Path::LinkedWorkTree { + git_dir: gix_path::normalize(git_dir, cwd)?.into_owned(), + work_dir: without_dot_git_dir(normalize_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(normalize_on_trailing_dot_dot(dir)?), + }, + None => { + let mut dir = normalize_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 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) + } +} |