Skip to content

Commit d767d22

Browse files
committed
fix!: leave more control to the user when creating pathspecs
1 parent 6a3d426 commit d767d22

File tree

2 files changed

+55
-25
lines changed

2 files changed

+55
-25
lines changed

gix/src/dirwalk.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct Options {
1717
emit_empty_directories: bool,
1818
classify_untracked_bare_repositories: bool,
1919
emit_collapsed: Option<CollapsedEntriesEmissionMode>,
20+
pub(crate) use_prefix: bool,
2021
}
2122

2223
/// Construction
@@ -34,6 +35,7 @@ impl Options {
3435
emit_empty_directories: false,
3536
classify_untracked_bare_repositories: false,
3637
emit_collapsed: None,
38+
use_prefix: false,
3739
}
3840
}
3941
}
@@ -57,6 +59,14 @@ impl From<Options> for gix_dir::walk::Options {
5759
}
5860

5961
impl Options {
62+
/// If `true`, default `false`, pathspecs and the directory walk itself will be setup to use the [prefix](crate::Repository::prefix).
63+
///
64+
/// This means that the directory walk will be limited to only what's inside the [repository prefix](crate::Repository::prefix).
65+
/// By default, the directory walk will see everything.
66+
pub fn use_prefix(mut self, toggle: bool) -> Self {
67+
self.use_prefix = toggle;
68+
self
69+
}
6070
/// If `toggle` is `true`, we will stop figuring out if any directory that is a candidate for recursion is also a nested repository,
6171
/// which saves time but leads to recurse into it. If `false`, nested repositories will not be traversed.
6272
pub fn recurse_repositories(mut self, toggle: bool) -> Self {

gix/src/repository/dirwalk.rs

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::bstr::BStr;
2-
use crate::{config, dirwalk, Repository};
2+
use crate::{config, dirwalk, AttributeStack, Pathspec, Repository};
33
use std::path::PathBuf;
44

55
/// The error returned by [dirwalk()](Repository::dirwalk()).
@@ -20,6 +20,19 @@ pub enum Error {
2020
FilesystemOptions(#[from] config::boolean::Error),
2121
}
2222

23+
/// The outcome of the [dirwalk()](Repository::dirwalk).
24+
pub struct Outcome<'repo> {
25+
/// The excludes stack used for the dirwalk, for access of `.gitignore` information.
26+
pub excludes: AttributeStack<'repo>,
27+
/// The pathspecs used to guide the operation,
28+
pub pathspec: Pathspec<'repo>,
29+
/// The root actually being used for the traversal, and useful to transform the paths returned for the user.
30+
/// It's always within the [`work-dir`](Repository::work_dir).
31+
pub traversal_root: PathBuf,
32+
/// The actual result of the dirwalk.
33+
pub dirwalk: gix_dir::walk::Outcome,
34+
}
35+
2336
impl Repository {
2437
/// Return default options suitable for performing a directory walk on this repository.
2538
///
@@ -42,54 +55,61 @@ impl Repository {
4255
patterns: impl IntoIterator<Item = impl AsRef<BStr>>,
4356
options: dirwalk::Options,
4457
delegate: &mut dyn gix_dir::walk::Delegate,
45-
) -> Result<(gix_dir::walk::Outcome, PathBuf), Error> {
58+
) -> Result<Outcome<'_>, Error> {
4659
let _span = gix_trace::coarse!("gix::dirwalk");
4760
let workdir = self.work_dir().ok_or(Error::MissinWorkDir)?;
48-
let mut excludes = self
49-
.excludes(
50-
index,
51-
None,
52-
crate::worktree::stack::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped,
53-
)?
54-
.detach();
55-
let (mut pathspec, mut maybe_attributes) = self
56-
.pathspec(
57-
patterns,
58-
true, /* inherit ignore case */
59-
index,
60-
crate::worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
61-
)?
62-
.into_parts();
63-
gix_trace::debug!(patterns = ?pathspec.patterns().map(|p| p.path()).collect::<Vec<_>>());
61+
let mut excludes = self.excludes(
62+
index,
63+
None,
64+
crate::worktree::stack::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped,
65+
)?;
66+
let mut pathspec = self.pathspec(
67+
patterns,
68+
true, /* inherit ignore case */
69+
index,
70+
crate::worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
71+
)?;
72+
gix_trace::debug!(
73+
longest_prefix = ?pathspec.search.longest_common_directory(),
74+
prefix_dir = ?pathspec.search.prefix_directory(),
75+
patterns = ?pathspec.search.patterns().map(gix_pathspec::Pattern::path).collect::<Vec<_>>()
76+
);
6477

6578
let git_dir_realpath =
6679
crate::path::realpath_opts(self.git_dir(), self.current_dir(), crate::path::realpath::MAX_SYMLINKS)?;
6780
let fs_caps = self.filesystem_options()?;
6881
let accelerate_lookup = fs_caps.ignore_case.then(|| index.prepare_icase_backing());
69-
gix_dir::walk(
82+
let (outcome, traversal_root) = gix_dir::walk(
7083
workdir,
7184
gix_dir::walk::Context {
7285
git_dir_realpath: git_dir_realpath.as_ref(),
7386
current_dir: self.current_dir(),
7487
index,
7588
ignore_case_index_lookup: accelerate_lookup.as_ref(),
76-
pathspec: &mut pathspec,
89+
pathspec: &mut pathspec.search,
7790
pathspec_attributes: &mut |relative_path, case, is_dir, out| {
78-
let stack = maybe_attributes
91+
let stack = pathspec
92+
.stack
7993
.as_mut()
8094
.expect("can only be called if attributes are used in patterns");
8195
stack
8296
.set_case(case)
8397
.at_entry(relative_path, Some(is_dir), &self.objects)
8498
.map_or(false, |platform| platform.matching_attributes(out))
8599
},
86-
excludes: Some(&mut excludes),
100+
excludes: Some(&mut excludes.inner),
87101
objects: &self.objects,
88-
explicit_traversal_root: None,
102+
explicit_traversal_root: (!options.use_prefix).then_some(workdir),
89103
},
90104
options.into(),
91105
delegate,
92-
)
93-
.map_err(Into::into)
106+
)?;
107+
108+
Ok(Outcome {
109+
dirwalk: outcome,
110+
traversal_root,
111+
excludes,
112+
pathspec,
113+
})
94114
}
95115
}

0 commit comments

Comments
 (0)