Skip to content

Commit 7fb39a3

Browse files
committed
Improve the way pruned items are handled
That way, consumers can start observing more items, which helps with UI.
1 parent 035f7ad commit 7fb39a3

File tree

9 files changed

+251
-256
lines changed

9 files changed

+251
-256
lines changed

gix-dir/src/entry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub enum Kind {
2727
pub enum Status {
2828
/// The filename of an entry was `.git`, which is generally pruned.
2929
DotGit,
30-
/// The provided pathspec prevented further processing as the path didn't match, or it is a `.git` directory.
30+
/// The provided pathspec prevented further processing as the path didn't match.
3131
/// If this happens, no further checks are done so we wouldn't know if the path is also ignored for example (by mention in `.gitignore`).
3232
Pruned,
3333
/// Always in conjunction with a directory on disk that is also known as cone-mode sparse-checkout exclude marker - i.e. a directory

gix-dir/src/lib.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ pub struct EntryRef<'a> {
3030
/// pruned very early on.
3131
pub status: entry::Status,
3232
/// Further specify the what the entry is on disk, similar to a file mode.
33-
pub disk_kind: entry::Kind,
33+
/// This is `None` if the entry was pruned by a pathspec that could not match, as we then won't invest the time to obtain
34+
/// the kind of the entry on disk.
35+
pub disk_kind: Option<entry::Kind>,
3436
/// The kind of entry according to the index, if tracked. *Usually* the same as `disk_kind`.
3537
pub index_kind: Option<entry::Kind>,
36-
/// Determines how the pathspec matched, which is information that is definitely available for untracked files,
37-
/// but will be `None` for directories and repositories as they don't necessarily have to match to allow recursion,
38-
/// which is why they are not tested.
39-
/// Can also be `None` if no pathspec matched, but will be `Some(PathspecMatch::Excluded)` if a negative pathspec matched.
38+
/// Determines how the pathspec matched.
39+
/// Can also be `None` if no pathspec matched, or if the status check stopped prior to checking for pathspec matches which is the case for [`entry::Status::DotGit`].
40+
/// Note that it can also be `Some(PathspecMatch::Excluded)` if a negative pathspec matched.
4041
pub pathspec_match: Option<entry::PathspecMatch>,
4142
}
4243

@@ -48,7 +49,7 @@ pub struct Entry {
4849
/// The status of entry, most closely related to what we know from `git status`, but not the same.
4950
pub status: entry::Status,
5051
/// Further specify the what the entry is on disk, similar to a file mode.
51-
pub disk_kind: entry::Kind,
52+
pub disk_kind: Option<entry::Kind>,
5253
/// The kind of entry according to the index, if tracked. *Usually* the same as `disk_kind`.
5354
pub index_kind: Option<entry::Kind>,
5455
/// Indicate how the pathspec matches the entry. See more in [`EntryRef::pathspec_match`].

gix-dir/src/walk/classify.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl From<&Entry> for Outcome {
8989
fn from(e: &Entry) -> Self {
9090
Outcome {
9191
status: e.status,
92-
disk_kind: e.disk_kind.into(),
92+
disk_kind: e.disk_kind,
9393
index_kind: e.index_kind,
9494
pathspec_match: e.pathspec_match,
9595
}
@@ -147,26 +147,27 @@ pub fn path(
147147

148148
maybe_status = maybe_status
149149
.or_else(|| (index_kind.map(|k| k.is_dir()) == kind.map(|k| k.is_dir())).then_some(entry::Status::Tracked));
150+
151+
// We always check the pathspec to have the value filled in reliably.
152+
out.pathspec_match = ctx
153+
.pathspec
154+
.pattern_matching_relative_path(
155+
rela_path.as_bstr(),
156+
disk_kind.map(|ft| ft.is_dir()),
157+
ctx.pathspec_attributes,
158+
)
159+
.map(|m| {
160+
if m.is_excluded() {
161+
PathspecMatch::Excluded
162+
} else {
163+
m.kind.into()
164+
}
165+
});
150166
if let Some(status) = maybe_status {
151167
return Ok(out.with_status(status).with_kind(kind, index_kind));
152168
}
153169
debug_assert!(maybe_status.is_none(), "It only communicates a single stae right now");
154170

155-
let mut do_pathspec_match = || {
156-
ctx.pathspec
157-
.pattern_matching_relative_path(
158-
rela_path.as_bstr(),
159-
disk_kind.map(|ft| ft.is_dir()),
160-
ctx.pathspec_attributes,
161-
)
162-
.map(|m| {
163-
if m.is_excluded() {
164-
PathspecMatch::Excluded
165-
} else {
166-
m.kind.into()
167-
}
168-
})
169-
};
170171
let mut maybe_upgrade_to_repository = |current_kind| {
171172
if recurse_repositories {
172173
return current_kind;
@@ -201,7 +202,6 @@ pub fn path(
201202
.map_err(Error::ExcludesAccess)?
202203
{
203204
if emit_ignored.is_some() {
204-
out.pathspec_match = do_pathspec_match();
205205
if kind.map_or(false, |d| d.is_dir()) && out.pathspec_match.is_none() {
206206
// we have patterns that didn't match at all. Try harder.
207207
out.pathspec_match = ctx
@@ -223,11 +223,8 @@ pub fn path(
223223

224224
if kind.map_or(false, |ft| ft.is_dir()) {
225225
kind = maybe_upgrade_to_repository(kind);
226-
} else {
227-
out.pathspec_match = do_pathspec_match();
228-
if out.pathspec_match.is_none() {
229-
status = entry::Status::Pruned;
230-
}
226+
} else if out.pathspec_match.is_none() {
227+
status = entry::Status::Pruned;
231228
}
232229
Ok(out.with_status(status).with_kind(kind, index_kind))
233230
}

gix-dir/src/walk/function.rs

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -126,20 +126,13 @@ pub(super) fn can_recurse(
126126
for_deletion: Option<ForDeletionMode>,
127127
delegate: &mut dyn Delegate,
128128
) -> bool {
129-
let disk_kind = match info.disk_kind {
130-
None => {
131-
// NOTE: assume something is wrong with the entry on disk, so just skip it.
132-
return false;
133-
}
134-
Some(kind) => kind,
135-
};
136-
if !disk_kind.is_dir() {
129+
if info.disk_kind.map_or(true, |k| !k.is_dir()) {
137130
return false;
138131
}
139132
let entry = EntryRef {
140133
rela_path: Cow::Borrowed(rela_path),
141134
status: info.status,
142-
disk_kind,
135+
disk_kind: info.disk_kind,
143136
index_kind: info.index_kind,
144137
pathspec_match: info.pathspec_match,
145138
};
@@ -157,29 +150,21 @@ pub(super) fn emit_entry(
157150
emit_tracked,
158151
emit_ignored,
159152
emit_empty_directories,
160-
for_deletion,
161153
..
162154
}: Options,
163155
out: &mut Outcome,
164156
delegate: &mut dyn Delegate,
165157
) -> Action {
166158
out.seen_entries += 1;
167-
let kind = match info.disk_kind {
168-
Some(kind) => kind,
169-
None => {
170-
// NOTE: assume something is wrong with the entry on disk, so just skip it.
171-
return Action::Continue;
172-
}
173-
};
174-
175-
if for_deletion.is_some() && info.pathspec_match.is_none() {
176-
return Action::Continue;
177-
}
178159

179-
if (!emit_empty_directories && kind == entry::Kind::EmptyDirectory
160+
if (!emit_empty_directories && info.disk_kind == Some(entry::Kind::EmptyDirectory)
180161
|| !emit_tracked && info.status == entry::Status::Tracked)
181162
|| emit_ignored.is_none() && matches!(info.status, entry::Status::Ignored(_))
182-
|| !emit_pruned && info.status.is_pruned()
163+
|| !emit_pruned
164+
&& (info.status.is_pruned()
165+
|| info
166+
.pathspec_match
167+
.map_or(true, |m| m == entry::PathspecMatch::Excluded))
183168
{
184169
return Action::Continue;
185170
}
@@ -189,7 +174,7 @@ pub(super) fn emit_entry(
189174
EntryRef {
190175
rela_path,
191176
status: info.status,
192-
disk_kind: kind,
177+
disk_kind: info.disk_kind,
193178
index_kind: info.index_kind,
194179
pathspec_match: info.pathspec_match,
195180
},

gix-dir/src/walk/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub trait Delegate {
7373
fn can_recurse(&mut self, entry: EntryRef<'_>, for_deletion: Option<ForDeletionMode>) -> bool {
7474
entry
7575
.status
76-
.can_recurse(Some(entry.disk_kind), entry.pathspec_match, for_deletion)
76+
.can_recurse(entry.disk_kind, entry.pathspec_match, for_deletion)
7777
}
7878
}
7979

gix-dir/src/walk/readdir.rs

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,11 @@ pub(super) struct State {
103103
impl State {
104104
/// Hold the entry with the given `status` if it's a candidate for collapsing the containing directory.
105105
fn held_for_directory_collapse(&mut self, rela_path: &BStr, info: classify::Outcome, opts: &Options) -> bool {
106-
let kind = match info.disk_kind {
107-
Some(kind) => kind,
108-
None => {
109-
// NOTE: this can be a `.git` directory or file, and we don't get the file type for it, or it's pruned files.
110-
return false;
111-
}
112-
};
113-
114106
if opts.should_hold(info.status) {
115107
self.on_hold.push(Entry {
116108
rela_path: rela_path.to_owned(),
117109
status: info.status,
118-
disk_kind: kind,
110+
disk_kind: info.disk_kind,
119111
index_kind: info.index_kind,
120112
pathspec_match: info.pathspec_match,
121113
});
@@ -155,7 +147,7 @@ impl Mark {
155147
delegate: &mut dyn walk::Delegate,
156148
) -> walk::Action {
157149
if num_entries == 0 {
158-
let mut empty_info = classify::Outcome {
150+
let empty_info = classify::Outcome {
159151
disk_kind: if num_entries == 0 {
160152
assert_ne!(
161153
dir_info.disk_kind,
@@ -168,19 +160,11 @@ impl Mark {
168160
},
169161
..dir_info
170162
};
171-
debug_assert!(
172-
dir_info.pathspec_match.is_none(),
173-
"we don't execute pathspecs on directories as they not emitted, unless they are empty"
174-
);
175-
empty_info.pathspec_match = ctx
176-
.pathspec
177-
.directory_matches_prefix(dir_rela_path.as_bstr(), true)
178-
.then_some(PathspecMatch::Always);
179163
if opts.should_hold(empty_info.status) {
180164
state.on_hold.push(Entry {
181165
rela_path: dir_rela_path.to_owned(),
182166
status: empty_info.status,
183-
disk_kind: empty_info.disk_kind.expect("known by now"),
167+
disk_kind: empty_info.disk_kind,
184168
index_kind: empty_info.index_kind,
185169
pathspec_match: empty_info.pathspec_match,
186170
});
@@ -244,7 +228,7 @@ impl Mark {
244228
.map(|e| (e.disk_kind, e.status, e.pathspec_match))
245229
{
246230
entries += 1;
247-
if kind == entry::Kind::Repository {
231+
if kind == Some(entry::Kind::Repository) {
248232
*prevent_collapse = true;
249233
return None;
250234
}
@@ -325,7 +309,7 @@ impl Mark {
325309
state.on_hold.push(Entry {
326310
rela_path: dir_rela_path.to_owned(),
327311
status: dir_status,
328-
disk_kind: dir_info.disk_kind.expect("set for directories"),
312+
disk_kind: dir_info.disk_kind,
329313
index_kind: dir_info.index_kind,
330314
pathspec_match: dir_pathspec_match,
331315
});

gix-dir/tests/dir_walk_cwd.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::walk_utils::{collect, entrya, fixture, options};
1+
use crate::walk_utils::{collect, entry, fixture, options};
22
use gix_dir::entry::Kind::File;
33
use gix_dir::entry::Status::Untracked;
44
use gix_dir::walk;
@@ -24,9 +24,9 @@ fn prefixes_work_as_expected() -> gix_testtools::Result {
2424
assert_eq!(
2525
&entries,
2626
&[
27-
entrya("d/a", Untracked, File),
28-
entrya("d/b", Untracked, File),
29-
entrya("d/d/a", Untracked, File),
27+
entry("d/a", Untracked, File),
28+
entry("d/b", Untracked, File),
29+
entry("d/d/a", Untracked, File),
3030
]
3131
);
3232
Ok(())

0 commit comments

Comments
 (0)