219 fn walk_explicit<'a>( |
219 fn walk_explicit<'a>( |
220 files: Option<&'a HashSet<&HgPath>>, |
220 files: Option<&'a HashSet<&HgPath>>, |
221 dmap: &'a DirstateMap, |
221 dmap: &'a DirstateMap, |
222 root_dir: impl AsRef<Path> + Sync + Send + 'a, |
222 root_dir: impl AsRef<Path> + Sync + Send + 'a, |
223 options: StatusOptions, |
223 options: StatusOptions, |
|
224 traversed_sender: crossbeam::Sender<HgPathBuf>, |
224 ) -> impl ParallelIterator<Item = IoResult<(&'a HgPath, Dispatch)>> { |
225 ) -> impl ParallelIterator<Item = IoResult<(&'a HgPath, Dispatch)>> { |
225 files |
226 files |
226 .unwrap_or(&DEFAULT_WORK) |
227 .unwrap_or(&DEFAULT_WORK) |
227 .par_iter() |
228 .par_iter() |
228 .map(move |filename| { |
229 .map(move |filename| { |
253 ))); |
254 ))); |
254 } |
255 } |
255 Some(Ok((normalized, Dispatch::Unknown))) |
256 Some(Ok((normalized, Dispatch::Unknown))) |
256 } else { |
257 } else { |
257 if file_type.is_dir() { |
258 if file_type.is_dir() { |
|
259 if options.collect_traversed_dirs { |
|
260 // The receiver always outlives the sender, |
|
261 // so unwrap. |
|
262 traversed_sender |
|
263 .send(normalized.to_owned()) |
|
264 .unwrap() |
|
265 } |
258 Some(Ok(( |
266 Some(Ok(( |
259 normalized, |
267 normalized, |
260 Dispatch::Directory { |
268 Dispatch::Directory { |
261 was_file: in_dmap.is_some(), |
269 was_file: in_dmap.is_some(), |
262 }, |
270 }, |
300 /// Whether we are on a filesystem with UNIX-like exec flags |
308 /// Whether we are on a filesystem with UNIX-like exec flags |
301 pub check_exec: bool, |
309 pub check_exec: bool, |
302 pub list_clean: bool, |
310 pub list_clean: bool, |
303 pub list_unknown: bool, |
311 pub list_unknown: bool, |
304 pub list_ignored: bool, |
312 pub list_ignored: bool, |
|
313 /// Whether to collect traversed dirs for applying a callback later. |
|
314 /// Used by `hg purge` for example. |
|
315 pub collect_traversed_dirs: bool, |
305 } |
316 } |
306 |
317 |
307 /// Dispatch a single entry (file, folder, symlink...) found during `traverse`. |
318 /// Dispatch a single entry (file, folder, symlink...) found during `traverse`. |
308 /// If the entry is a folder that needs to be traversed, it will be handled |
319 /// If the entry is a folder that needs to be traversed, it will be handled |
309 /// in a separate thread. |
320 /// in a separate thread. |
317 ignore_fn: &'a IgnoreFnType, |
328 ignore_fn: &'a IgnoreFnType, |
318 dir_ignore_fn: &'a IgnoreFnType, |
329 dir_ignore_fn: &'a IgnoreFnType, |
319 options: StatusOptions, |
330 options: StatusOptions, |
320 filename: HgPathBuf, |
331 filename: HgPathBuf, |
321 dir_entry: DirEntry, |
332 dir_entry: DirEntry, |
|
333 traversed_sender: crossbeam::Sender<HgPathBuf>, |
322 ) -> IoResult<()> { |
334 ) -> IoResult<()> { |
323 let file_type = dir_entry.file_type()?; |
335 let file_type = dir_entry.file_type()?; |
324 let entry_option = dmap.get(&filename); |
336 let entry_option = dmap.get(&filename); |
325 |
337 |
326 if filename.as_bytes() == b".hg" { |
338 if filename.as_bytes() == b".hg" { |
405 ignore_fn: &'a IgnoreFnType, |
418 ignore_fn: &'a IgnoreFnType, |
406 dir_ignore_fn: &'a IgnoreFnType, |
419 dir_ignore_fn: &'a IgnoreFnType, |
407 options: StatusOptions, |
420 options: StatusOptions, |
408 entry_option: Option<&'a DirstateEntry>, |
421 entry_option: Option<&'a DirstateEntry>, |
409 directory: HgPathBuf, |
422 directory: HgPathBuf, |
|
423 traversed_sender: crossbeam::Sender<HgPathBuf>, |
410 ) { |
424 ) { |
411 scope.spawn(move |_| { |
425 scope.spawn(move |_| { |
412 // Nested `if` until `rust-lang/rust#53668` is stable |
426 // Nested `if` until `rust-lang/rust#53668` is stable |
413 if let Some(entry) = entry_option { |
427 if let Some(entry) = entry_option { |
414 // Used to be a file, is now a folder |
428 // Used to be a file, is now a folder |
449 directory: impl AsRef<HgPath>, |
464 directory: impl AsRef<HgPath>, |
450 old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>, |
465 old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>, |
451 ignore_fn: &IgnoreFnType, |
466 ignore_fn: &IgnoreFnType, |
452 dir_ignore_fn: &IgnoreFnType, |
467 dir_ignore_fn: &IgnoreFnType, |
453 options: StatusOptions, |
468 options: StatusOptions, |
|
469 traversed_sender: crossbeam::Sender<HgPathBuf>, |
454 ) -> IoResult<()> { |
470 ) -> IoResult<()> { |
455 let directory = directory.as_ref(); |
471 let directory = directory.as_ref(); |
|
472 |
|
473 if options.collect_traversed_dirs { |
|
474 // The receiver always outlives the sender, so unwrap. |
|
475 traversed_sender.send(directory.to_owned()).unwrap() |
|
476 } |
456 |
477 |
457 let visit_entries = match matcher.visit_children_set(directory) { |
478 let visit_entries = match matcher.visit_children_set(directory) { |
458 VisitChildrenSet::Empty => return Ok(()), |
479 VisitChildrenSet::Empty => return Ok(()), |
459 VisitChildrenSet::This | VisitChildrenSet::Recursive => None, |
480 VisitChildrenSet::This | VisitChildrenSet::Recursive => None, |
460 VisitChildrenSet::Set(set) => Some(set), |
481 VisitChildrenSet::Set(set) => Some(set), |
531 old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>, |
553 old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>, |
532 ignore_fn: &IgnoreFnType, |
554 ignore_fn: &IgnoreFnType, |
533 dir_ignore_fn: &IgnoreFnType, |
555 dir_ignore_fn: &IgnoreFnType, |
534 options: StatusOptions, |
556 options: StatusOptions, |
535 results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>, |
557 results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>, |
|
558 traversed_sender: crossbeam::Sender<HgPathBuf>, |
536 ) -> IoResult<()> { |
559 ) -> IoResult<()> { |
537 let root_dir = root_dir.as_ref(); |
560 let root_dir = root_dir.as_ref(); |
538 |
561 |
539 // The traversal is done in parallel, so use a channel to gather entries. |
562 // The traversal is done in parallel, so use a channel to gather entries. |
540 // `crossbeam::Sender` is `Send`, while `mpsc::Sender` is not. |
563 // `crossbeam::Sender` is `Send`, while `mpsc::Sender` is not. |
638 pub deleted: Vec<Cow<'a, HgPath>>, |
662 pub deleted: Vec<Cow<'a, HgPath>>, |
639 pub clean: Vec<Cow<'a, HgPath>>, |
663 pub clean: Vec<Cow<'a, HgPath>>, |
640 pub ignored: Vec<Cow<'a, HgPath>>, |
664 pub ignored: Vec<Cow<'a, HgPath>>, |
641 pub unknown: Vec<Cow<'a, HgPath>>, |
665 pub unknown: Vec<Cow<'a, HgPath>>, |
642 pub bad: Vec<(Cow<'a, HgPath>, BadMatch)>, |
666 pub bad: Vec<(Cow<'a, HgPath>, BadMatch)>, |
|
667 /// Only filled if `collect_traversed_dirs` is `true` |
|
668 pub traversed: Vec<HgPathBuf>, |
643 } |
669 } |
644 |
670 |
645 #[timed] |
671 #[timed] |
646 fn build_response<'a>( |
672 fn build_response<'a>( |
647 results: impl IntoIterator<Item = (Cow<'a, HgPath>, Dispatch)>, |
673 results: impl IntoIterator<Item = (Cow<'a, HgPath>, Dispatch)>, |
|
674 traversed: Vec<HgPathBuf>, |
648 ) -> (Vec<Cow<'a, HgPath>>, DirstateStatus<'a>) { |
675 ) -> (Vec<Cow<'a, HgPath>>, DirstateStatus<'a>) { |
649 let mut lookup = vec![]; |
676 let mut lookup = vec![]; |
650 let mut modified = vec![]; |
677 let mut modified = vec![]; |
651 let mut added = vec![]; |
678 let mut added = vec![]; |
652 let mut removed = vec![]; |
679 let mut removed = vec![]; |
847 (Box::new(|&_| true), vec![]) |
875 (Box::new(|&_| true), vec![]) |
848 }; |
876 }; |
849 |
877 |
850 let files = matcher.file_set(); |
878 let files = matcher.file_set(); |
851 |
879 |
|
880 // `crossbeam::Sender` is `Send`, while `mpsc::Sender` is not. |
|
881 let (traversed_sender, traversed_recv) = crossbeam::channel::unbounded(); |
|
882 |
852 // Step 1: check the files explicitly mentioned by the user |
883 // Step 1: check the files explicitly mentioned by the user |
853 let explicit = walk_explicit(files, &dmap, root_dir, options); |
884 let explicit = walk_explicit( |
|
885 files, |
|
886 &dmap, |
|
887 root_dir, |
|
888 options, |
|
889 traversed_sender.clone(), |
|
890 ); |
854 |
891 |
855 // Collect results into a `Vec` because we do very few lookups in most |
892 // Collect results into a `Vec` because we do very few lookups in most |
856 // cases. |
893 // cases. |
857 let (work, mut results): (Vec<_>, Vec<_>) = explicit |
894 let (work, mut results): (Vec<_>, Vec<_>) = explicit |
858 .filter_map(Result::ok) |
895 .filter_map(Result::ok) |
909 // and check everything we missed. |
947 // and check everything we missed. |
910 extend_from_dmap(&dmap, root_dir, options, &mut results); |
948 extend_from_dmap(&dmap, root_dir, options, &mut results); |
911 } |
949 } |
912 } |
950 } |
913 |
951 |
914 Ok((build_response(results), warnings)) |
952 // Close the channel |
915 } |
953 drop(traversed_sender); |
|
954 let traversed_dirs = traversed_recv.into_iter().collect(); |
|
955 |
|
956 Ok((build_response(results, traversed_dirs), warnings)) |
|
957 } |