##// END OF EJS Templates
rust-status: add function for sequential traversal of the working directory...
Raphaël Gomès -
r45014:1debb589 default
parent child Browse files
Show More
@@ -11,18 +11,21 b''
11
11
12 use crate::{
12 use crate::{
13 dirstate::SIZE_FROM_OTHER_PARENT,
13 dirstate::SIZE_FROM_OTHER_PARENT,
14 matchers::Matcher,
14 matchers::{Matcher, VisitChildrenSet},
15 utils::{
15 utils::{
16 files::HgMetadata,
16 files::HgMetadata,
17 hg_path::{
17 hg_path::{
18 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
18 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
19 },
19 },
20 },
20 },
21 CopyMap, DirstateEntry, DirstateMap, EntryState,
21 CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
22 };
22 };
23 use rayon::prelude::*;
23 use rayon::prelude::*;
24 use std::collections::HashSet;
24 use std::borrow::Cow;
25 use std::collections::{HashSet, VecDeque};
25 use std::fs::{read_dir, DirEntry};
26 use std::fs::{read_dir, DirEntry};
27 use std::io::ErrorKind;
28 use std::ops::Deref;
26 use std::path::Path;
29 use std::path::Path;
27
30
28 /// Wrong type of file from a `BadMatch`
31 /// Wrong type of file from a `BadMatch`
@@ -238,6 +241,178 b' pub struct StatusOptions {'
238 /// Whether we are on a filesystem with UNIX-like exec flags
241 /// Whether we are on a filesystem with UNIX-like exec flags
239 pub check_exec: bool,
242 pub check_exec: bool,
240 pub list_clean: bool,
243 pub list_clean: bool,
244 pub list_unknown: bool,
245 pub list_ignored: bool,
246 }
247
248 /// Dispatch a single file found during `traverse`.
249 /// If `file` is a folder that needs to be traversed, it will be pushed into
250 /// `work`.
251 fn traverse_worker<'a>(
252 work: &mut VecDeque<HgPathBuf>,
253 matcher: &impl Matcher,
254 dmap: &DirstateMap,
255 filename: impl AsRef<HgPath>,
256 dir_entry: &DirEntry,
257 ignore_fn: &impl for<'r> Fn(&'r HgPath) -> bool,
258 dir_ignore_fn: &impl for<'r> Fn(&'r HgPath) -> bool,
259 options: StatusOptions,
260 ) -> Option<IoResult<(Cow<'a, HgPath>, Dispatch)>> {
261 let file_type = match dir_entry.file_type() {
262 Ok(x) => x,
263 Err(e) => return Some(Err(e.into())),
264 };
265 let filename = filename.as_ref();
266 let entry_option = dmap.get(filename);
267
268 if file_type.is_dir() {
269 // Do we need to traverse it?
270 if !ignore_fn(&filename) || options.list_ignored {
271 work.push_front(filename.to_owned());
272 }
273 // Nested `if` until `rust-lang/rust#53668` is stable
274 if let Some(entry) = entry_option {
275 // Used to be a file, is now a folder
276 if matcher.matches_everything() || matcher.matches(&filename) {
277 return Some(Ok((
278 Cow::Owned(filename.to_owned()),
279 dispatch_missing(entry.state),
280 )));
281 }
282 }
283 } else if file_type.is_file() || file_type.is_symlink() {
284 if let Some(entry) = entry_option {
285 if matcher.matches_everything() || matcher.matches(&filename) {
286 let metadata = match dir_entry.metadata() {
287 Ok(x) => x,
288 Err(e) => return Some(Err(e.into())),
289 };
290 return Some(Ok((
291 Cow::Owned(filename.to_owned()),
292 dispatch_found(
293 &filename,
294 *entry,
295 HgMetadata::from_metadata(metadata),
296 &dmap.copy_map,
297 options,
298 ),
299 )));
300 }
301 } else if (matcher.matches_everything() || matcher.matches(&filename))
302 && !ignore_fn(&filename)
303 {
304 if (options.list_ignored || matcher.exact_match(&filename))
305 && dir_ignore_fn(&filename)
306 {
307 if options.list_ignored {
308 return Some(Ok((
309 Cow::Owned(filename.to_owned()),
310 Dispatch::Ignored,
311 )));
312 }
313 } else {
314 return Some(Ok((
315 Cow::Owned(filename.to_owned()),
316 Dispatch::Unknown,
317 )));
318 }
319 }
320 } else if let Some(entry) = entry_option {
321 // Used to be a file or a folder, now something else.
322 if matcher.matches_everything() || matcher.matches(&filename) {
323 return Some(Ok((
324 Cow::Owned(filename.to_owned()),
325 dispatch_missing(entry.state),
326 )));
327 }
328 }
329 None
330 }
331
332 /// Walk the working directory recursively to look for changes compared to the
333 /// current `DirstateMap`.
334 fn traverse<'a>(
335 matcher: &(impl Matcher + Sync),
336 root_dir: impl AsRef<Path>,
337 dmap: &DirstateMap,
338 path: impl AsRef<HgPath>,
339 old_results: FastHashMap<Cow<'a, HgPath>, Dispatch>,
340 ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
341 dir_ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
342 options: StatusOptions,
343 ) -> IoResult<FastHashMap<Cow<'a, HgPath>, Dispatch>> {
344 let root_dir = root_dir.as_ref();
345 let mut new_results = FastHashMap::default();
346
347 let mut work = VecDeque::new();
348 work.push_front(path.as_ref().to_owned());
349
350 while let Some(ref directory) = work.pop_front() {
351 if directory.as_bytes() == b".hg" {
352 continue;
353 }
354 let visit_entries = match matcher.visit_children_set(directory) {
355 VisitChildrenSet::Empty => continue,
356 VisitChildrenSet::This | VisitChildrenSet::Recursive => None,
357 VisitChildrenSet::Set(set) => Some(set),
358 };
359 let buf = hg_path_to_path_buf(directory)?;
360 let dir_path = root_dir.join(buf);
361
362 let skip_dot_hg = !directory.as_bytes().is_empty();
363 let entries = match list_directory(dir_path, skip_dot_hg) {
364 Err(e) => match e.kind() {
365 ErrorKind::NotFound | ErrorKind::PermissionDenied => {
366 new_results.insert(
367 Cow::Owned(directory.to_owned()),
368 Dispatch::Bad(BadMatch::OsError(
369 // Unwrapping here is OK because the error always
370 // is a real os error
371 e.raw_os_error().unwrap(),
372 )),
373 );
374 continue;
375 }
376 _ => return Err(e),
377 },
378 Ok(entries) => entries,
379 };
380
381 for (filename, dir_entry) in entries {
382 if let Some(ref set) = visit_entries {
383 if !set.contains(filename.deref()) {
384 continue;
385 }
386 }
387 // TODO normalize
388 let filename = if directory.is_empty() {
389 filename.to_owned()
390 } else {
391 directory.join(&filename)
392 };
393
394 if !old_results.contains_key(filename.deref()) {
395 if let Some((res, dispatch)) = traverse_worker(
396 &mut work,
397 matcher,
398 &dmap,
399 &filename,
400 &dir_entry,
401 &ignore_fn,
402 &dir_ignore_fn,
403 options,
404 )
405 .transpose()?
406 {
407 new_results.insert(res, dispatch);
408 }
409 }
410 }
411 }
412
413 new_results.extend(old_results.into_iter());
414
415 Ok(new_results)
241 }
416 }
242
417
243 /// Stat all entries in the `DirstateMap` and mark them for dispatch into
418 /// Stat all entries in the `DirstateMap` and mark them for dispatch into
General Comments 0
You need to be logged in to leave comments. Login now