1 use crate::changelog::Changelog; |
1 use crate::changelog::Changelog; |
2 use crate::config::{Config, ConfigError, ConfigParseError}; |
2 use crate::config::{Config, ConfigError, ConfigParseError}; |
3 use crate::dirstate::DirstateParents; |
3 use crate::dirstate::DirstateParents; |
4 use crate::dirstate_tree::dirstate_map::DirstateMap; |
4 use crate::dirstate_tree::dirstate_map::DirstateMap; |
|
5 use crate::dirstate_tree::on_disk::Docket as DirstateDocket; |
5 use crate::dirstate_tree::owning::OwningDirstateMap; |
6 use crate::dirstate_tree::owning::OwningDirstateMap; |
6 use crate::errors::HgError; |
|
7 use crate::errors::HgResultExt; |
7 use crate::errors::HgResultExt; |
|
8 use crate::errors::{HgError, IoResultExt}; |
8 use crate::exit_codes; |
9 use crate::exit_codes; |
9 use crate::lock::{try_with_lock_no_wait, LockError}; |
10 use crate::lock::{try_with_lock_no_wait, LockError}; |
10 use crate::manifest::{Manifest, Manifestlog}; |
11 use crate::manifest::{Manifest, Manifestlog}; |
11 use crate::revlog::filelog::Filelog; |
12 use crate::revlog::filelog::Filelog; |
12 use crate::revlog::revlog::RevlogError; |
13 use crate::revlog::revlog::RevlogError; |
16 use crate::vfs::{is_dir, is_file, Vfs}; |
17 use crate::vfs::{is_dir, is_file, Vfs}; |
17 use crate::{requirements, NodePrefix}; |
18 use crate::{requirements, NodePrefix}; |
18 use crate::{DirstateError, Revision}; |
19 use crate::{DirstateError, Revision}; |
19 use std::cell::{Ref, RefCell, RefMut}; |
20 use std::cell::{Ref, RefCell, RefMut}; |
20 use std::collections::HashSet; |
21 use std::collections::HashSet; |
|
22 use std::io::Seek; |
|
23 use std::io::SeekFrom; |
|
24 use std::io::Write as IoWrite; |
21 use std::path::{Path, PathBuf}; |
25 use std::path::{Path, PathBuf}; |
22 |
26 |
23 /// A repository on disk |
27 /// A repository on disk |
24 pub struct Repo { |
28 pub struct Repo { |
25 working_directory: PathBuf, |
29 working_directory: PathBuf, |
414 } |
418 } |
415 |
419 |
416 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> { |
420 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> { |
417 Filelog::open(self, path) |
421 Filelog::open(self, path) |
418 } |
422 } |
|
423 |
|
424 /// Write to disk any updates that were made through `dirstate_map_mut`. |
|
425 /// |
|
426 /// The "wlock" must be held while calling this. |
|
427 /// See for example `try_with_wlock_no_wait`. |
|
428 /// |
|
429 /// TODO: have a `WritableRepo` type only accessible while holding the |
|
430 /// lock? |
|
431 pub fn write_dirstate(&self) -> Result<(), DirstateError> { |
|
432 let map = self.dirstate_map()?; |
|
433 // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if |
|
434 // it’s unset |
|
435 let parents = self.dirstate_parents()?; |
|
436 let packed_dirstate = if self.has_dirstate_v2() { |
|
437 let uuid = self.dirstate_data_file_uuid.get_or_init(self)?; |
|
438 let mut uuid = uuid.as_ref(); |
|
439 let can_append = uuid.is_some(); |
|
440 let (data, tree_metadata, append) = map.pack_v2(can_append)?; |
|
441 if !append { |
|
442 uuid = None |
|
443 } |
|
444 let uuid = if let Some(uuid) = uuid { |
|
445 std::str::from_utf8(uuid) |
|
446 .map_err(|_| { |
|
447 HgError::corrupted("non-UTF-8 dirstate data file ID") |
|
448 })? |
|
449 .to_owned() |
|
450 } else { |
|
451 DirstateDocket::new_uid() |
|
452 }; |
|
453 let data_filename = format!("dirstate.{}", uuid); |
|
454 let data_filename = self.hg_vfs().join(data_filename); |
|
455 let mut options = std::fs::OpenOptions::new(); |
|
456 if append { |
|
457 options.append(true); |
|
458 } else { |
|
459 options.write(true).create_new(true); |
|
460 } |
|
461 let data_size = (|| { |
|
462 // TODO: loop and try another random ID if !append and this |
|
463 // returns `ErrorKind::AlreadyExists`? Collision chance of two |
|
464 // random IDs is one in 2**32 |
|
465 let mut file = options.open(&data_filename)?; |
|
466 file.write_all(&data)?; |
|
467 file.flush()?; |
|
468 // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+ |
|
469 file.seek(SeekFrom::Current(0)) |
|
470 })() |
|
471 .when_writing_file(&data_filename)?; |
|
472 DirstateDocket::serialize( |
|
473 parents, |
|
474 tree_metadata, |
|
475 data_size, |
|
476 uuid.as_bytes(), |
|
477 ) |
|
478 .map_err(|_: std::num::TryFromIntError| { |
|
479 HgError::corrupted("overflow in dirstate docket serialization") |
|
480 })? |
|
481 } else { |
|
482 map.pack_v1(parents)? |
|
483 }; |
|
484 self.hg_vfs().atomic_write("dirstate", &packed_dirstate)?; |
|
485 Ok(()) |
|
486 } |
419 } |
487 } |
420 |
488 |
421 /// Lazily-initialized component of `Repo` with interior mutability |
489 /// Lazily-initialized component of `Repo` with interior mutability |
422 /// |
490 /// |
423 /// This differs from `OnceCell` in that the value can still be "deinitialized" |
491 /// This differs from `OnceCell` in that the value can still be "deinitialized" |