summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/worktree
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src/worktree')
-rw-r--r--vendor/gix/src/worktree/mod.rs160
-rw-r--r--vendor/gix/src/worktree/proxy.rs101
2 files changed, 261 insertions, 0 deletions
diff --git a/vendor/gix/src/worktree/mod.rs b/vendor/gix/src/worktree/mod.rs
new file mode 100644
index 000000000..19a44a900
--- /dev/null
+++ b/vendor/gix/src/worktree/mod.rs
@@ -0,0 +1,160 @@
+use std::path::PathBuf;
+
+pub use gix_worktree::*;
+
+use crate::{
+ bstr::{BStr, BString},
+ Repository,
+};
+
+pub(crate) type IndexStorage = gix_features::threading::OwnShared<gix_features::fs::MutableSnapshot<gix_index::File>>;
+/// A lazily loaded and auto-updated worktree index.
+pub type Index = gix_features::fs::SharedSnapshot<gix_index::File>;
+
+/// A stand-in to a worktree as result of a worktree iteration.
+///
+/// It provides access to typical worktree state, but may not actually point to a valid checkout as the latter has been moved or
+/// deleted.
+#[derive(Debug, Clone)]
+pub struct Proxy<'repo> {
+ pub(crate) parent: &'repo Repository,
+ pub(crate) git_dir: PathBuf,
+}
+
+/// Access
+impl<'repo> crate::Worktree<'repo> {
+ /// Read the location of the checkout, the base of the work tree
+ pub fn base(&self) -> &'repo std::path::Path {
+ self.path
+ }
+
+ /// Return true if this worktree is the main worktree associated with a non-bare git repository.
+ ///
+ /// It cannot be removed.
+ pub fn is_main(&self) -> bool {
+ self.id().is_none()
+ }
+
+ /// Return true if this worktree cannot be pruned, moved or deleted, which is useful if it is located on an external storage device.
+ ///
+ /// Always false for the main worktree.
+ pub fn is_locked(&self) -> bool {
+ Proxy::new(self.parent, self.parent.git_dir()).is_locked()
+ }
+
+ /// Provide a reason for the locking of this worktree, if it is locked at all.
+ ///
+ /// Note that we squelch errors in case the file cannot be read in which case the
+ /// reason is an empty string.
+ pub fn lock_reason(&self) -> Option<BString> {
+ Proxy::new(self.parent, self.parent.git_dir()).lock_reason()
+ }
+
+ /// Return the ID of the repository worktree, if it is a linked worktree, or `None` if it's a linked worktree.
+ pub fn id(&self) -> Option<&BStr> {
+ id(self.parent.git_dir(), self.parent.common_dir.is_some())
+ }
+}
+
+pub(crate) fn id(git_dir: &std::path::Path, has_common_dir: bool) -> Option<&BStr> {
+ if !has_common_dir {
+ return None;
+ }
+ let candidate = gix_path::os_str_into_bstr(git_dir.file_name().expect("at least one directory level"))
+ .expect("no illformed UTF-8");
+ let maybe_worktrees = git_dir.parent()?;
+ (maybe_worktrees.file_name()?.to_str()? == "worktrees").then_some(candidate)
+}
+
+///
+pub mod proxy;
+
+///
+pub mod open_index {
+ use crate::bstr::BString;
+
+ /// The error returned by [`Worktree::open_index()`][crate::Worktree::open_index()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Could not interpret value '{}' as 'index.threads'", .value)]
+ ConfigIndexThreads {
+ value: BString,
+ #[source]
+ err: gix_config::value::Error,
+ },
+ #[error(transparent)]
+ IndexFile(#[from] gix_index::file::init::Error),
+ }
+
+ impl<'repo> crate::Worktree<'repo> {
+ /// A shortcut to [`crate::Repository::open_index()`].
+ pub fn open_index(&self) -> Result<gix_index::File, Error> {
+ self.parent.open_index()
+ }
+
+ /// A shortcut to [`crate::Repository::index()`].
+ pub fn index(&self) -> Result<crate::worktree::Index, Error> {
+ self.parent.index()
+ }
+ }
+}
+
+///
+pub mod excludes {
+ use std::path::PathBuf;
+
+ /// The error returned by [`Worktree::excludes()`][crate::Worktree::excludes()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Could not read repository exclude.")]
+ Io(#[from] std::io::Error),
+ #[error(transparent)]
+ EnvironmentPermission(#[from] gix_sec::permission::Error<PathBuf>),
+ #[error("The value for `core.excludesFile` could not be read from configuration")]
+ ExcludesFilePathInterpolation(#[from] gix_config::path::interpolate::Error),
+ }
+
+ impl<'repo> crate::Worktree<'repo> {
+ /// Configure a file-system cache checking if files below the repository are excluded.
+ ///
+ /// This takes into consideration all the usual repository configuration.
+ // TODO: test, provide higher-level interface that is much easier to use and doesn't panic.
+ pub fn excludes(
+ &self,
+ index: &gix_index::State,
+ overrides: Option<gix_attributes::MatchGroup<gix_attributes::Ignore>>,
+ ) -> Result<gix_worktree::fs::Cache, Error> {
+ let repo = self.parent;
+ let case = repo
+ .config
+ .ignore_case
+ .then_some(gix_glob::pattern::Case::Fold)
+ .unwrap_or_default();
+ let mut buf = Vec::with_capacity(512);
+ let excludes_file = match repo.config.excludes_file().transpose()? {
+ Some(user_path) => Some(user_path),
+ None => repo.config.xdg_config_path("ignore")?,
+ };
+ let state = gix_worktree::fs::cache::State::IgnoreStack(gix_worktree::fs::cache::state::Ignore::new(
+ overrides.unwrap_or_default(),
+ gix_attributes::MatchGroup::<gix_attributes::Ignore>::from_git_dir(
+ repo.git_dir(),
+ excludes_file,
+ &mut buf,
+ )?,
+ None,
+ case,
+ ));
+ let attribute_list = state.build_attribute_list(index, index.path_backing(), case);
+ Ok(gix_worktree::fs::Cache::new(
+ self.path,
+ state,
+ case,
+ buf,
+ attribute_list,
+ ))
+ }
+ }
+}
diff --git a/vendor/gix/src/worktree/proxy.rs b/vendor/gix/src/worktree/proxy.rs
new file mode 100644
index 000000000..8a77db815
--- /dev/null
+++ b/vendor/gix/src/worktree/proxy.rs
@@ -0,0 +1,101 @@
+#![allow(clippy::result_large_err)]
+use std::path::{Path, PathBuf};
+
+use crate::{
+ bstr::{BStr, BString, ByteSlice},
+ worktree::Proxy,
+ Repository, ThreadSafeRepository,
+};
+
+#[allow(missing_docs)]
+pub mod into_repo {
+ use std::path::PathBuf;
+
+ /// The error returned by [`Proxy::into_repo()`][super::Proxy::into_repo()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ Open(#[from] crate::open::Error),
+ #[error("Worktree at '{}' is inaccessible", .base.display())]
+ MissingWorktree { base: PathBuf },
+ #[error(transparent)]
+ MissingGitDirFile(#[from] std::io::Error),
+ }
+}
+
+impl<'repo> Proxy<'repo> {
+ pub(crate) fn new(parent: &'repo Repository, git_dir: impl Into<PathBuf>) -> Self {
+ Proxy {
+ parent,
+ git_dir: git_dir.into(),
+ }
+ }
+}
+
+impl<'repo> Proxy<'repo> {
+ /// Read the location of the checkout, the base of the work tree.
+ /// Note that the location might not exist.
+ pub fn base(&self) -> std::io::Result<PathBuf> {
+ let git_dir = self.git_dir.join("gitdir");
+ let base_dot_git = gix_discover::path::from_plain_file(&git_dir).ok_or_else(|| {
+ std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ format!("Required file '{}' does not exist", git_dir.display()),
+ )
+ })??;
+
+ Ok(gix_discover::path::without_dot_git_dir(base_dot_git))
+ }
+
+ /// The git directory for the work tree, typically contained within the parent git dir.
+ pub fn git_dir(&self) -> &Path {
+ &self.git_dir
+ }
+
+ /// The name of the worktree, which is derived from its folder within the `worktrees` directory within the parent `.git` folder.
+ pub fn id(&self) -> &BStr {
+ gix_path::os_str_into_bstr(self.git_dir.file_name().expect("worktrees/ parent dir"))
+ .expect("no illformed UTF-8")
+ }
+
+ /// Return true if the worktree cannot be pruned, moved or deleted, which is useful if it is located on an external storage device.
+ pub fn is_locked(&self) -> bool {
+ self.git_dir.join("locked").is_file()
+ }
+
+ /// Provide a reason for the locking of this worktree, if it is locked at all.
+ ///
+ /// Note that we squelch errors in case the file cannot be read in which case the
+ /// reason is an empty string.
+ pub fn lock_reason(&self) -> Option<BString> {
+ std::fs::read(self.git_dir.join("locked"))
+ .ok()
+ .map(|contents| contents.trim().into())
+ }
+
+ /// Transform this proxy into a [`Repository`] while ignoring issues reading `base()` and ignoring that it might not exist.
+ ///
+ /// Most importantly, the `Repository` might be initialized with a non-existing work tree directory as the checkout
+ /// was removed or moved in the mean time or is unavailable for other reasons.
+ /// The caller will encounter io errors if it's used like the work tree is guaranteed to be present, but can still access
+ /// a lot of information if work tree access is avoided.
+ pub fn into_repo_with_possibly_inaccessible_worktree(self) -> Result<Repository, crate::open::Error> {
+ let base = self.base().ok();
+ let repo = ThreadSafeRepository::open_from_paths(self.git_dir, base, self.parent.options.clone())?;
+ Ok(repo.into())
+ }
+
+ /// Like `into_repo_with_possibly_inaccessible_worktree()` but will fail if the `base()` cannot be read or
+ /// if the worktree doesn't exist.
+ ///
+ /// Note that it won't fail if the worktree doesn't exist.
+ pub fn into_repo(self) -> Result<Repository, into_repo::Error> {
+ let base = self.base()?;
+ if !base.is_dir() {
+ return Err(into_repo::Error::MissingWorktree { base });
+ }
+ let repo = ThreadSafeRepository::open_from_paths(self.git_dir, base.into(), self.parent.options.clone())?;
+ Ok(repo.into())
+ }
+}