use std::path::PathBuf;
#[cfg(feature = "worktree-archive")]
pub use gix_archive as archive;
#[cfg(feature = "excludes")]
pub use gix_worktree::*;
#[cfg(feature = "worktree-mutation")]
pub use gix_worktree_state as state;
#[cfg(feature = "worktree-stream")]
pub use gix_worktree_stream as stream;
use crate::{
bstr::{BStr, BString},
Repository,
};
#[cfg(feature = "index")]
pub(crate) type IndexStorage = gix_features::threading::OwnShared<gix_fs::SharedFileSnapshotMut<gix_index::File>>;
#[cfg(feature = "index")]
pub type Index = gix_fs::SharedFileSnapshot<gix_index::File>;
#[cfg(feature = "index")]
#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
pub enum IndexPersistedOrInMemory {
Persisted(Index),
InMemory(gix_index::File),
}
#[cfg(feature = "index")]
impl From<Index> for IndexPersistedOrInMemory {
fn from(value: Index) -> Self {
IndexPersistedOrInMemory::Persisted(value)
}
}
#[cfg(feature = "index")]
impl From<gix_index::File> for IndexPersistedOrInMemory {
fn from(value: gix_index::File) -> Self {
IndexPersistedOrInMemory::InMemory(value)
}
}
#[derive(Debug, Clone)]
pub struct Proxy<'repo> {
pub(crate) parent: &'repo Repository,
pub(crate) git_dir: PathBuf,
}
impl<'repo> crate::Worktree<'repo> {
pub fn base(&self) -> &'repo std::path::Path {
self.path
}
pub fn is_main(&self) -> bool {
self.id().is_none()
}
pub fn is_locked(&self) -> bool {
Proxy::new(self.parent, self.parent.git_dir()).is_locked()
}
pub fn lock_reason(&self) -> Option<BString> {
Proxy::new(self.parent, self.parent.git_dir()).lock_reason()
}
pub fn id(&self) -> Option<&BStr> {
id(self.parent.git_dir(), self.parent.common_dir.is_some())
}
pub fn dot_git_exists(&self) -> bool {
self.path.join(gix_discover::DOT_GIT_DIR).exists()
}
}
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;
#[cfg(feature = "index")]
pub mod open_index {
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
ConfigIndexThreads(#[from] crate::config::key::GenericErrorWithValue),
#[error(transparent)]
ConfigSkipHash(#[from] crate::config::boolean::Error),
#[error(transparent)]
IndexFile(#[from] gix_index::file::init::Error),
#[error(transparent)]
IndexCorrupt(#[from] gix_index::file::verify::Error),
}
impl crate::Worktree<'_> {
pub fn open_index(&self) -> Result<gix_index::File, Error> {
self.parent.open_index()
}
pub fn index(&self) -> Result<crate::worktree::Index, Error> {
self.parent.index()
}
}
}
#[cfg(feature = "excludes")]
pub mod excludes {
use crate::AttributeStack;
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
OpenIndex(#[from] crate::worktree::open_index::Error),
#[error(transparent)]
CreateCache(#[from] crate::config::exclude_stack::Error),
}
impl crate::Worktree<'_> {
pub fn excludes(&self, overrides: Option<gix_ignore::Search>) -> Result<AttributeStack<'_>, Error> {
let index = self.index()?;
Ok(self.parent.excludes(
&index,
overrides,
gix_worktree::stack::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped,
)?)
}
}
}
#[cfg(feature = "attributes")]
pub mod attributes {
use crate::{AttributeStack, Worktree};
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
OpenIndex(#[from] crate::worktree::open_index::Error),
#[error(transparent)]
CreateCache(#[from] crate::repository::attributes::Error),
}
impl<'repo> Worktree<'repo> {
pub fn attributes(&self, overrides: Option<gix_ignore::Search>) -> Result<AttributeStack<'repo>, Error> {
let index = self.index()?;
Ok(self.parent.attributes(
&index,
gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
gix_worktree::stack::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped,
overrides,
)?)
}
pub fn attributes_only(&self) -> Result<AttributeStack<'repo>, Error> {
let index = self.index()?;
self.parent
.attributes_only(
&index,
gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
)
.map_err(|err| Error::CreateCache(err.into()))
}
}
}
#[cfg(feature = "attributes")]
pub mod pathspec {
use crate::{
bstr::BStr,
config::{cache::util::ApplyLeniencyDefaultValue, tree::gitoxide},
Worktree,
};
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
Init(#[from] crate::pathspec::init::Error),
#[error(transparent)]
OpenIndex(#[from] crate::worktree::open_index::Error),
}
impl<'repo> Worktree<'repo> {
pub fn pathspec(
&self,
patterns: impl IntoIterator<Item = impl AsRef<BStr>>,
) -> Result<crate::Pathspec<'repo>, Error> {
let index = self.index()?;
let inherit_ignore_case = self
.parent
.config
.resolved
.boolean("gitoxide.pathspec.inheritIgnoreCase")
.map(|res| {
gitoxide::Pathspec::INHERIT_IGNORE_CASE
.enrich_error(res)
.with_lenient_default_value(
self.parent.config.lenient_config,
gitoxide::Pathspec::INHERIT_IGNORE_CASE_DEFAULT,
)
})
.transpose()
.map_err(|err| Error::Init(crate::pathspec::init::Error::Defaults(err.into())))?
.unwrap_or(gitoxide::Pathspec::INHERIT_IGNORE_CASE_DEFAULT);
Ok(self.parent.pathspec(
true,
patterns,
inherit_ignore_case,
&index,
gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
)?)
}
}
}