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 { |
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 {} |