This moves low-level dirstate wrangling out of the status command and into
a more reusable location.
The open dirstate map is lazily initialized and kept on the Repo object,
for reuse by sub-sequent calls.
( )
| Alphare | 
| hg-reviewers | 
This moves low-level dirstate wrangling out of the status command and into
a more reusable location.
The open dirstate map is lazily initialized and kept on the Repo object,
for reuse by sub-sequent calls.
| No Linters Available | 
| No Unit Test Coverage | 
| rust/hg-core/src/repo.rs | ||
|---|---|---|
| 270 | Indeed! Done. (Though it’s not critical, it just saves the dirstate_parents getter from opening on trying to open the .hg/dirstate file again.) | |
| Path | Packages | |||
|---|---|---|---|---|
| M | rust/hg-core/src/dirstate.rs (2 lines) | |||
| M | rust/hg-core/src/repo.rs (106 lines) | |||
| M | rust/rhg/src/commands/status.rs (50 lines) | 
| Commit | Parents | Author | Summary | Date | 
|---|---|---|---|---|
| 4e12204617a8 | 3b97eb9c1e70 | Simon Sapin | Sep 9 2021, 3:04 PM | 
| Status | Author | Revision | |
|---|---|---|---|
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | 
| use bytes_cast::{unaligned, BytesCast}; | use bytes_cast::{unaligned, BytesCast}; | ||||
| use std::convert::TryFrom; | use std::convert::TryFrom; | ||||
| pub mod dirs_multiset; | pub mod dirs_multiset; | ||||
| pub mod dirstate_map; | pub mod dirstate_map; | ||||
| pub mod parsers; | pub mod parsers; | ||||
| pub mod status; | pub mod status; | ||||
| #[derive(Debug, PartialEq, Clone, BytesCast)] | #[derive(Debug, PartialEq, Copy, Clone, BytesCast)] | ||||
| #[repr(C)] | #[repr(C)] | ||||
| pub struct DirstateParents { | pub struct DirstateParents { | ||||
| pub p1: Node, | pub p1: Node, | ||||
| pub p2: Node, | pub p2: Node, | ||||
| } | } | ||||
| impl DirstateParents { | impl DirstateParents { | ||||
| pub const NULL: Self = Self { | pub const NULL: Self = Self { | ||||
| // status.rs | // status.rs | ||||
| // | // | ||||
| // Copyright 2020, Georges Racinet <georges.racinets@octobus.net> | // Copyright 2020, Georges Racinet <georges.racinets@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::error::CommandError; | use crate::error::CommandError; | ||||
| use crate::ui::Ui; | use crate::ui::Ui; | ||||
| use clap::{Arg, SubCommand}; | use clap::{Arg, SubCommand}; | ||||
| use hg; | use hg; | ||||
| use hg::dirstate_tree::dirstate_map::DirstateMap; | use hg::dirstate_tree::dispatch::DirstateMapMethods; | ||||
| use hg::dirstate_tree::on_disk; | |||||
| use hg::errors::HgResultExt; | |||||
| use hg::errors::IoResultExt; | use hg::errors::IoResultExt; | ||||
| use hg::matchers::AlwaysMatcher; | use hg::matchers::AlwaysMatcher; | ||||
| use hg::operations::cat; | use hg::operations::cat; | ||||
| use hg::repo::Repo; | use hg::repo::Repo; | ||||
| use hg::revlog::node::Node; | use hg::revlog::node::Node; | ||||
| use hg::utils::hg_path::{hg_path_to_os_string, HgPath}; | use hg::utils::hg_path::{hg_path_to_os_string, HgPath}; | ||||
| use hg::StatusError; | use hg::StatusError; | ||||
| use hg::{HgPathCow, StatusOptions}; | use hg::{HgPathCow, StatusOptions}; | ||||
| if requested.is_empty() { | if requested.is_empty() { | ||||
| DEFAULT_DISPLAY_STATES | DEFAULT_DISPLAY_STATES | ||||
| } else { | } else { | ||||
| requested | requested | ||||
| } | } | ||||
| }; | }; | ||||
| let repo = invocation.repo?; | let repo = invocation.repo?; | ||||
| let dirstate_data_mmap; | let mut dmap = repo.dirstate_map_mut()?; | ||||
| let (mut dmap, parents) = if repo.has_dirstate_v2() { | |||||
| let docket_data = | |||||
| repo.hg_vfs().read("dirstate").io_not_found_as_none()?; | |||||
| let parents; | |||||
| let dirstate_data; | |||||
| let data_size; | |||||
| let docket; | |||||
| let tree_metadata; | |||||
| if let Some(docket_data) = &docket_data { | |||||
| docket = on_disk::read_docket(docket_data)?; | |||||
| tree_metadata = docket.tree_metadata(); | |||||
| parents = Some(docket.parents()); | |||||
| data_size = docket.data_size(); | |||||
| dirstate_data_mmap = repo | |||||
| .hg_vfs() | |||||
| .mmap_open(docket.data_filename()) | |||||
| .io_not_found_as_none()?; | |||||
| dirstate_data = dirstate_data_mmap.as_deref().unwrap_or(b""); | |||||
| } else { | |||||
| parents = None; | |||||
| tree_metadata = b""; | |||||
| data_size = 0; | |||||
| dirstate_data = b""; | |||||
| } | |||||
| let dmap = | |||||
| DirstateMap::new_v2(dirstate_data, data_size, tree_metadata)?; | |||||
| (dmap, parents) | |||||
| } else { | |||||
| dirstate_data_mmap = | |||||
| repo.hg_vfs().mmap_open("dirstate").io_not_found_as_none()?; | |||||
| let dirstate_data = dirstate_data_mmap.as_deref().unwrap_or(b""); | |||||
| DirstateMap::new_v1(dirstate_data)? | |||||
| }; | |||||
| let options = StatusOptions { | let options = StatusOptions { | ||||
| // TODO should be provided by the dirstate parsing and | // TODO should be provided by the dirstate parsing and | ||||
| // hence be stored on dmap. Using a value that assumes we aren't | // hence be stored on dmap. Using a value that assumes we aren't | ||||
| // below the time resolution granularity of the FS and the | // below the time resolution granularity of the FS and the | ||||
| // dirstate. | // dirstate. | ||||
| last_normal_time: 0, | last_normal_time: 0, | ||||
| // we're currently supporting file systems with exec flags only | // we're currently supporting file systems with exec flags only | ||||
| // 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 (mut ds_status, pattern_warnings) = hg::dirstate_tree::status::status( | let (mut ds_status, pattern_warnings) = dmap.status( | ||||
| &mut 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 !ds_status.unsure.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: {:?}", | ||||
| &ds_status.unsure | &ds_status.unsure | ||||
| ); | ); | ||||
| } | } | ||||
| if !ds_status.unsure.is_empty() | if !ds_status.unsure.is_empty() | ||||
| && (display_states.modified || display_states.clean) | && (display_states.modified || display_states.clean) | ||||
| { | { | ||||
| let p1: Node = parents | let p1: Node = repo.dirstate_parents()?.p1.into(); | ||||
| .expect( | |||||
| "Dirstate with no parents should not list any file to | |||||
| be rechecked for modifications", | |||||
| ) | |||||
| .p1 | |||||
| .into(); | |||||
| let p1_hex = format!("{:x}", p1); | let p1_hex = format!("{:x}", p1); | ||||
| for to_check in ds_status.unsure { | 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)? { | ||||
| if display_states.modified { | if display_states.modified { | ||||
| ds_status.modified.push(to_check); | ds_status.modified.push(to_check); | ||||
| } | } | ||||
| } else { | } else { | ||||
| if display_states.clean { | if display_states.clean { | ||||
Should we set the parents to DirstateParents::NULL here?