##// END OF EJS Templates
rust-status: don't bubble up os errors, translate them to bad matches...
Raphaël Gomès -
r46466:5c736ba5 default
parent child Browse files
Show More
@@ -109,6 +109,10 b" pub type HgPathCow<'a> = Cow<'a, HgPath>"
109 /// A path with its computed ``Dispatch`` information
109 /// A path with its computed ``Dispatch`` information
110 type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch);
110 type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch);
111
111
112 /// The conversion from `HgPath` to a real fs path failed.
113 /// `22` is the error code for "Invalid argument"
114 const INVALID_PATH_DISPATCH: Dispatch = Dispatch::Bad(BadMatch::OsError(22));
115
112 /// Dates and times that are outside the 31-bit signed range are compared
116 /// Dates and times that are outside the 31-bit signed range are compared
113 /// modulo 2^31. This should prevent hg from behaving badly with very large
117 /// modulo 2^31. This should prevent hg from behaving badly with very large
114 /// files or corrupt dates while still having a high probability of detecting
118 /// files or corrupt dates while still having a high probability of detecting
@@ -217,6 +221,12 b' fn dispatch_missing(state: EntryState) -'
217 }
221 }
218 }
222 }
219
223
224 fn dispatch_os_error(e: &std::io::Error) -> Dispatch {
225 Dispatch::Bad(BadMatch::OsError(
226 e.raw_os_error().expect("expected real OS error"),
227 ))
228 }
229
220 lazy_static! {
230 lazy_static! {
221 static ref DEFAULT_WORK: HashSet<&'static HgPath> = {
231 static ref DEFAULT_WORK: HashSet<&'static HgPath> = {
222 let mut h = HashSet::new();
232 let mut h = HashSet::new();
@@ -372,13 +382,18 b' where'
372 .file_set()
382 .file_set()
373 .unwrap_or(&DEFAULT_WORK)
383 .unwrap_or(&DEFAULT_WORK)
374 .par_iter()
384 .par_iter()
375 .map(|&filename| -> Option<IoResult<_>> {
385 .flat_map(|&filename| -> Option<_> {
376 // TODO normalization
386 // TODO normalization
377 let normalized = filename;
387 let normalized = filename;
378
388
379 let buf = match hg_path_to_path_buf(normalized) {
389 let buf = match hg_path_to_path_buf(normalized) {
380 Ok(x) => x,
390 Ok(x) => x,
381 Err(e) => return Some(Err(e.into())),
391 Err(_) => {
392 return Some((
393 Cow::Borrowed(normalized),
394 INVALID_PATH_DISPATCH,
395 ))
396 }
382 };
397 };
383 let target = self.root_dir.join(buf);
398 let target = self.root_dir.join(buf);
384 let st = target.symlink_metadata();
399 let st = target.symlink_metadata();
@@ -389,7 +404,7 b' where'
389 return if file_type.is_file() || file_type.is_symlink()
404 return if file_type.is_file() || file_type.is_symlink()
390 {
405 {
391 if let Some(entry) = in_dmap {
406 if let Some(entry) = in_dmap {
392 return Some(Ok((
407 return Some((
393 Cow::Borrowed(normalized),
408 Cow::Borrowed(normalized),
394 dispatch_found(
409 dispatch_found(
395 &normalized,
410 &normalized,
@@ -398,26 +413,26 b' where'
398 &self.dmap.copy_map,
413 &self.dmap.copy_map,
399 self.options,
414 self.options,
400 ),
415 ),
401 )));
416 ));
402 }
417 }
403 Some(Ok((
418 Some((
404 Cow::Borrowed(normalized),
419 Cow::Borrowed(normalized),
405 Dispatch::Unknown,
420 Dispatch::Unknown,
406 )))
421 ))
407 } else if file_type.is_dir() {
422 } else if file_type.is_dir() {
408 if self.options.collect_traversed_dirs {
423 if self.options.collect_traversed_dirs {
409 traversed_sender
424 traversed_sender
410 .send(normalized.to_owned())
425 .send(normalized.to_owned())
411 .expect("receiver should outlive sender");
426 .expect("receiver should outlive sender");
412 }
427 }
413 Some(Ok((
428 Some((
414 Cow::Borrowed(normalized),
429 Cow::Borrowed(normalized),
415 Dispatch::Directory {
430 Dispatch::Directory {
416 was_file: in_dmap.is_some(),
431 was_file: in_dmap.is_some(),
417 },
432 },
418 )))
433 ))
419 } else {
434 } else {
420 Some(Ok((
435 Some((
421 Cow::Borrowed(normalized),
436 Cow::Borrowed(normalized),
422 Dispatch::Bad(BadMatch::BadType(
437 Dispatch::Bad(BadMatch::BadType(
423 // TODO do more than unknown
438 // TODO do more than unknown
@@ -428,22 +443,20 b' where'
428 // users.
443 // users.
429 BadType::Unknown,
444 BadType::Unknown,
430 )),
445 )),
431 )))
446 ))
432 };
447 };
433 }
448 }
434 Err(_) => {
449 Err(_) => {
435 if let Some(entry) = in_dmap {
450 if let Some(entry) = in_dmap {
436 return Some(Ok((
451 return Some((
437 Cow::Borrowed(normalized),
452 Cow::Borrowed(normalized),
438 dispatch_missing(entry.state),
453 dispatch_missing(entry.state),
439 )));
454 ));
440 }
455 }
441 }
456 }
442 };
457 };
443 None
458 None
444 })
459 })
445 .flatten()
446 .filter_map(Result::ok)
447 .partition(|(_, dispatch)| match dispatch {
460 .partition(|(_, dispatch)| match dispatch {
448 Dispatch::Directory { .. } => true,
461 Dispatch::Directory { .. } => true,
449 _ => false,
462 _ => false,
@@ -462,7 +475,7 b' where'
462 old_results: &FastHashMap<HgPathCow<'a>, Dispatch>,
475 old_results: &FastHashMap<HgPathCow<'a>, Dispatch>,
463 results: &mut Vec<DispatchedPath<'a>>,
476 results: &mut Vec<DispatchedPath<'a>>,
464 traversed_sender: crossbeam::Sender<HgPathBuf>,
477 traversed_sender: crossbeam::Sender<HgPathBuf>,
465 ) -> IoResult<()> {
478 ) {
466 // The traversal is done in parallel, so use a channel to gather
479 // The traversal is done in parallel, so use a channel to gather
467 // entries. `crossbeam::Sender` is `Sync`, while `mpsc::Sender`
480 // entries. `crossbeam::Sender` is `Sync`, while `mpsc::Sender`
468 // is not.
481 // is not.
@@ -474,25 +487,17 b' where'
474 path,
487 path,
475 &old_results,
488 &old_results,
476 traversed_sender,
489 traversed_sender,
477 )?;
490 );
478
491
479 // Disconnect the channel so the receiver stops waiting
492 // Disconnect the channel so the receiver stops waiting
480 drop(files_transmitter);
493 drop(files_transmitter);
481
494
482 // TODO don't collect. Find a way of replicating the behavior of
495 let new_results = files_receiver
483 // `itertools::process_results`, but for `rayon::ParallelIterator`
496 .into_iter()
484 let new_results: IoResult<Vec<(Cow<HgPath>, Dispatch)>> =
497 .par_bridge()
485 files_receiver
498 .map(|(f, d)| (Cow::Owned(f), d));
486 .into_iter()
487 .map(|item| {
488 let (f, d) = item?;
489 Ok((Cow::Owned(f), d))
490 })
491 .collect();
492
499
493 results.par_extend(new_results?);
500 results.par_extend(new_results);
494
495 Ok(())
496 }
501 }
497
502
498 /// Dispatch a single entry (file, folder, symlink...) found during
503 /// Dispatch a single entry (file, folder, symlink...) found during
@@ -501,7 +506,7 b' where'
501 fn handle_traversed_entry<'b>(
506 fn handle_traversed_entry<'b>(
502 &'a self,
507 &'a self,
503 scope: &rayon::Scope<'b>,
508 scope: &rayon::Scope<'b>,
504 files_sender: &'b crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
509 files_sender: &'b crossbeam::Sender<(HgPathBuf, Dispatch)>,
505 old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
510 old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
506 filename: HgPathBuf,
511 filename: HgPathBuf,
507 dir_entry: DirEntry,
512 dir_entry: DirEntry,
@@ -534,7 +539,7 b' where'
534 {
539 {
535 let metadata = dir_entry.metadata()?;
540 let metadata = dir_entry.metadata()?;
536 files_sender
541 files_sender
537 .send(Ok((
542 .send((
538 filename.to_owned(),
543 filename.to_owned(),
539 dispatch_found(
544 dispatch_found(
540 &filename,
545 &filename,
@@ -543,7 +548,7 b' where'
543 &self.dmap.copy_map,
548 &self.dmap.copy_map,
544 self.options,
549 self.options,
545 ),
550 ),
546 )))
551 ))
547 .unwrap();
552 .unwrap();
548 }
553 }
549 } else if (self.matcher.matches_everything()
554 } else if (self.matcher.matches_everything()
@@ -556,17 +561,17 b' where'
556 {
561 {
557 if self.options.list_ignored {
562 if self.options.list_ignored {
558 files_sender
563 files_sender
559 .send(Ok((filename.to_owned(), Dispatch::Ignored)))
564 .send((filename.to_owned(), Dispatch::Ignored))
560 .unwrap();
565 .unwrap();
561 }
566 }
562 } else if self.options.list_unknown {
567 } else if self.options.list_unknown {
563 files_sender
568 files_sender
564 .send(Ok((filename.to_owned(), Dispatch::Unknown)))
569 .send((filename.to_owned(), Dispatch::Unknown))
565 .unwrap();
570 .unwrap();
566 }
571 }
567 } else if self.is_ignored(&filename) && self.options.list_ignored {
572 } else if self.is_ignored(&filename) && self.options.list_ignored {
568 files_sender
573 files_sender
569 .send(Ok((filename.to_owned(), Dispatch::Ignored)))
574 .send((filename.to_owned(), Dispatch::Ignored))
570 .unwrap();
575 .unwrap();
571 }
576 }
572 } else if let Some(entry) = entry_option {
577 } else if let Some(entry) = entry_option {
@@ -575,10 +580,7 b' where'
575 || self.matcher.matches(&filename)
580 || self.matcher.matches(&filename)
576 {
581 {
577 files_sender
582 files_sender
578 .send(Ok((
583 .send((filename.to_owned(), dispatch_missing(entry.state)))
579 filename.to_owned(),
580 dispatch_missing(entry.state),
581 )))
582 .unwrap();
584 .unwrap();
583 }
585 }
584 }
586 }
@@ -590,7 +592,7 b' where'
590 fn handle_traversed_dir<'b>(
592 fn handle_traversed_dir<'b>(
591 &'a self,
593 &'a self,
592 scope: &rayon::Scope<'b>,
594 scope: &rayon::Scope<'b>,
593 files_sender: &'b crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
595 files_sender: &'b crossbeam::Sender<(HgPathBuf, Dispatch)>,
594 old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
596 old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
595 entry_option: Option<&'a DirstateEntry>,
597 entry_option: Option<&'a DirstateEntry>,
596 directory: HgPathBuf,
598 directory: HgPathBuf,
@@ -606,10 +608,10 b' where'
606 || self.matcher.matches(&directory)
608 || self.matcher.matches(&directory)
607 {
609 {
608 files_sender
610 files_sender
609 .send(Ok((
611 .send((
610 directory.to_owned(),
612 directory.to_owned(),
611 dispatch_missing(entry.state),
613 dispatch_missing(entry.state),
612 )))
614 ))
613 .unwrap();
615 .unwrap();
614 }
616 }
615 }
617 }
@@ -621,7 +623,6 b' where'
621 &old_results,
623 &old_results,
622 traversed_sender,
624 traversed_sender,
623 )
625 )
624 .unwrap_or_else(|e| files_sender.send(Err(e)).unwrap())
625 }
626 }
626 });
627 });
627 }
628 }
@@ -630,11 +631,11 b' where'
630 /// entries in a separate thread.
631 /// entries in a separate thread.
631 fn traverse_dir(
632 fn traverse_dir(
632 &self,
633 &self,
633 files_sender: &crossbeam::Sender<IoResult<(HgPathBuf, Dispatch)>>,
634 files_sender: &crossbeam::Sender<(HgPathBuf, Dispatch)>,
634 directory: impl AsRef<HgPath>,
635 directory: impl AsRef<HgPath>,
635 old_results: &FastHashMap<Cow<HgPath>, Dispatch>,
636 old_results: &FastHashMap<Cow<HgPath>, Dispatch>,
636 traversed_sender: crossbeam::Sender<HgPathBuf>,
637 traversed_sender: crossbeam::Sender<HgPathBuf>,
637 ) -> IoResult<()> {
638 ) {
638 let directory = directory.as_ref();
639 let directory = directory.as_ref();
639
640
640 if self.options.collect_traversed_dirs {
641 if self.options.collect_traversed_dirs {
@@ -644,38 +645,33 b' where'
644 }
645 }
645
646
646 let visit_entries = match self.matcher.visit_children_set(directory) {
647 let visit_entries = match self.matcher.visit_children_set(directory) {
647 VisitChildrenSet::Empty => return Ok(()),
648 VisitChildrenSet::Empty => return,
648 VisitChildrenSet::This | VisitChildrenSet::Recursive => None,
649 VisitChildrenSet::This | VisitChildrenSet::Recursive => None,
649 VisitChildrenSet::Set(set) => Some(set),
650 VisitChildrenSet::Set(set) => Some(set),
650 };
651 };
651 let buf = hg_path_to_path_buf(directory)?;
652 let buf = match hg_path_to_path_buf(directory) {
653 Ok(b) => b,
654 Err(_) => {
655 files_sender
656 .send((directory.to_owned(), INVALID_PATH_DISPATCH))
657 .expect("receiver should outlive sender");
658 return;
659 }
660 };
652 let dir_path = self.root_dir.join(buf);
661 let dir_path = self.root_dir.join(buf);
653
662
654 let skip_dot_hg = !directory.as_bytes().is_empty();
663 let skip_dot_hg = !directory.as_bytes().is_empty();
655 let entries = match list_directory(dir_path, skip_dot_hg) {
664 let entries = match list_directory(dir_path, skip_dot_hg) {
656 Err(e) => {
665 Err(e) => {
657 return match e.kind() {
666 files_sender
658 ErrorKind::NotFound | ErrorKind::PermissionDenied => {
667 .send((directory.to_owned(), dispatch_os_error(&e)))
659 files_sender
668 .expect("receiver should outlive sender");
660 .send(Ok((
669 return;
661 directory.to_owned(),
662 Dispatch::Bad(BadMatch::OsError(
663 // Unwrapping here is OK because the error
664 // always is a
665 // real os error
666 e.raw_os_error().unwrap(),
667 )),
668 )))
669 .expect("receiver should outlive sender");
670 Ok(())
671 }
672 _ => Err(e),
673 };
674 }
670 }
675 Ok(entries) => entries,
671 Ok(entries) => entries,
676 };
672 };
677
673
678 rayon::scope(|scope| -> IoResult<()> {
674 rayon::scope(|scope| {
679 for (filename, dir_entry) in entries {
675 for (filename, dir_entry) in entries {
680 if let Some(ref set) = visit_entries {
676 if let Some(ref set) = visit_entries {
681 if !set.contains(filename.deref()) {
677 if !set.contains(filename.deref()) {
@@ -690,17 +686,26 b' where'
690 };
686 };
691
687
692 if !old_results.contains_key(filename.deref()) {
688 if !old_results.contains_key(filename.deref()) {
693 self.handle_traversed_entry(
689 match self.handle_traversed_entry(
694 scope,
690 scope,
695 files_sender,
691 files_sender,
696 old_results,
692 old_results,
697 filename,
693 filename,
698 dir_entry,
694 dir_entry,
699 traversed_sender.clone(),
695 traversed_sender.clone(),
700 )?;
696 ) {
697 Err(e) => {
698 files_sender
699 .send((
700 directory.to_owned(),
701 dispatch_os_error(&e),
702 ))
703 .expect("receiver should outlive sender");
704 }
705 Ok(_) => {}
706 }
701 }
707 }
702 }
708 }
703 Ok(())
704 })
709 })
705 }
710 }
706
711
@@ -716,14 +721,23 b' where'
716 .fs_iter(self.root_dir.clone())
721 .fs_iter(self.root_dir.clone())
717 .par_bridge()
722 .par_bridge()
718 .filter(|(path, _)| self.matcher.matches(path))
723 .filter(|(path, _)| self.matcher.matches(path))
719 .flat_map(move |(filename, shortcut)| {
724 .map(move |(filename, shortcut)| {
720 let entry = match shortcut {
725 let entry = match shortcut {
721 StatusShortcut::Entry(e) => e,
726 StatusShortcut::Entry(e) => e,
722 StatusShortcut::Dispatch(d) => {
727 StatusShortcut::Dispatch(d) => {
723 return Ok((Cow::Owned(filename), d))
728 return (Cow::Owned(filename), d)
724 }
729 }
725 };
730 };
726 let filename_as_path = hg_path_to_path_buf(&filename)?;
731 let filename_as_path = match hg_path_to_path_buf(&filename)
732 {
733 Ok(f) => f,
734 Err(_) => {
735 return (
736 Cow::Owned(filename),
737 INVALID_PATH_DISPATCH,
738 )
739 }
740 };
727 let meta = self
741 let meta = self
728 .root_dir
742 .root_dir
729 .join(filename_as_path)
743 .join(filename_as_path)
@@ -734,10 +748,10 b' where'
734 if !(m.file_type().is_file()
748 if !(m.file_type().is_file()
735 || m.file_type().is_symlink()) =>
749 || m.file_type().is_symlink()) =>
736 {
750 {
737 Ok((
751 (
738 Cow::Owned(filename),
752 Cow::Owned(filename),
739 dispatch_missing(entry.state),
753 dispatch_missing(entry.state),
740 ))
754 )
741 }
755 }
742 Ok(m) => {
756 Ok(m) => {
743 let dispatch = dispatch_found(
757 let dispatch = dispatch_found(
@@ -747,7 +761,7 b' where'
747 &self.dmap.copy_map,
761 &self.dmap.copy_map,
748 self.options,
762 self.options,
749 );
763 );
750 Ok((Cow::Owned(filename), dispatch))
764 (Cow::Owned(filename), dispatch)
751 }
765 }
752 Err(e)
766 Err(e)
753 if e.kind() == ErrorKind::NotFound
767 if e.kind() == ErrorKind::NotFound
@@ -758,12 +772,14 b' where'
758 // It happens if the dirstate contains `foo/bar`
772 // It happens if the dirstate contains `foo/bar`
759 // and foo is not a
773 // and foo is not a
760 // directory
774 // directory
761 Ok((
775 (
762 Cow::Owned(filename),
776 Cow::Owned(filename),
763 dispatch_missing(entry.state),
777 dispatch_missing(entry.state),
764 ))
778 )
765 }
779 }
766 Err(e) => Err(e),
780 Err(e) => {
781 (Cow::Owned(filename), dispatch_os_error(&e))
782 }
767 }
783 }
768 }),
784 }),
769 );
785 );
@@ -776,10 +792,18 b' where'
776 #[cfg(not(feature = "dirstate-tree"))]
792 #[cfg(not(feature = "dirstate-tree"))]
777 #[timed]
793 #[timed]
778 pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) {
794 pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) {
779 results.par_extend(self.dmap.par_iter().flat_map(
795 results.par_extend(self.dmap.par_iter().map(
780 move |(filename, entry)| {
796 move |(filename, entry)| {
781 let filename: &HgPath = filename;
797 let filename: &HgPath = filename;
782 let filename_as_path = hg_path_to_path_buf(filename)?;
798 let filename_as_path = match hg_path_to_path_buf(filename) {
799 Ok(f) => f,
800 Err(_) => {
801 return (
802 Cow::Borrowed(filename),
803 INVALID_PATH_DISPATCH,
804 )
805 }
806 };
783 let meta =
807 let meta =
784 self.root_dir.join(filename_as_path).symlink_metadata();
808 self.root_dir.join(filename_as_path).symlink_metadata();
785 match meta {
809 match meta {
@@ -787,12 +811,12 b' where'
787 if !(m.file_type().is_file()
811 if !(m.file_type().is_file()
788 || m.file_type().is_symlink()) =>
812 || m.file_type().is_symlink()) =>
789 {
813 {
790 Ok((
814 (
791 Cow::Borrowed(filename),
815 Cow::Borrowed(filename),
792 dispatch_missing(entry.state),
816 dispatch_missing(entry.state),
793 ))
817 )
794 }
818 }
795 Ok(m) => Ok((
819 Ok(m) => (
796 Cow::Borrowed(filename),
820 Cow::Borrowed(filename),
797 dispatch_found(
821 dispatch_found(
798 filename,
822 filename,
@@ -801,7 +825,7 b' where'
801 &self.dmap.copy_map,
825 &self.dmap.copy_map,
802 self.options,
826 self.options,
803 ),
827 ),
804 )),
828 ),
805 Err(e)
829 Err(e)
806 if e.kind() == ErrorKind::NotFound
830 if e.kind() == ErrorKind::NotFound
807 || e.raw_os_error() == Some(20) =>
831 || e.raw_os_error() == Some(20) =>
@@ -811,12 +835,12 b' where'
811 // It happens if the dirstate contains `foo/bar`
835 // It happens if the dirstate contains `foo/bar`
812 // and foo is not a
836 // and foo is not a
813 // directory
837 // directory
814 Ok((
838 (
815 Cow::Borrowed(filename),
839 Cow::Borrowed(filename),
816 dispatch_missing(entry.state),
840 dispatch_missing(entry.state),
817 ))
841 )
818 }
842 }
819 Err(e) => Err(e),
843 Err(e) => (Cow::Borrowed(filename), dispatch_os_error(&e)),
820 }
844 }
821 },
845 },
822 ));
846 ));
@@ -830,10 +854,7 b' where'
830 /// `extend` in timings
854 /// `extend` in timings
831 #[cfg(not(feature = "dirstate-tree"))]
855 #[cfg(not(feature = "dirstate-tree"))]
832 #[timed]
856 #[timed]
833 pub fn handle_unknowns(
857 pub fn handle_unknowns(&self, results: &mut Vec<DispatchedPath<'a>>) {
834 &self,
835 results: &mut Vec<DispatchedPath<'a>>,
836 ) -> IoResult<()> {
837 let to_visit: Vec<(&HgPath, &DirstateEntry)> =
858 let to_visit: Vec<(&HgPath, &DirstateEntry)> =
838 if results.is_empty() && self.matcher.matches_everything() {
859 if results.is_empty() && self.matcher.matches_everything() {
839 self.dmap.iter().map(|(f, e)| (f.deref(), e)).collect()
860 self.dmap.iter().map(|(f, e)| (f.deref(), e)).collect()
@@ -857,21 +878,23 b' where'
857
878
858 let path_auditor = PathAuditor::new(&self.root_dir);
879 let path_auditor = PathAuditor::new(&self.root_dir);
859
880
860 // TODO don't collect. Find a way of replicating the behavior of
881 let new_results = to_visit.into_par_iter().filter_map(
861 // `itertools::process_results`, but for `rayon::ParallelIterator`
882 |(filename, entry)| -> Option<_> {
862 let new_results: IoResult<Vec<_>> = to_visit
863 .into_par_iter()
864 .filter_map(|(filename, entry)| -> Option<IoResult<_>> {
865 // Report ignored items in the dmap as long as they are not
883 // Report ignored items in the dmap as long as they are not
866 // under a symlink directory.
884 // under a symlink directory.
867 if path_auditor.check(filename) {
885 if path_auditor.check(filename) {
868 // TODO normalize for case-insensitive filesystems
886 // TODO normalize for case-insensitive filesystems
869 let buf = match hg_path_to_path_buf(filename) {
887 let buf = match hg_path_to_path_buf(filename) {
870 Ok(x) => x,
888 Ok(x) => x,
871 Err(e) => return Some(Err(e.into())),
889 Err(_) => {
890 return Some((
891 Cow::Owned(filename.to_owned()),
892 INVALID_PATH_DISPATCH,
893 ));
894 }
872 };
895 };
873 Some(Ok((
896 Some((
874 Cow::Borrowed(filename),
897 Cow::Owned(filename.to_owned()),
875 match self.root_dir.join(&buf).symlink_metadata() {
898 match self.root_dir.join(&buf).symlink_metadata() {
876 // File was just ignored, no links, and exists
899 // File was just ignored, no links, and exists
877 Ok(meta) => {
900 Ok(meta) => {
@@ -887,21 +910,19 b' where'
887 // File doesn't exist
910 // File doesn't exist
888 Err(_) => dispatch_missing(entry.state),
911 Err(_) => dispatch_missing(entry.state),
889 },
912 },
890 )))
913 ))
891 } else {
914 } else {
892 // It's either missing or under a symlink directory which
915 // It's either missing or under a symlink directory which
893 // we, in this case, report as missing.
916 // we, in this case, report as missing.
894 Some(Ok((
917 Some((
895 Cow::Borrowed(filename),
918 Cow::Owned(filename.to_owned()),
896 dispatch_missing(entry.state),
919 dispatch_missing(entry.state),
897 )))
920 ))
898 }
921 }
899 })
922 },
900 .collect();
923 );
901
924
902 results.par_extend(new_results?);
925 results.par_extend(new_results);
903
904 Ok(())
905 }
926 }
906 }
927 }
907
928
@@ -56,7 +56,7 b" impl<'a, M: Matcher + Sync> Status<'a, M"
56 .expect("old results should exist"),
56 .expect("old results should exist"),
57 &mut results,
57 &mut results,
58 traversed_sender.clone(),
58 traversed_sender.clone(),
59 )?;
59 );
60 }
60 }
61 }
61 }
62 _ => {
62 _ => {
@@ -104,7 +104,7 b" impl<'a, M: Matcher + Sync> Status<'a, M"
104 &old_results,
104 &old_results,
105 &mut results,
105 &mut results,
106 traversed_sender.clone(),
106 traversed_sender.clone(),
107 )?;
107 );
108 }
108 }
109 }
109 }
110 _ => {
110 _ => {
@@ -116,7 +116,7 b" impl<'a, M: Matcher + Sync> Status<'a, M"
116
116
117 if !self.matcher.is_exact() {
117 if !self.matcher.is_exact() {
118 if self.options.list_unknown {
118 if self.options.list_unknown {
119 self.handle_unknowns(&mut results)?;
119 self.handle_unknowns(&mut results);
120 } else {
120 } else {
121 // TODO this is incorrect, see issue6335
121 // TODO this is incorrect, see issue6335
122 // This requires a fix in both Python and Rust that can happen
122 // This requires a fix in both Python and Rust that can happen
General Comments 0
You need to be logged in to leave comments. Login now