rust/hg-core/src/dirstate/status.rs
changeset 44897 c802ec4f7196
parent 44896 dc60ba32d43b
child 44901 73d6ce2746d2
equal deleted inserted replaced
44896:dc60ba32d43b 44897:c802ec4f7196
   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" {
   339             ignore_fn,
   351             ignore_fn,
   340             dir_ignore_fn,
   352             dir_ignore_fn,
   341             options,
   353             options,
   342             entry_option,
   354             entry_option,
   343             filename,
   355             filename,
       
   356             traversed_sender,
   344         );
   357         );
   345     } else if file_type.is_file() || file_type.is_symlink() {
   358     } else if file_type.is_file() || file_type.is_symlink() {
   346         if let Some(entry) = entry_option {
   359         if let Some(entry) = entry_option {
   347             if matcher.matches_everything() || matcher.matches(&filename) {
   360             if matcher.matches_everything() || matcher.matches(&filename) {
   348                 let metadata = dir_entry.metadata()?;
   361                 let metadata = dir_entry.metadata()?;
   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
   431                 directory,
   445                 directory,
   432                 &old_results,
   446                 &old_results,
   433                 ignore_fn,
   447                 ignore_fn,
   434                 dir_ignore_fn,
   448                 dir_ignore_fn,
   435                 options,
   449                 options,
       
   450                 traversed_sender,
   436             )
   451             )
   437             .unwrap_or_else(|e| files_sender.send(Err(e)).unwrap())
   452             .unwrap_or_else(|e| files_sender.send(Err(e)).unwrap())
   438         }
   453         }
   439     });
   454     });
   440 }
   455 }
   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),
   508                     ignore_fn,
   529                     ignore_fn,
   509                     dir_ignore_fn,
   530                     dir_ignore_fn,
   510                     options,
   531                     options,
   511                     filename,
   532                     filename,
   512                     dir_entry,
   533                     dir_entry,
       
   534                     traversed_sender.clone(),
   513                 )?;
   535                 )?;
   514             }
   536             }
   515         }
   537         }
   516         Ok(())
   538         Ok(())
   517     })
   539     })
   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.
   548         path,
   571         path,
   549         &old_results,
   572         &old_results,
   550         &ignore_fn,
   573         &ignore_fn,
   551         &dir_ignore_fn,
   574         &dir_ignore_fn,
   552         options,
   575         options,
       
   576         traversed_sender,
   553     )?;
   577     )?;
   554 
   578 
   555     // Disconnect the channel so the receiver stops waiting
   579     // Disconnect the channel so the receiver stops waiting
   556     drop(files_transmitter);
   580     drop(files_transmitter);
   557 
   581 
   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![];
   681             deleted,
   708             deleted,
   682             clean,
   709             clean,
   683             ignored,
   710             ignored,
   684             unknown,
   711             unknown,
   685             bad,
   712             bad,
       
   713             traversed,
   686         },
   714         },
   687     )
   715     )
   688 }
   716 }
   689 
   717 
   690 #[derive(Debug)]
   718 #[derive(Debug)]
   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)
   886                             &old_results,
   923                             &old_results,
   887                             &ignore_fn,
   924                             &ignore_fn,
   888                             &dir_ignore_fn,
   925                             &dir_ignore_fn,
   889                             options,
   926                             options,
   890                             &mut results,
   927                             &mut results,
       
   928                             traversed_sender.clone(),
   891                         )?;
   929                         )?;
   892                     }
   930                     }
   893                 }
   931                 }
   894                 _ => unreachable!("There can only be directories in `work`"),
   932                 _ => unreachable!("There can only be directories in `work`"),
   895             }
   933             }
   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 }