use std::sync::atomic::AtomicBool;
use crate::error::{ResultExt, message};
use crate::{
Error, Repository, Result,
bstr::{BStr, BString},
dirwalk, is_dir_to_mode,
util::OwnedOrStaticAtomicBool,
worktree::IndexPersistedOrInMemory,
};
impl Repository {
pub fn dirwalk_options(&self) -> Result<dirwalk::Options> {
Ok(dirwalk::Options::from_fs_caps(self.filesystem_options()?))
}
pub fn dirwalk(
&self,
index: &gix_index::State,
patterns: impl IntoIterator<Item = impl AsRef<BStr>>,
should_interrupt: &AtomicBool,
options: dirwalk::Options,
delegate: &mut dyn gix_dir::walk::Delegate,
) -> Result<dirwalk::Outcome<'_>> {
let _span = gix_trace::coarse!("gix::dirwalk");
let workdir = self
.workdir()
.ok_or_else(|| Error::from_error(message("A working tree is required to perform a directory walk")))?;
let mut excludes = self.excludes(
index,
None,
crate::worktree::stack::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped,
)?;
let mut pathspec = self.pathspec(
options.empty_patterns_match_prefix,
patterns,
true,
index,
crate::worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
)?;
let git_dir_realpath =
crate::path::realpath_opts(self.git_dir(), self.current_dir(), crate::path::realpath::MAX_SYMLINKS)?;
let fs_caps = self.filesystem_options()?;
let accelerate_lookup = fs_caps.ignore_case.then(|| index.prepare_icase_backing());
let mut opts = gix_dir::walk::Options::from(options);
let worktree_relative_worktree_dirs_storage;
if let Some(workdir) = self.workdir().filter(|_| opts.for_deletion.is_some()) {
let linked_worktrees = self
.worktrees()
.or_raise(|| message("Could not list worktrees to assure they are no candidates for deletion"))?;
if !linked_worktrees.is_empty() {
let real_workdir = gix_path::realpath_opts(
workdir,
self.options.current_dir_or_empty(),
gix_path::realpath::MAX_SYMLINKS,
)?;
worktree_relative_worktree_dirs_storage = linked_worktrees
.into_iter()
.filter_map(|proxy| proxy.base().ok())
.filter_map(|base| base.strip_prefix(&real_workdir).map(ToOwned::to_owned).ok())
.map(|rela_path| {
Ok(gix_path::to_unix_separators_on_windows(gix_path::into_bstr(rela_path)?).into_owned())
})
.collect::<Result<_>>()?;
opts.worktree_relative_worktree_dirs = Some(&worktree_relative_worktree_dirs_storage);
}
}
let (outcome, traversal_root) = gix_dir::walk(
workdir,
gix_dir::walk::Context {
should_interrupt: Some(should_interrupt),
git_dir_realpath: git_dir_realpath.as_ref(),
current_dir: self.current_dir(),
index,
ignore_case_index_lookup: accelerate_lookup.as_ref(),
pathspec: &mut pathspec.search,
pathspec_attributes: &mut |relative_path, case, is_dir, out| {
let stack = pathspec
.stack
.as_mut()
.expect("can only be called if attributes are used in patterns");
stack
.set_case(case)
.at_entry(relative_path, Some(is_dir_to_mode(is_dir)), &self.objects)
.is_ok_and(|platform| platform.matching_attributes(out))
},
excludes: Some(&mut excludes.inner),
objects: &self.objects,
explicit_traversal_root: (!options.empty_patterns_match_prefix).then_some(workdir),
},
opts,
delegate,
)?;
Ok(dirwalk::Outcome {
dirwalk: outcome,
traversal_root,
excludes,
pathspec,
})
}
pub fn dirwalk_iter(
&self,
index: impl Into<IndexPersistedOrInMemory>,
patterns: impl IntoIterator<Item = impl Into<BString>>,
should_interrupt: OwnedOrStaticAtomicBool,
options: dirwalk::Options,
) -> Result<dirwalk::Iter> {
dirwalk::Iter::new(
self,
index.into(),
patterns.into_iter().map(Into::into).collect(),
should_interrupt,
options,
)
}
}