Skip to content

Commit 0e10b62

Browse files
committed
feat: a way for status to stop early.
That way, 'is_dirty()` scenarios can be done without wasting too much time.
1 parent 0d01eb2 commit 0e10b62

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

gix-status/src/index_as_worktree/function.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
1+
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
22
use std::{io, marker::PhantomData, path::Path};
33

44
use bstr::BStr;
@@ -23,6 +23,8 @@ use crate::{
2323
/// Note that `index` is updated with the latest seen stat information from the worktree, and its timestamp is adjusted to
2424
/// the current time for which it will be considered fresh as long as it is included which depends on `pathspec`.
2525
///
26+
/// `should_interrupt` can be used to stop all processing.
27+
///
2628
/// ### Note
2729
///
2830
/// Technically, this function does more as this also provides additional information, like whether a file has conflicts,
@@ -43,6 +45,7 @@ pub fn index_as_worktree<'index, T, U, Find, E1, E2>(
4345
find: Find,
4446
progress: &mut dyn gix_features::progress::Progress,
4547
pathspec: impl Pathspec + Send + Clone,
48+
should_interrupt: &AtomicBool,
4649
options: Options,
4750
) -> Result<Outcome, Error>
4851
where
@@ -68,7 +71,7 @@ where
6871
.prefixed_entries_range(pathspec.common_prefix())
6972
.unwrap_or(0..index.entries().len());
7073
let (entries, path_backing) = index.entries_mut_and_pathbacking();
71-
let num_entries = entries.len();
74+
let mut num_entries = entries.len();
7275
let entries = &mut entries[range];
7376

7477
let _span = gix_features::trace::detail!("gix_status::index_as_worktree",
@@ -80,12 +83,13 @@ where
8083
let (skipped_by_pathspec, skipped_by_entry_flags, symlink_metadata_calls, entries_updated) = Default::default();
8184
let (worktree_bytes, worktree_reads, odb_bytes, odb_reads, racy_clean) = Default::default();
8285

86+
num_entries = entries.len();
8387
progress.init(entries.len().into(), gix_features::progress::count("files"));
8488
let count = progress.counter();
8589

8690
in_parallel_if(
8791
|| true, // TODO: heuristic: when is parallelization not worth it? Git says 500 items per thread, but to 20 threads, we can be more fine-grained though.
88-
entries.chunks_mut(chunk_size),
92+
gix_features::interrupt::Iter::new(entries.chunks_mut(chunk_size), should_interrupt),
8993
thread_limit,
9094
{
9195
let options = &options;
@@ -137,6 +141,8 @@ where
137141
)?;
138142

139143
Ok(Outcome {
144+
entries_to_process: num_entries,
145+
entries_processed: count.load(Ordering::Relaxed),
140146
entries_skipped_by_common_prefix,
141147
entries_skipped_by_pathspec: skipped_by_pathspec.load(Ordering::Relaxed),
142148
entries_skipped_by_entry_flags: skipped_by_entry_flags.load(Ordering::Relaxed),

gix-status/src/index_as_worktree/types.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ pub struct Options {
3535
/// Provide additional information collected during the runtime of [`index_as_worktree()`](crate::index_as_worktree()).
3636
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
3737
pub struct Outcome {
38+
/// The total amount of entries that is to be processed.
39+
pub entries_to_process: usize,
40+
/// The amount of entries we actually processed. If this isn't the entire set, the operation was interrupted.
41+
pub entries_processed: usize,
3842
/// The amount of entries we didn't even traverse (and thus update with stat) due to a common prefix in pathspecs.
3943
/// This is similar to the current working directory.
4044
pub entries_skipped_by_common_prefix: usize,

gix-status/tests/status/index_as_worktree.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::sync::atomic::AtomicBool;
12
use std::sync::{
23
atomic::{AtomicUsize, Ordering},
34
Arc,
@@ -75,6 +76,7 @@ fn fixture_filtered_detailed(
7576
|_, _| Ok::<_, std::convert::Infallible>(gix_object::BlobRef { data: &[] }),
7677
&mut gix_features::progress::Discard,
7778
Pathspec(search),
79+
&AtomicBool::default(),
7880
Options {
7981
fs: gix_fs::Capabilities::probe(&git_dir),
8082
stat: TEST_OPTIONS,
@@ -131,6 +133,8 @@ fn removed() {
131133
assert_eq!(
132134
out,
133135
Outcome {
136+
entries_to_process: 4,
137+
entries_processed: 4,
134138
symlink_metadata_calls: 4,
135139
..Default::default()
136140
}
@@ -147,6 +151,8 @@ fn removed() {
147151
assert_eq!(
148152
out,
149153
Outcome {
154+
entries_to_process: 2,
155+
entries_processed: 2,
150156
entries_skipped_by_common_prefix: 2,
151157
symlink_metadata_calls: 2,
152158
..Default::default()
@@ -159,6 +165,8 @@ fn subomdule_nochange() {
159165
assert_eq!(
160166
ignore_racyclean(submodule_fixture("no-change", &[])),
161167
Outcome {
168+
entries_to_process: 2,
169+
entries_processed: 2,
162170
entries_updated: 1,
163171
symlink_metadata_calls: 2,
164172
worktree_bytes: 46,
@@ -176,6 +184,8 @@ fn subomdule_deleted_dir() {
176184
&[(BStr::new(b"m1"), Some(Change::Removed), NO_CONFLICT)]
177185
)),
178186
Outcome {
187+
entries_to_process: 2,
188+
entries_processed: 2,
179189
entries_updated: 1,
180190
symlink_metadata_calls: 2,
181191
worktree_files_read: 1,
@@ -193,6 +203,8 @@ fn subomdule_typechange() {
193203
&[(BStr::new(b"m1"), Some(Change::Type), NO_CONFLICT)]
194204
)),
195205
Outcome {
206+
entries_to_process: 2,
207+
entries_processed: 2,
196208
entries_updated: 1,
197209
symlink_metadata_calls: 2,
198210
worktree_files_read: 1,
@@ -207,6 +219,8 @@ fn subomdule_empty_dir_no_change() {
207219
assert_eq!(
208220
ignore_racyclean(submodule_fixture("empty-dir-no-change", &[])),
209221
Outcome {
222+
entries_to_process: 2,
223+
entries_processed: 2,
210224
entries_updated: 1,
211225
symlink_metadata_calls: 2,
212226
worktree_files_read: 1,
@@ -225,6 +239,8 @@ fn subomdule_empty_dir_no_change_is_passed_to_submodule_handler() {
225239
true,
226240
)),
227241
Outcome {
242+
entries_to_process: 2,
243+
entries_processed: 2,
228244
entries_updated: 1,
229245
symlink_metadata_calls: 2,
230246
worktree_files_read: 1,
@@ -242,6 +258,8 @@ fn intent_to_add() {
242258
&[(BStr::new(b"content"), Some(Change::IntentToAdd), NO_CONFLICT)],
243259
),
244260
Outcome {
261+
entries_to_process: 1,
262+
entries_processed: 1,
245263
symlink_metadata_calls: 1,
246264
..Default::default()
247265
}
@@ -263,6 +281,8 @@ fn conflict() {
263281
)],
264282
),
265283
Outcome {
284+
entries_to_process: 3,
285+
entries_processed: 3,
266286
symlink_metadata_calls: 1,
267287
worktree_files_read: 1,
268288
worktree_bytes: 51,
@@ -314,6 +334,8 @@ fn modified() {
314334
],
315335
),
316336
Outcome {
337+
entries_to_process: 5,
338+
entries_processed: 5,
317339
symlink_metadata_calls: 5,
318340
entries_updated: 1,
319341
worktree_files_read: 2,
@@ -379,6 +401,7 @@ fn racy_git() {
379401
|_, _| Err(std::io::Error::new(std::io::ErrorKind::Other, "no odb access expected")),
380402
&mut gix_features::progress::Discard,
381403
Pathspec::default(),
404+
&AtomicBool::default(),
382405
Options {
383406
fs,
384407
stat: TEST_OPTIONS,
@@ -389,6 +412,8 @@ fn racy_git() {
389412
assert_eq!(
390413
out,
391414
Outcome {
415+
entries_to_process: 1,
416+
entries_processed: 1,
392417
symlink_metadata_calls: 1,
393418
..Default::default()
394419
}
@@ -414,6 +439,7 @@ fn racy_git() {
414439
|_, _| Err(std::io::Error::new(std::io::ErrorKind::Other, "no odb access expected")),
415440
&mut gix_features::progress::Discard,
416441
Pathspec::default(),
442+
&AtomicBool::default(),
417443
Options {
418444
fs,
419445
stat: TEST_OPTIONS,
@@ -424,6 +450,8 @@ fn racy_git() {
424450
assert_eq!(
425451
out,
426452
Outcome {
453+
entries_to_process: 1,
454+
entries_processed: 1,
427455
symlink_metadata_calls: 1,
428456
racy_clean: 1,
429457
worktree_bytes: 3,

0 commit comments

Comments
 (0)