Instead of having status() returning a tuple of those paths and
DirstateStatus.
Details
Details
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Branch
- default
- Lint
No Linters Available - Unit
No Unit Test Coverage
Instead of having status() returning a tuple of those paths and
DirstateStatus.
| No Linters Available |
| No Unit Test Coverage |
| Path | Packages | |||
|---|---|---|---|---|
| M | rust/hg-core/src/dirstate/status.rs (41 lines) | |||
| M | rust/hg-core/src/dirstate_tree/dirstate_map.rs (10 lines) | |||
| M | rust/hg-core/src/dirstate_tree/dispatch.rs (18 lines) | |||
| M | rust/hg-core/src/operations/dirstate_status.rs (9 lines) | |||
| M | rust/hg-cpython/src/dirstate/status.rs (19 lines) | |||
| M | rust/rhg/src/commands/status.rs (10 lines) |
| Commit | Parents | Author | Summary | Date |
|---|---|---|---|---|
| 209827d7b740 | cb12ad69b33d | Simon Sapin | Apr 6 2021, 9:14 AM |
| Status | Author | Revision | |
|---|---|---|---|
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Abandoned | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin |
| pub modified: Vec<HgPathCow<'a>>, | pub modified: Vec<HgPathCow<'a>>, | ||||
| pub added: Vec<HgPathCow<'a>>, | pub added: Vec<HgPathCow<'a>>, | ||||
| pub removed: Vec<HgPathCow<'a>>, | pub removed: Vec<HgPathCow<'a>>, | ||||
| pub deleted: Vec<HgPathCow<'a>>, | pub deleted: Vec<HgPathCow<'a>>, | ||||
| pub clean: Vec<HgPathCow<'a>>, | pub clean: Vec<HgPathCow<'a>>, | ||||
| pub ignored: Vec<HgPathCow<'a>>, | pub ignored: Vec<HgPathCow<'a>>, | ||||
| pub unknown: Vec<HgPathCow<'a>>, | pub unknown: Vec<HgPathCow<'a>>, | ||||
| pub bad: Vec<(HgPathCow<'a>, BadMatch)>, | pub bad: Vec<(HgPathCow<'a>, BadMatch)>, | ||||
| /// Either clean or modified, but we can’t tell from filesystem metadata | |||||
| /// alone. The file contents need to be read and compared with that in | |||||
| /// the parent. | |||||
| pub unsure: Vec<HgPathCow<'a>>, | |||||
| /// Only filled if `collect_traversed_dirs` is `true` | /// Only filled if `collect_traversed_dirs` is `true` | ||||
| pub traversed: Vec<HgPathBuf>, | pub traversed: Vec<HgPathBuf>, | ||||
| } | } | ||||
| #[derive(Debug, derive_more::From)] | #[derive(Debug, derive_more::From)] | ||||
| pub enum StatusError { | pub enum StatusError { | ||||
| /// Generic IO error | /// Generic IO error | ||||
| IO(std::io::Error), | IO(std::io::Error), | ||||
| results.par_extend(new_results); | results.par_extend(new_results); | ||||
| } | } | ||||
| } | } | ||||
| #[timed] | #[timed] | ||||
| pub fn build_response<'a>( | pub fn build_response<'a>( | ||||
| results: impl IntoIterator<Item = DispatchedPath<'a>>, | results: impl IntoIterator<Item = DispatchedPath<'a>>, | ||||
| traversed: Vec<HgPathBuf>, | traversed: Vec<HgPathBuf>, | ||||
| ) -> (Vec<HgPathCow<'a>>, DirstateStatus<'a>) { | ) -> DirstateStatus<'a> { | ||||
| let mut lookup = vec![]; | let mut unsure = vec![]; | ||||
| let mut modified = vec![]; | let mut modified = vec![]; | ||||
| let mut added = vec![]; | let mut added = vec![]; | ||||
| let mut removed = vec![]; | let mut removed = vec![]; | ||||
| let mut deleted = vec![]; | let mut deleted = vec![]; | ||||
| let mut clean = vec![]; | let mut clean = vec![]; | ||||
| let mut ignored = vec![]; | let mut ignored = vec![]; | ||||
| let mut unknown = vec![]; | let mut unknown = vec![]; | ||||
| let mut bad = vec![]; | let mut bad = vec![]; | ||||
| for (filename, dispatch) in results.into_iter() { | for (filename, dispatch) in results.into_iter() { | ||||
| match dispatch { | match dispatch { | ||||
| Dispatch::Unknown => unknown.push(filename), | Dispatch::Unknown => unknown.push(filename), | ||||
| Dispatch::Unsure => lookup.push(filename), | Dispatch::Unsure => unsure.push(filename), | ||||
| Dispatch::Modified => modified.push(filename), | Dispatch::Modified => modified.push(filename), | ||||
| Dispatch::Added => added.push(filename), | Dispatch::Added => added.push(filename), | ||||
| Dispatch::Removed => removed.push(filename), | Dispatch::Removed => removed.push(filename), | ||||
| Dispatch::Deleted => deleted.push(filename), | Dispatch::Deleted => deleted.push(filename), | ||||
| Dispatch::Clean => clean.push(filename), | Dispatch::Clean => clean.push(filename), | ||||
| Dispatch::Ignored => ignored.push(filename), | Dispatch::Ignored => ignored.push(filename), | ||||
| Dispatch::None => {} | Dispatch::None => {} | ||||
| Dispatch::Bad(reason) => bad.push((filename, reason)), | Dispatch::Bad(reason) => bad.push((filename, reason)), | ||||
| Dispatch::Directory { .. } => {} | Dispatch::Directory { .. } => {} | ||||
| } | } | ||||
| } | } | ||||
| ( | |||||
| lookup, | |||||
| DirstateStatus { | DirstateStatus { | ||||
| modified, | modified, | ||||
| added, | added, | ||||
| removed, | removed, | ||||
| deleted, | deleted, | ||||
| clean, | clean, | ||||
| ignored, | ignored, | ||||
| unknown, | unknown, | ||||
| bad, | bad, | ||||
| unsure, | |||||
| traversed, | traversed, | ||||
| }, | } | ||||
| ) | |||||
| } | } | ||||
| /// 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. | ||||
| #[timed] | #[timed] | ||||
| pub fn status<'a>( | pub fn status<'a>( | ||||
| dmap: &'a DirstateMap, | dmap: &'a DirstateMap, | ||||
| matcher: &'a (dyn Matcher + Sync), | matcher: &'a (dyn Matcher + Sync), | ||||
| root_dir: PathBuf, | root_dir: PathBuf, | ||||
| ignore_files: Vec<PathBuf>, | ignore_files: Vec<PathBuf>, | ||||
| options: StatusOptions, | options: StatusOptions, | ||||
| ) -> StatusResult<( | ) -> StatusResult<(DirstateStatus<'a>, Vec<PatternFileWarning>)> { | ||||
| (Vec<HgPathCow<'a>>, DirstateStatus<'a>), | |||||
| Vec<PatternFileWarning>, | |||||
| )> { | |||||
| let (status, warnings) = | let (status, warnings) = | ||||
| Status::new(dmap, matcher, root_dir, ignore_files, options)?; | Status::new(dmap, matcher, root_dir, ignore_files, options)?; | ||||
| Ok((status.run()?, warnings)) | Ok((status.run()?, warnings)) | ||||
| } | } | ||||
| use crate::utils::hg_path::{HgPath, HgPathBuf}; | use crate::utils::hg_path::{HgPath, HgPathBuf}; | ||||
| use crate::CopyMapIter; | use crate::CopyMapIter; | ||||
| use crate::DirstateEntry; | use crate::DirstateEntry; | ||||
| use crate::DirstateError; | use crate::DirstateError; | ||||
| use crate::DirstateMapError; | use crate::DirstateMapError; | ||||
| use crate::DirstateParents; | use crate::DirstateParents; | ||||
| use crate::DirstateStatus; | use crate::DirstateStatus; | ||||
| use crate::EntryState; | use crate::EntryState; | ||||
| use crate::HgPathCow; | |||||
| use crate::PatternFileWarning; | use crate::PatternFileWarning; | ||||
| use crate::StateMapIter; | use crate::StateMapIter; | ||||
| use crate::StatusError; | use crate::StatusError; | ||||
| use crate::StatusOptions; | use crate::StatusOptions; | ||||
| pub struct DirstateMap { | pub struct DirstateMap { | ||||
| parents: Option<DirstateParents>, | parents: Option<DirstateParents>, | ||||
| dirty_parents: bool, | dirty_parents: bool, | ||||
| } | } | ||||
| fn status<'a>( | fn status<'a>( | ||||
| &'a self, | &'a self, | ||||
| _matcher: &'a (dyn Matcher + Sync), | _matcher: &'a (dyn Matcher + Sync), | ||||
| _root_dir: PathBuf, | _root_dir: PathBuf, | ||||
| _ignore_files: Vec<PathBuf>, | _ignore_files: Vec<PathBuf>, | ||||
| _options: StatusOptions, | _options: StatusOptions, | ||||
| ) -> Result< | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> | ||||
| ( | { | ||||
| (Vec<HgPathCow<'a>>, DirstateStatus<'a>), | |||||
| Vec<PatternFileWarning>, | |||||
| ), | |||||
| StatusError, | |||||
| > { | |||||
| todo!() | todo!() | ||||
| } | } | ||||
| fn copy_map_len(&self) -> usize { | fn copy_map_len(&self) -> usize { | ||||
| self.nodes_with_copy_source_count | self.nodes_with_copy_source_count | ||||
| } | } | ||||
| fn copy_map_iter(&self) -> CopyMapIter<'_> { | fn copy_map_iter(&self) -> CopyMapIter<'_> { | ||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||
| use crate::dirstate::parsers::Timestamp; | use crate::dirstate::parsers::Timestamp; | ||||
| use crate::matchers::Matcher; | use crate::matchers::Matcher; | ||||
| use crate::utils::hg_path::{HgPath, HgPathBuf}; | use crate::utils::hg_path::{HgPath, HgPathBuf}; | ||||
| use crate::CopyMapIter; | use crate::CopyMapIter; | ||||
| use crate::DirstateEntry; | use crate::DirstateEntry; | ||||
| use crate::DirstateError; | use crate::DirstateError; | ||||
| use crate::DirstateMap; | use crate::DirstateMap; | ||||
| use crate::DirstateMapError; | use crate::DirstateMapError; | ||||
| use crate::DirstateParents; | use crate::DirstateParents; | ||||
| use crate::DirstateStatus; | use crate::DirstateStatus; | ||||
| use crate::EntryState; | use crate::EntryState; | ||||
| use crate::HgPathCow; | |||||
| use crate::PatternFileWarning; | use crate::PatternFileWarning; | ||||
| use crate::StateMapIter; | use crate::StateMapIter; | ||||
| use crate::StatusError; | use crate::StatusError; | ||||
| use crate::StatusOptions; | use crate::StatusOptions; | ||||
| pub trait DirstateMapMethods { | pub trait DirstateMapMethods { | ||||
| fn clear(&mut self); | fn clear(&mut self); | ||||
| fn set_dirs(&mut self) -> Result<(), DirstateMapError>; | fn set_dirs(&mut self) -> Result<(), DirstateMapError>; | ||||
| fn status<'a>( | fn status<'a>( | ||||
| &'a self, | &'a self, | ||||
| matcher: &'a (dyn Matcher + Sync), | matcher: &'a (dyn Matcher + Sync), | ||||
| root_dir: PathBuf, | root_dir: PathBuf, | ||||
| ignore_files: Vec<PathBuf>, | ignore_files: Vec<PathBuf>, | ||||
| options: StatusOptions, | options: StatusOptions, | ||||
| ) -> Result< | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>; | ||||
| ( | |||||
| (Vec<HgPathCow<'a>>, DirstateStatus<'a>), | |||||
| Vec<PatternFileWarning>, | |||||
| ), | |||||
| StatusError, | |||||
| >; | |||||
| fn copy_map_len(&self) -> usize; | fn copy_map_len(&self) -> usize; | ||||
| fn copy_map_iter(&self) -> CopyMapIter<'_>; | fn copy_map_iter(&self) -> CopyMapIter<'_>; | ||||
| fn copy_map_contains_key(&self, key: &HgPath) -> bool; | fn copy_map_contains_key(&self, key: &HgPath) -> bool; | ||||
| fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf>; | fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf>; | ||||
| } | } | ||||
| fn status<'a>( | fn status<'a>( | ||||
| &'a self, | &'a self, | ||||
| matcher: &'a (dyn Matcher + Sync), | matcher: &'a (dyn Matcher + Sync), | ||||
| root_dir: PathBuf, | root_dir: PathBuf, | ||||
| ignore_files: Vec<PathBuf>, | ignore_files: Vec<PathBuf>, | ||||
| options: StatusOptions, | options: StatusOptions, | ||||
| ) -> Result< | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> | ||||
| ( | { | ||||
| (Vec<HgPathCow<'a>>, DirstateStatus<'a>), | |||||
| Vec<PatternFileWarning>, | |||||
| ), | |||||
| StatusError, | |||||
| > { | |||||
| crate::status(self, matcher, root_dir, ignore_files, options) | crate::status(self, matcher, root_dir, ignore_files, options) | ||||
| } | } | ||||
| fn copy_map_len(&self) -> usize { | fn copy_map_len(&self) -> usize { | ||||
| self.copy_map.len() | self.copy_map.len() | ||||
| } | } | ||||
| fn copy_map_iter(&self) -> CopyMapIter<'_> { | fn copy_map_iter(&self) -> CopyMapIter<'_> { | ||||
| // dirstate_status.rs | // dirstate_status.rs | ||||
| // | // | ||||
| // Copyright 2019, Raphaël Gomès <rgomes@octobus.net> | // Copyright 2019, Raphaël Gomès <rgomes@octobus.net> | ||||
| // | // | ||||
| // This software may be used and distributed according to the terms of the | // This software may be used and distributed according to the terms of the | ||||
| // GNU General Public License version 2 or any later version. | // GNU General Public License version 2 or any later version. | ||||
| use crate::dirstate::status::{build_response, Dispatch, HgPathCow, Status}; | use crate::dirstate::status::{build_response, Dispatch, Status}; | ||||
| use crate::matchers::Matcher; | use crate::matchers::Matcher; | ||||
| use crate::{DirstateStatus, StatusError}; | use crate::{DirstateStatus, StatusError}; | ||||
| /// A tuple of the paths that need to be checked in the filelog because it's | |||||
| /// ambiguous whether they've changed, and the rest of the already dispatched | |||||
| /// files. | |||||
| pub type LookupAndStatus<'a> = (Vec<HgPathCow<'a>>, DirstateStatus<'a>); | |||||
| impl<'a, M: ?Sized + Matcher + Sync> Status<'a, M> { | impl<'a, M: ?Sized + Matcher + Sync> Status<'a, M> { | ||||
| pub(crate) fn run(&self) -> Result<LookupAndStatus<'a>, StatusError> { | pub(crate) fn run(&self) -> Result<DirstateStatus<'a>, StatusError> { | ||||
| let (traversed_sender, traversed_receiver) = | let (traversed_sender, traversed_receiver) = | ||||
| crossbeam_channel::unbounded(); | crossbeam_channel::unbounded(); | ||||
| // Step 1: check the files explicitly mentioned by the user | // Step 1: check the files explicitly mentioned by the user | ||||
| let (work, mut results) = self.walk_explicit(traversed_sender.clone()); | let (work, mut results) = self.walk_explicit(traversed_sender.clone()); | ||||
| if !work.is_empty() { | if !work.is_empty() { | ||||
| // Hashmaps are quite a bit slower to build than vecs, so only | // Hashmaps are quite a bit slower to build than vecs, so only | ||||
| parse_pattern_syntax, | parse_pattern_syntax, | ||||
| utils::{ | utils::{ | ||||
| files::{get_bytes_from_path, get_path_from_bytes}, | files::{get_bytes_from_path, get_path_from_bytes}, | ||||
| hg_path::{HgPath, HgPathBuf}, | hg_path::{HgPath, HgPathBuf}, | ||||
| }, | }, | ||||
| BadMatch, DirstateStatus, IgnorePattern, PatternFileWarning, StatusError, | BadMatch, DirstateStatus, IgnorePattern, PatternFileWarning, StatusError, | ||||
| StatusOptions, | StatusOptions, | ||||
| }; | }; | ||||
| use std::borrow::{Borrow, Cow}; | use std::borrow::Borrow; | ||||
| /// This will be useless once trait impls for collection are added to `PyBytes` | /// This will be useless once trait impls for collection are added to `PyBytes` | ||||
| /// upstream. | /// upstream. | ||||
| fn collect_pybytes_list( | fn collect_pybytes_list( | ||||
| py: Python, | py: Python, | ||||
| collection: &[impl AsRef<HgPath>], | collection: &[impl AsRef<HgPath>], | ||||
| ) -> PyList { | ) -> PyList { | ||||
| let list = PyList::new(py, &[]); | let list = PyList::new(py, &[]); | ||||
| Ok(get_path_from_bytes(file.data(py)).to_owned()) | Ok(get_path_from_bytes(file.data(py)).to_owned()) | ||||
| }) | }) | ||||
| .collect(); | .collect(); | ||||
| let ignore_files = ignore_files?; | let ignore_files = ignore_files?; | ||||
| match matcher.get_type(py).name(py).borrow() { | match matcher.get_type(py).name(py).borrow() { | ||||
| "alwaysmatcher" => { | "alwaysmatcher" => { | ||||
| let matcher = AlwaysMatcher; | let matcher = AlwaysMatcher; | ||||
| let ((lookup, status_res), warnings) = dmap | let (status_res, warnings) = dmap | ||||
| .status( | .status( | ||||
| &matcher, | &matcher, | ||||
| root_dir.to_path_buf(), | root_dir.to_path_buf(), | ||||
| ignore_files, | ignore_files, | ||||
| StatusOptions { | StatusOptions { | ||||
| check_exec, | check_exec, | ||||
| last_normal_time, | last_normal_time, | ||||
| list_clean, | list_clean, | ||||
| list_ignored, | list_ignored, | ||||
| list_unknown, | list_unknown, | ||||
| collect_traversed_dirs, | collect_traversed_dirs, | ||||
| }, | }, | ||||
| ) | ) | ||||
| .map_err(|e| handle_fallback(py, e))?; | .map_err(|e| handle_fallback(py, e))?; | ||||
| build_response(py, lookup, status_res, warnings) | build_response(py, status_res, warnings) | ||||
| } | } | ||||
| "exactmatcher" => { | "exactmatcher" => { | ||||
| let files = matcher.call_method( | let files = matcher.call_method( | ||||
| py, | py, | ||||
| "files", | "files", | ||||
| PyTuple::new(py, &[]), | PyTuple::new(py, &[]), | ||||
| None, | None, | ||||
| )?; | )?; | ||||
| let files: PyList = files.cast_into(py)?; | let files: PyList = files.cast_into(py)?; | ||||
| let files: PyResult<Vec<HgPathBuf>> = files | let files: PyResult<Vec<HgPathBuf>> = files | ||||
| .iter(py) | .iter(py) | ||||
| .map(|f| { | .map(|f| { | ||||
| Ok(HgPathBuf::from_bytes( | Ok(HgPathBuf::from_bytes( | ||||
| f.extract::<PyBytes>(py)?.data(py), | f.extract::<PyBytes>(py)?.data(py), | ||||
| )) | )) | ||||
| }) | }) | ||||
| .collect(); | .collect(); | ||||
| let files = files?; | let files = files?; | ||||
| let matcher = FileMatcher::new(files.as_ref()) | let matcher = FileMatcher::new(files.as_ref()) | ||||
| .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?; | .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?; | ||||
| let ((lookup, status_res), warnings) = dmap | let (status_res, warnings) = dmap | ||||
| .status( | .status( | ||||
| &matcher, | &matcher, | ||||
| root_dir.to_path_buf(), | root_dir.to_path_buf(), | ||||
| ignore_files, | ignore_files, | ||||
| StatusOptions { | StatusOptions { | ||||
| check_exec, | check_exec, | ||||
| last_normal_time, | last_normal_time, | ||||
| list_clean, | list_clean, | ||||
| list_ignored, | list_ignored, | ||||
| list_unknown, | list_unknown, | ||||
| collect_traversed_dirs, | collect_traversed_dirs, | ||||
| }, | }, | ||||
| ) | ) | ||||
| .map_err(|e| handle_fallback(py, e))?; | .map_err(|e| handle_fallback(py, e))?; | ||||
| build_response(py, lookup, status_res, warnings) | build_response(py, status_res, warnings) | ||||
| } | } | ||||
| "includematcher" => { | "includematcher" => { | ||||
| // Get the patterns from Python even though most of them are | // Get the patterns from Python even though most of them are | ||||
| // redundant with those we will parse later on, as they include | // redundant with those we will parse later on, as they include | ||||
| // those passed from the command line. | // those passed from the command line. | ||||
| let ignore_patterns: PyResult<Vec<_>> = matcher | let ignore_patterns: PyResult<Vec<_>> = matcher | ||||
| .getattr(py, "_kindpats")? | .getattr(py, "_kindpats")? | ||||
| .iter(py)? | .iter(py)? | ||||
| let ignore_patterns = ignore_patterns?; | let ignore_patterns = ignore_patterns?; | ||||
| let mut all_warnings = vec![]; | let mut all_warnings = vec![]; | ||||
| let (matcher, warnings) = | let (matcher, warnings) = | ||||
| IncludeMatcher::new(ignore_patterns, &root_dir) | IncludeMatcher::new(ignore_patterns, &root_dir) | ||||
| .map_err(|e| handle_fallback(py, e.into()))?; | .map_err(|e| handle_fallback(py, e.into()))?; | ||||
| all_warnings.extend(warnings); | all_warnings.extend(warnings); | ||||
| let ((lookup, status_res), warnings) = dmap | let (status_res, warnings) = dmap | ||||
| .status( | .status( | ||||
| &matcher, | &matcher, | ||||
| root_dir.to_path_buf(), | root_dir.to_path_buf(), | ||||
| ignore_files, | ignore_files, | ||||
| StatusOptions { | StatusOptions { | ||||
| check_exec, | check_exec, | ||||
| last_normal_time, | last_normal_time, | ||||
| list_clean, | list_clean, | ||||
| list_ignored, | list_ignored, | ||||
| list_unknown, | list_unknown, | ||||
| collect_traversed_dirs, | collect_traversed_dirs, | ||||
| }, | }, | ||||
| ) | ) | ||||
| .map_err(|e| handle_fallback(py, e))?; | .map_err(|e| handle_fallback(py, e))?; | ||||
| all_warnings.extend(warnings); | all_warnings.extend(warnings); | ||||
| build_response(py, lookup, status_res, all_warnings) | build_response(py, status_res, all_warnings) | ||||
| } | } | ||||
| e => Err(PyErr::new::<ValueError, _>( | e => Err(PyErr::new::<ValueError, _>( | ||||
| py, | py, | ||||
| format!("Unsupported matcher {}", e), | format!("Unsupported matcher {}", e), | ||||
| )), | )), | ||||
| } | } | ||||
| } | } | ||||
| fn build_response( | fn build_response( | ||||
| py: Python, | py: Python, | ||||
| lookup: Vec<Cow<HgPath>>, | |||||
| status_res: DirstateStatus, | status_res: DirstateStatus, | ||||
| warnings: Vec<PatternFileWarning>, | warnings: Vec<PatternFileWarning>, | ||||
| ) -> PyResult<PyTuple> { | ) -> PyResult<PyTuple> { | ||||
| let modified = collect_pybytes_list(py, status_res.modified.as_ref()); | let modified = collect_pybytes_list(py, status_res.modified.as_ref()); | ||||
| let added = collect_pybytes_list(py, status_res.added.as_ref()); | let added = collect_pybytes_list(py, status_res.added.as_ref()); | ||||
| let removed = collect_pybytes_list(py, status_res.removed.as_ref()); | let removed = collect_pybytes_list(py, status_res.removed.as_ref()); | ||||
| let deleted = collect_pybytes_list(py, status_res.deleted.as_ref()); | let deleted = collect_pybytes_list(py, status_res.deleted.as_ref()); | ||||
| let clean = collect_pybytes_list(py, status_res.clean.as_ref()); | let clean = collect_pybytes_list(py, status_res.clean.as_ref()); | ||||
| let ignored = collect_pybytes_list(py, status_res.ignored.as_ref()); | let ignored = collect_pybytes_list(py, status_res.ignored.as_ref()); | ||||
| let unknown = collect_pybytes_list(py, status_res.unknown.as_ref()); | let unknown = collect_pybytes_list(py, status_res.unknown.as_ref()); | ||||
| let lookup = collect_pybytes_list(py, lookup.as_ref()); | let unsure = collect_pybytes_list(py, status_res.unsure.as_ref()); | ||||
| let bad = collect_bad_matches(py, status_res.bad.as_ref())?; | let bad = collect_bad_matches(py, status_res.bad.as_ref())?; | ||||
| let traversed = collect_pybytes_list(py, status_res.traversed.as_ref()); | let traversed = collect_pybytes_list(py, status_res.traversed.as_ref()); | ||||
| let py_warnings = PyList::new(py, &[]); | let py_warnings = PyList::new(py, &[]); | ||||
| for warning in warnings.iter() { | for warning in warnings.iter() { | ||||
| // We use duck-typing on the Python side for dispatch, good enough for | // We use duck-typing on the Python side for dispatch, good enough for | ||||
| // now. | // now. | ||||
| match warning { | match warning { | ||||
| PatternFileWarning::InvalidSyntax(file, syn) => { | PatternFileWarning::InvalidSyntax(file, syn) => { | ||||
| PyBytes::new(py, &get_bytes_from_path(&file)).into_object(), | PyBytes::new(py, &get_bytes_from_path(&file)).into_object(), | ||||
| ), | ), | ||||
| } | } | ||||
| } | } | ||||
| Ok(PyTuple::new( | Ok(PyTuple::new( | ||||
| py, | py, | ||||
| &[ | &[ | ||||
| lookup.into_object(), | unsure.into_object(), | ||||
| modified.into_object(), | modified.into_object(), | ||||
| added.into_object(), | added.into_object(), | ||||
| removed.into_object(), | removed.into_object(), | ||||
| deleted.into_object(), | deleted.into_object(), | ||||
| clean.into_object(), | clean.into_object(), | ||||
| ignored.into_object(), | ignored.into_object(), | ||||
| unknown.into_object(), | unknown.into_object(), | ||||
| py_warnings.into_object(), | py_warnings.into_object(), | ||||
| bad.into_object(), | bad.into_object(), | ||||
| traversed.into_object(), | traversed.into_object(), | ||||
| ][..], | ][..], | ||||
| )) | )) | ||||
| } | } | ||||
| // anyway | // anyway | ||||
| check_exec: true, | check_exec: true, | ||||
| list_clean: display_states.clean, | list_clean: display_states.clean, | ||||
| list_unknown: display_states.unknown, | list_unknown: display_states.unknown, | ||||
| list_ignored: display_states.ignored, | list_ignored: display_states.ignored, | ||||
| collect_traversed_dirs: false, | collect_traversed_dirs: false, | ||||
| }; | }; | ||||
| let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded | let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded | ||||
| let ((lookup, ds_status), pattern_warnings) = hg::status( | let (ds_status, pattern_warnings) = hg::status( | ||||
| &dmap, | &dmap, | ||||
| &AlwaysMatcher, | &AlwaysMatcher, | ||||
| repo.working_directory_path().to_owned(), | repo.working_directory_path().to_owned(), | ||||
| vec![ignore_file], | vec![ignore_file], | ||||
| options, | options, | ||||
| )?; | )?; | ||||
| if !pattern_warnings.is_empty() { | if !pattern_warnings.is_empty() { | ||||
| warn!("Pattern warnings: {:?}", &pattern_warnings); | warn!("Pattern warnings: {:?}", &pattern_warnings); | ||||
| } | } | ||||
| if !ds_status.bad.is_empty() { | if !ds_status.bad.is_empty() { | ||||
| warn!("Bad matches {:?}", &(ds_status.bad)) | warn!("Bad matches {:?}", &(ds_status.bad)) | ||||
| } | } | ||||
| if !lookup.is_empty() { | if !ds_status.unsure.is_empty() { | ||||
| info!( | info!( | ||||
| "Files to be rechecked by retrieval from filelog: {:?}", | "Files to be rechecked by retrieval from filelog: {:?}", | ||||
| &lookup | &ds_status.unsure | ||||
| ); | ); | ||||
| } | } | ||||
| // TODO check ordering to match `hg status` output. | // TODO check ordering to match `hg status` output. | ||||
| // (this is as in `hg help status`) | // (this is as in `hg help status`) | ||||
| if display_states.modified { | if display_states.modified { | ||||
| display_status_paths(ui, &(ds_status.modified), b"M")?; | display_status_paths(ui, &(ds_status.modified), b"M")?; | ||||
| } | } | ||||
| if !lookup.is_empty() { | if !ds_status.unsure.is_empty() { | ||||
| let p1: Node = parents | let p1: Node = parents | ||||
| .expect( | .expect( | ||||
| "Dirstate with no parents should not list any file to | "Dirstate with no parents should not list any file to | ||||
| be rechecked for modifications", | be rechecked for modifications", | ||||
| ) | ) | ||||
| .p1 | .p1 | ||||
| .into(); | .into(); | ||||
| let p1_hex = format!("{:x}", p1); | let p1_hex = format!("{:x}", p1); | ||||
| let mut rechecked_modified: Vec<HgPathCow> = Vec::new(); | let mut rechecked_modified: Vec<HgPathCow> = Vec::new(); | ||||
| let mut rechecked_clean: Vec<HgPathCow> = Vec::new(); | let mut rechecked_clean: Vec<HgPathCow> = Vec::new(); | ||||
| for to_check in lookup { | for to_check in ds_status.unsure { | ||||
| if cat_file_is_modified(repo, &to_check, &p1_hex)? { | if cat_file_is_modified(repo, &to_check, &p1_hex)? { | ||||
| rechecked_modified.push(to_check); | rechecked_modified.push(to_check); | ||||
| } else { | } else { | ||||
| rechecked_clean.push(to_check); | rechecked_clean.push(to_check); | ||||
| } | } | ||||
| } | } | ||||
| if display_states.modified { | if display_states.modified { | ||||
| display_status_paths(ui, &rechecked_modified, b"M")?; | display_status_paths(ui, &rechecked_modified, b"M")?; | ||||