Details
Details
- Reviewers
- None
- Group Reviewers
hg-reviewers - Commits
- rHG5f6a504dc0bd: rust-status: refactor handling of unknown files
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Branch
- default
- Lint
No Linters Available - Unit
No Unit Test Coverage
| hg-reviewers |
| No Linters Available |
| No Unit Test Coverage |
| Path | Packages | |||
|---|---|---|---|---|
| M | rust/hg-core/src/dirstate/status.rs (138 lines) |
| Commit | Parents | Author | Summary | Date |
|---|---|---|---|---|
| afce9a4a6d56 | 7bea3ca9a4ff | Raphaël Gomès | Mar 6 2020, 11:48 AM |
| Status | Author | Revision | |
|---|---|---|---|
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | D8252 rust: add logging utils | |
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare |
| match self { | match self { | ||||
| StatusError::IO(e) => e.to_string(), | StatusError::IO(e) => e.to_string(), | ||||
| StatusError::Path(e) => e.to_string(), | StatusError::Path(e) => e.to_string(), | ||||
| StatusError::Pattern(e) => e.to_string(), | StatusError::Pattern(e) => e.to_string(), | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /// This takes a mutable reference to the results to account for the `extend` | |||||
| /// in timings | |||||
| fn handle_unknowns<'a>( | |||||
| dmap: &'a DirstateMap, | |||||
| matcher: &(impl Matcher + Sync), | |||||
| root_dir: impl AsRef<Path> + Sync + Send + Copy, | |||||
| options: StatusOptions, | |||||
| results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>, | |||||
| ) -> IoResult<()> { | |||||
| let to_visit: Vec<(&HgPath, &DirstateEntry)> = if results.is_empty() | |||||
| && matcher.matches_everything() | |||||
| { | |||||
| dmap.iter().map(|(f, e)| (f.deref(), e)).collect() | |||||
| } else { | |||||
| // Only convert to a hashmap if needed. | |||||
| let old_results: FastHashMap<_, _> = results.iter().cloned().collect(); | |||||
| dmap.iter() | |||||
| .filter_map(move |(f, e)| { | |||||
| if !old_results.contains_key(f.deref()) && matcher.matches(f) { | |||||
| Some((f.deref(), e)) | |||||
| } else { | |||||
| None | |||||
| } | |||||
| }) | |||||
| .collect() | |||||
| }; | |||||
| // We walked all dirs under the roots that weren't ignored, and | |||||
| // everything that matched was stat'ed and is already in results. | |||||
| // The rest must thus be ignored or under a symlink. | |||||
| let path_auditor = PathAuditor::new(root_dir); | |||||
| // TODO don't collect. Find a way of replicating the behavior of | |||||
| // `itertools::process_results`, but for `rayon::ParallelIterator` | |||||
| let new_results: IoResult<Vec<_>> = to_visit | |||||
| .into_par_iter() | |||||
| .filter_map(|(filename, entry)| -> Option<IoResult<_>> { | |||||
| // Report ignored items in the dmap as long as they are not | |||||
| // under a symlink directory. | |||||
| if path_auditor.check(filename) { | |||||
| // TODO normalize for case-insensitive filesystems | |||||
| let buf = match hg_path_to_path_buf(filename) { | |||||
| Ok(x) => x, | |||||
| Err(e) => return Some(Err(e.into())), | |||||
| }; | |||||
| Some(Ok(( | |||||
| Cow::Borrowed(filename), | |||||
| match root_dir.as_ref().join(&buf).symlink_metadata() { | |||||
| // File was just ignored, no links, and exists | |||||
| Ok(meta) => { | |||||
| let metadata = HgMetadata::from_metadata(meta); | |||||
| dispatch_found( | |||||
| filename, | |||||
| *entry, | |||||
| metadata, | |||||
| &dmap.copy_map, | |||||
| options, | |||||
| ) | |||||
| } | |||||
| // File doesn't exist | |||||
| Err(_) => dispatch_missing(entry.state), | |||||
| }, | |||||
| ))) | |||||
| } else { | |||||
| // It's either missing or under a symlink directory which | |||||
| // we, in this case, report as missing. | |||||
| Some(Ok(( | |||||
| Cow::Borrowed(filename), | |||||
| dispatch_missing(entry.state), | |||||
| ))) | |||||
| } | |||||
| }) | |||||
| .collect(); | |||||
| results.par_extend(new_results?); | |||||
| Ok(()) | |||||
| } | |||||
| /// Get the status of files in the working directory. | /// Get the status of files in the working directory. | ||||
| /// | /// | ||||
| /// This is the current entry-point for `hg-core` and is realistically unusable | /// This is the current entry-point for `hg-core` and is realistically unusable | ||||
| /// outside of a Python context because its arguments need to provide a lot of | /// outside of a Python context because its arguments need to provide a lot of | ||||
| /// information that will not be necessary in the future. | /// information that will not be necessary in the future. | ||||
| pub fn status<'a: 'c, 'b: 'c, 'c>( | pub fn status<'a: 'c, 'b: 'c, 'c>( | ||||
| dmap: &'a DirstateMap, | dmap: &'a DirstateMap, | ||||
| matcher: &'b (impl Matcher + Sync), | matcher: &'b (impl Matcher + Sync), | ||||
| if !matcher.is_exact() { | if !matcher.is_exact() { | ||||
| // Step 3: Check the remaining files from the dmap. | // Step 3: Check the remaining files from the dmap. | ||||
| // If a dmap file is not in results yet, it was either | // If a dmap file is not in results yet, it was either | ||||
| // a) not matched b) ignored, c) missing, or d) under a | // a) not matched b) ignored, c) missing, or d) under a | ||||
| // symlink directory. | // symlink directory. | ||||
| if options.list_unknown { | if options.list_unknown { | ||||
| let to_visit: Box<dyn Iterator<Item = (&HgPath, &DirstateEntry)>> = | handle_unknowns(dmap, matcher, root_dir, options, &mut results)?; | ||||
| if results.is_empty() && matcher.matches_everything() { | |||||
| Box::new(dmap.iter().map(|(f, e)| (f.deref(), e))) | |||||
| } else { | |||||
| // Only convert to a hashmap if needed. | |||||
| let old_results: FastHashMap<_, _> = | |||||
| results.iter().cloned().collect(); | |||||
| Box::new(dmap.iter().filter_map(move |(f, e)| { | |||||
| if !old_results.contains_key(f.deref()) | |||||
| && matcher.matches(f) | |||||
| { | |||||
| Some((f.deref(), e)) | |||||
| } else { | |||||
| None | |||||
| } | |||||
| })) | |||||
| }; | |||||
| let mut to_visit: Vec<_> = to_visit.collect(); | |||||
| to_visit.sort_by(|a, b| a.0.cmp(&b.0)); | |||||
| // We walked all dirs under the roots that weren't ignored, and | |||||
| // everything that matched was stat'ed and is already in results. | |||||
| // The rest must thus be ignored or under a symlink. | |||||
| let path_auditor = PathAuditor::new(root_dir); | |||||
| for (ref filename, entry) in to_visit { | |||||
| // Report ignored items in the dmap as long as they are not | |||||
| // under a symlink directory. | |||||
| if path_auditor.check(filename) { | |||||
| // TODO normalize for case-insensitive filesystems | |||||
| let buf = hg_path_to_path_buf(filename)?; | |||||
| results.push(( | |||||
| Cow::Borrowed(filename), | |||||
| match root_dir.as_ref().join(&buf).symlink_metadata() { | |||||
| // File was just ignored, no links, and exists | |||||
| Ok(meta) => { | |||||
| let metadata = HgMetadata::from_metadata(meta); | |||||
| dispatch_found( | |||||
| filename, | |||||
| *entry, | |||||
| metadata, | |||||
| &dmap.copy_map, | |||||
| options, | |||||
| ) | |||||
| } | |||||
| // File doesn't exist | |||||
| Err(_) => dispatch_missing(entry.state), | |||||
| }, | |||||
| )); | |||||
| } else { | |||||
| // It's either missing or under a symlink directory which | |||||
| // we, in this case, report as missing. | |||||
| results.push(( | |||||
| Cow::Borrowed(filename), | |||||
| dispatch_missing(entry.state), | |||||
| )); | |||||
| } | |||||
| } | |||||
| } else { | } else { | ||||
| // We may not have walked the full directory tree above, so stat | // We may not have walked the full directory tree above, so stat | ||||
| // and check everything we missed. | // and check everything we missed. | ||||
| let stat_results = stat_dmap_entries(&dmap, root_dir, options); | let stat_results = stat_dmap_entries(&dmap, root_dir, options); | ||||
| results.par_extend(stat_results.flatten().map( | results.par_extend(stat_results.flatten().map( | ||||
| |(filename, dispatch)| (Cow::Borrowed(filename), dispatch), | |(filename, dispatch)| (Cow::Borrowed(filename), dispatch), | ||||
| )); | )); | ||||
| } | } | ||||
| } | } | ||||
| Ok((build_response(results), warnings)) | Ok((build_response(results), warnings)) | ||||
| } | } | ||||