comparison rust/hg-core/src/repo.rs @ 47956:81aedf1fc897

rust: Add Repo::dirstate_map and use it in `rhg status` 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. Differential Revision: https://phab.mercurial-scm.org/D11398
author Simon Sapin <simon.sapin@octobus.net>
date Thu, 09 Sep 2021 21:04:55 +0200
parents 9cd35c8c6044
children fc208d6faed3
comparison
equal deleted inserted replaced
47955:e834b79def74 47956:81aedf1fc897
1 use crate::config::{Config, ConfigError, ConfigParseError}; 1 use crate::config::{Config, ConfigError, ConfigParseError};
2 use crate::dirstate::DirstateParents;
3 use crate::dirstate_tree::dirstate_map::DirstateMap;
4 use crate::dirstate_tree::owning::OwningDirstateMap;
2 use crate::errors::HgError; 5 use crate::errors::HgError;
6 use crate::errors::HgResultExt;
3 use crate::exit_codes; 7 use crate::exit_codes;
4 use crate::requirements; 8 use crate::requirements;
5 use crate::utils::files::get_path_from_bytes; 9 use crate::utils::files::get_path_from_bytes;
6 use crate::utils::SliceExt; 10 use crate::utils::SliceExt;
7 use crate::vfs::{is_dir, is_file, Vfs}; 11 use crate::vfs::{is_dir, is_file, Vfs};
12 use crate::DirstateError;
13 use std::cell::{Cell, Ref, RefCell, RefMut};
8 use std::collections::HashSet; 14 use std::collections::HashSet;
9 use std::path::{Path, PathBuf}; 15 use std::path::{Path, PathBuf};
10 16
11 /// A repository on disk 17 /// A repository on disk
12 pub struct Repo { 18 pub struct Repo {
13 working_directory: PathBuf, 19 working_directory: PathBuf,
14 dot_hg: PathBuf, 20 dot_hg: PathBuf,
15 store: PathBuf, 21 store: PathBuf,
16 requirements: HashSet<String>, 22 requirements: HashSet<String>,
17 config: Config, 23 config: Config,
24 // None means not known/initialized yet
25 dirstate_parents: Cell<Option<DirstateParents>>,
26 dirstate_map: RefCell<Option<OwningDirstateMap>>,
18 } 27 }
19 28
20 #[derive(Debug, derive_more::From)] 29 #[derive(Debug, derive_more::From)]
21 pub enum RepoError { 30 pub enum RepoError {
22 NotFound { 31 NotFound {
184 requirements: reqs, 193 requirements: reqs,
185 working_directory, 194 working_directory,
186 store: store_path, 195 store: store_path,
187 dot_hg, 196 dot_hg,
188 config: repo_config, 197 config: repo_config,
198 dirstate_parents: Cell::new(None),
199 dirstate_map: RefCell::new(None),
189 }; 200 };
190 201
191 requirements::check(&repo)?; 202 requirements::check(&repo)?;
192 203
193 Ok(repo) 204 Ok(repo)
226 pub fn has_dirstate_v2(&self) -> bool { 237 pub fn has_dirstate_v2(&self) -> bool {
227 self.requirements 238 self.requirements
228 .contains(requirements::DIRSTATE_V2_REQUIREMENT) 239 .contains(requirements::DIRSTATE_V2_REQUIREMENT)
229 } 240 }
230 241
231 pub fn dirstate_parents( 242 fn dirstate_file_contents(&self) -> Result<Vec<u8>, HgError> {
232 &self, 243 Ok(self
233 ) -> Result<crate::dirstate::DirstateParents, HgError> { 244 .hg_vfs()
234 let dirstate = self.hg_vfs().mmap_open("dirstate")?; 245 .read("dirstate")
235 if dirstate.is_empty() { 246 .io_not_found_as_none()?
236 return Ok(crate::dirstate::DirstateParents::NULL); 247 .unwrap_or(Vec::new()))
237 } 248 }
238 let parents = if self.has_dirstate_v2() { 249
250 pub fn dirstate_parents(&self) -> Result<DirstateParents, HgError> {
251 if let Some(parents) = self.dirstate_parents.get() {
252 return Ok(parents);
253 }
254 let dirstate = self.dirstate_file_contents()?;
255 let parents = if dirstate.is_empty() {
256 DirstateParents::NULL
257 } else if self.has_dirstate_v2() {
239 crate::dirstate_tree::on_disk::read_docket(&dirstate)?.parents() 258 crate::dirstate_tree::on_disk::read_docket(&dirstate)?.parents()
240 } else { 259 } else {
241 crate::dirstate::parsers::parse_dirstate_parents(&dirstate)? 260 crate::dirstate::parsers::parse_dirstate_parents(&dirstate)?
242 .clone() 261 .clone()
243 }; 262 };
263 self.dirstate_parents.set(Some(parents));
244 Ok(parents) 264 Ok(parents)
245 } 265 }
246 } 266
267 fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> {
268 let dirstate_file_contents = self.dirstate_file_contents()?;
269 if dirstate_file_contents.is_empty() {
270 self.dirstate_parents.set(Some(DirstateParents::NULL));
271 Ok(OwningDirstateMap::new_empty(Vec::new()))
272 } else if self.has_dirstate_v2() {
273 let docket = crate::dirstate_tree::on_disk::read_docket(
274 &dirstate_file_contents,
275 )?;
276 self.dirstate_parents.set(Some(docket.parents()));
277 let data_size = docket.data_size();
278 let metadata = docket.tree_metadata();
279 let mut map = if let Some(data_mmap) = self
280 .hg_vfs()
281 .mmap_open(docket.data_filename())
282 .io_not_found_as_none()?
283 {
284 OwningDirstateMap::new_empty(MmapWrapper(data_mmap))
285 } else {
286 OwningDirstateMap::new_empty(Vec::new())
287 };
288 let (on_disk, placeholder) = map.get_mut_pair();
289 *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?;
290 Ok(map)
291 } else {
292 let mut map = OwningDirstateMap::new_empty(dirstate_file_contents);
293 let (on_disk, placeholder) = map.get_mut_pair();
294 let (inner, parents) = DirstateMap::new_v1(on_disk)?;
295 self.dirstate_parents
296 .set(Some(parents.unwrap_or(DirstateParents::NULL)));
297 *placeholder = inner;
298 Ok(map)
299 }
300 }
301
302 pub fn dirstate_map(
303 &self,
304 ) -> Result<Ref<OwningDirstateMap>, DirstateError> {
305 let mut borrowed = self.dirstate_map.borrow();
306 if borrowed.is_none() {
307 drop(borrowed);
308 // Only use `borrow_mut` if it is really needed to avoid panic in
309 // case there is another outstanding borrow but mutation is not
310 // needed.
311 *self.dirstate_map.borrow_mut() = Some(self.new_dirstate_map()?);
312 borrowed = self.dirstate_map.borrow()
313 }
314 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap()))
315 }
316
317 pub fn dirstate_map_mut(
318 &self,
319 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
320 let mut borrowed = self.dirstate_map.borrow_mut();
321 if borrowed.is_none() {
322 *borrowed = Some(self.new_dirstate_map()?);
323 }
324 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap()))
325 }
326 }
327
328 // TODO: remove this when https://github.com/RazrFalcon/memmap2-rs/pull/22 is on crates.io
329 struct MmapWrapper(memmap2::Mmap);
330
331 impl std::ops::Deref for MmapWrapper {
332 type Target = [u8];
333
334 fn deref(&self) -> &[u8] {
335 self.0.deref()
336 }
337 }
338
339 unsafe impl stable_deref_trait::StableDeref for MmapWrapper {}