Mercurial > public > mercurial-scm > hg
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 {} |