Mercurial > public > mercurial-scm > hg-stable
diff rust/hg-core/src/repo.rs @ 48467:2097f63575a5
rhg: Add Repo::write_dirstate
This method is not used yet. It saves to disk any mutation that was done to
the `Repo`?s dirstate through `Repo::dirstate_map_mut`. It takes care of
dirstate-v1 v.s. dirstate-v2, dockets, data files, appending when possible,
etc.
Differential Revision: https://phab.mercurial-scm.org/D11839
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Thu, 02 Dec 2021 15:10:03 +0100 |
parents | c7c23bb036c9 |
children | 7f633432ca92 |
line wrap: on
line diff
--- a/rust/hg-core/src/repo.rs Thu Dec 02 12:05:36 2021 +0100 +++ b/rust/hg-core/src/repo.rs Thu Dec 02 15:10:03 2021 +0100 @@ -2,9 +2,10 @@ use crate::config::{Config, ConfigError, ConfigParseError}; use crate::dirstate::DirstateParents; use crate::dirstate_tree::dirstate_map::DirstateMap; +use crate::dirstate_tree::on_disk::Docket as DirstateDocket; use crate::dirstate_tree::owning::OwningDirstateMap; -use crate::errors::HgError; use crate::errors::HgResultExt; +use crate::errors::{HgError, IoResultExt}; use crate::exit_codes; use crate::lock::{try_with_lock_no_wait, LockError}; use crate::manifest::{Manifest, Manifestlog}; @@ -18,6 +19,9 @@ use crate::{DirstateError, Revision}; use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashSet; +use std::io::Seek; +use std::io::SeekFrom; +use std::io::Write as IoWrite; use std::path::{Path, PathBuf}; /// A repository on disk @@ -416,6 +420,70 @@ pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> { Filelog::open(self, path) } + + /// Write to disk any updates that were made through `dirstate_map_mut`. + /// + /// The "wlock" must be held while calling this. + /// See for example `try_with_wlock_no_wait`. + /// + /// TODO: have a `WritableRepo` type only accessible while holding the + /// lock? + pub fn write_dirstate(&self) -> Result<(), DirstateError> { + let map = self.dirstate_map()?; + // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if + // it’s unset + let parents = self.dirstate_parents()?; + let packed_dirstate = if self.has_dirstate_v2() { + let uuid = self.dirstate_data_file_uuid.get_or_init(self)?; + let mut uuid = uuid.as_ref(); + let can_append = uuid.is_some(); + let (data, tree_metadata, append) = map.pack_v2(can_append)?; + if !append { + uuid = None + } + let uuid = if let Some(uuid) = uuid { + std::str::from_utf8(uuid) + .map_err(|_| { + HgError::corrupted("non-UTF-8 dirstate data file ID") + })? + .to_owned() + } else { + DirstateDocket::new_uid() + }; + let data_filename = format!("dirstate.{}", uuid); + let data_filename = self.hg_vfs().join(data_filename); + let mut options = std::fs::OpenOptions::new(); + if append { + options.append(true); + } else { + options.write(true).create_new(true); + } + let data_size = (|| { + // TODO: loop and try another random ID if !append and this + // returns `ErrorKind::AlreadyExists`? Collision chance of two + // random IDs is one in 2**32 + let mut file = options.open(&data_filename)?; + file.write_all(&data)?; + file.flush()?; + // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+ + file.seek(SeekFrom::Current(0)) + })() + .when_writing_file(&data_filename)?; + DirstateDocket::serialize( + parents, + tree_metadata, + data_size, + uuid.as_bytes(), + ) + .map_err(|_: std::num::TryFromIntError| { + HgError::corrupted("overflow in dirstate docket serialization") + })? + } else { + map.pack_v1(parents)? + }; + self.hg_vfs().atomic_write("dirstate", &packed_dirstate)?; + Ok(()) + } } /// Lazily-initialized component of `Repo` with interior mutability