##// END OF EJS Templates
dirstate-v2: complain early on docket name collision...
dirstate-v2: complain early on docket name collision The alternative is that the dirstate gets deleted so the corruption persists and is hard to investigate. This happened to me in tests, where the dirstate names are taken from file, since the file got reverted. I expect this can also happen in prod with non-trivial probability (1/4 billion).

File last commit:

r50832:75040950 default
r50992:ca9d65d6 stable
Show More
repo.rs
567 lines | 19.0 KiB | application/rls-services+xml | RustLexer
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 use crate::changelog::Changelog;
Simon Sapin
rhg: Parse per-repository configuration...
r47215 use crate::config::{Config, ConfigError, ConfigParseError};
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 use crate::dirstate::DirstateParents;
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 use crate::dirstate_tree::on_disk::Docket as DirstateDocket;
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 use crate::dirstate_tree::owning::OwningDirstateMap;
use crate::errors::HgResultExt;
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 use crate::errors::{HgError, IoResultExt};
Simon Sapin
rhg: Initial repository locking...
r49245 use crate::lock::{try_with_lock_no_wait, LockError};
Simon Sapin
rust: Add Repo::manifest(revision)...
r48774 use crate::manifest::{Manifest, Manifestlog};
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 use crate::revlog::filelog::Filelog;
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 use crate::revlog::revlog::RevlogError;
Simon Sapin
rhg: initial support for shared repositories...
r47190 use crate::utils::files::get_path_from_bytes;
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 use crate::utils::hg_path::HgPath;
Simon Sapin
rhg: Don’t make repository path absolute too early...
r47474 use crate::utils::SliceExt;
Simon Sapin
rust: Move VFS code to its own module...
r48764 use crate::vfs::{is_dir, is_file, Vfs};
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 use crate::{requirements, NodePrefix};
Simon Sapin
rust: Add Repo::manifest(revision)...
r48774 use crate::{DirstateError, Revision};
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 use std::cell::{Ref, RefCell, RefMut};
Simon Sapin
rhg: initial support for shared repositories...
r47190 use std::collections::HashSet;
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write as IoWrite;
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 use std::path::{Path, PathBuf};
/// A repository on disk
pub struct Repo {
working_directory: PathBuf,
dot_hg: PathBuf,
store: PathBuf,
Simon Sapin
rhg: initial support for shared repositories...
r47190 requirements: HashSet<String>,
Simon Sapin
rhg: Parse per-repository configuration...
r47215 config: Config,
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 dirstate_parents: LazyCell<DirstateParents>,
dirstate_data_file_uuid: LazyCell<Option<Vec<u8>>>,
dirstate_map: LazyCell<OwningDirstateMap>,
changelog: LazyCell<Changelog>,
manifestlog: LazyCell<Manifestlog>,
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 }
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 #[derive(Debug, derive_more::From)]
Simon Sapin
rhg: Parse per-repository configuration...
r47215 pub enum RepoError {
NotFound {
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253 at: PathBuf,
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 },
#[from]
Simon Sapin
rhg: Parse per-repository configuration...
r47215 ConfigParseError(ConfigParseError),
#[from]
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 Other(HgError),
}
Simon Sapin
rhg: Parse per-repository configuration...
r47215 impl From<ConfigError> for RepoError {
fn from(error: ConfigError) -> Self {
match error {
ConfigError::Parse(error) => error.into(),
ConfigError::Other(error) => error.into(),
}
}
}
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 impl Repo {
Pulkit Goyal
rhg: look for repository in ancestors also instead of cwd only...
r48197 /// tries to find nearest repository root in current working directory or
/// its ancestors
pub fn find_repo_root() -> Result<PathBuf, RepoError> {
let current_directory = crate::utils::current_dir()?;
// ancestors() is inclusive: it first yields `current_directory`
// as-is.
for ancestor in current_directory.ancestors() {
Simon Sapin
rhg: Propagate permission errors when finding a repository...
r48584 if is_dir(ancestor.join(".hg"))? {
Pulkit Goyal
rhg: look for repository in ancestors also instead of cwd only...
r48197 return Ok(ancestor.to_path_buf());
}
}
return Err(RepoError::NotFound {
at: current_directory,
});
}
Simon Sapin
rhg: add limited support for the `config` sub-command...
r47255 /// Find a repository, either at the given path (which must contain a `.hg`
/// sub-directory) or by searching the current directory and its
/// ancestors.
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253 ///
Simon Sapin
rhg: add limited support for the `config` sub-command...
r47255 /// A method with two very different "modes" like this usually a code smell
/// to make two methods instead, but in this case an `Option` is what rhg
/// sub-commands get from Clap for the `-R` / `--repository` CLI argument.
/// Having two methods would just move that `if` to almost all callers.
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253 pub fn find(
config: &Config,
Pulkit Goyal
rhg: read [paths] for `--repository` value...
r48196 explicit_path: Option<PathBuf>,
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253 ) -> Result<Self, RepoError> {
if let Some(root) = explicit_path {
Simon Sapin
rhg: Propagate permission errors when finding a repository...
r48584 if is_dir(root.join(".hg"))? {
Simon Sapin
rhg: Don’t make repository path absolute too early...
r47474 Self::new_at_path(root.to_owned(), config)
Simon Sapin
rhg: Propagate permission errors when finding a repository...
r48584 } else if is_file(&root)? {
Simon Sapin
rhg: Fall back to Python for bundle repositories...
r47464 Err(HgError::unsupported("bundle repository").into())
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253 } else {
Err(RepoError::NotFound {
at: root.to_owned(),
})
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 }
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253 } else {
Pulkit Goyal
rhg: look for repository in ancestors also instead of cwd only...
r48197 let root = Self::find_repo_root()?;
Self::new_at_path(root, config)
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 }
}
Simon Sapin
rhg: initial support for shared repositories...
r47190 /// To be called after checking that `.hg` is a sub-directory
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 fn new_at_path(
working_directory: PathBuf,
config: &Config,
Simon Sapin
rhg: Parse per-repository configuration...
r47215 ) -> Result<Self, RepoError> {
Simon Sapin
rhg: initial support for shared repositories...
r47190 let dot_hg = working_directory.join(".hg");
Simon Sapin
rhg: add support for share-safe...
r47191
Simon Sapin
rhg: Parse per-repository configuration...
r47215 let mut repo_config_files = Vec::new();
repo_config_files.push(dot_hg.join("hgrc"));
repo_config_files.push(dot_hg.join("hgrc-not-shared"));
Simon Sapin
rhg: initial support for shared repositories...
r47190 let hg_vfs = Vfs { base: &dot_hg };
Simon Sapin
rhg: add support for share-safe...
r47191 let mut reqs = requirements::load_if_exists(hg_vfs)?;
Simon Sapin
rhg: initial support for shared repositories...
r47190 let relative =
reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
let shared =
reqs.contains(requirements::SHARED_REQUIREMENT) || relative;
Simon Sapin
rhg: add support for share-safe...
r47191
// From `mercurial/localrepo.py`:
//
// if .hg/requires contains the sharesafe requirement, it means
// there exists a `.hg/store/requires` too and we should read it
// NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
// is present. We never write SHARESAFE_REQUIREMENT for a repo if store
// is not present, refer checkrequirementscompat() for that
//
// However, if SHARESAFE_REQUIREMENT is not present, it means that the
// repository was shared the old way. We check the share source
// .hg/requires for SHARESAFE_REQUIREMENT to detect whether the
// current repository needs to be reshared
let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT);
Simon Sapin
rhg: initial support for shared repositories...
r47190 let store_path;
if !shared {
store_path = dot_hg.join("store");
} else {
let bytes = hg_vfs.read("sharedpath")?;
Simon Sapin
rhg: Ignore trailing newlines in .hg/sharedpath...
r47427 let mut shared_path =
Simon Sapin
rust: Generalize the `trim_end_newlines` utility of byte strings...
r48761 get_path_from_bytes(bytes.trim_end_matches(|b| b == b'\n'))
.to_owned();
Simon Sapin
rhg: initial support for shared repositories...
r47190 if relative {
shared_path = dot_hg.join(shared_path)
}
Simon Sapin
rhg: Propagate permission errors when finding a repository...
r48584 if !is_dir(&shared_path)? {
Simon Sapin
rhg: initial support for shared repositories...
r47190 return Err(HgError::corrupted(format!(
".hg/sharedpath points to nonexistent directory {}",
shared_path.display()
Simon Sapin
rhg: Parse per-repository configuration...
r47215 ))
.into());
Simon Sapin
rhg: initial support for shared repositories...
r47190 }
store_path = shared_path.join("store");
Simon Sapin
rhg: add support for share-safe...
r47191
let source_is_share_safe =
requirements::load(Vfs { base: &shared_path })?
.contains(requirements::SHARESAFE_REQUIREMENT);
Arseniy Alekseyev
rhg: simplify the handling of share-safe config mismatch...
r49664 if share_safe != source_is_share_safe {
return Err(HgError::unsupported("share-safe mismatch").into());
Simon Sapin
rhg: add support for share-safe...
r47191 }
Simon Sapin
rhg: Parse per-repository configuration...
r47215
if share_safe {
repo_config_files.insert(0, shared_path.join("hgrc"))
}
Simon Sapin
rhg: initial support for shared repositories...
r47190 }
Simon Sapin
rhg: Bug fix: with share-safe, always read store requirements...
r47357 if share_safe {
reqs.extend(requirements::load(Vfs { base: &store_path })?);
}
Simon Sapin
rhg: initial support for shared repositories...
r47190
Simon Sapin
rhg: Add support for the HGRCSKIPREPO environment variable...
r47475 let repo_config = if std::env::var_os("HGRCSKIPREPO").is_none() {
config.combine_with_repo(&repo_config_files)?
} else {
config.clone()
};
Simon Sapin
rhg: Parse per-repository configuration...
r47215
Simon Sapin
rhg: initial support for shared repositories...
r47190 let repo = Self {
requirements: reqs,
working_directory,
store: store_path,
dot_hg,
Simon Sapin
rhg: Parse per-repository configuration...
r47215 config: repo_config,
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 dirstate_parents: LazyCell::new(),
dirstate_data_file_uuid: LazyCell::new(),
dirstate_map: LazyCell::new(),
changelog: LazyCell::new(),
manifestlog: LazyCell::new(),
Simon Sapin
rhg: initial support for shared repositories...
r47190 };
requirements::check(&repo)?;
Ok(repo)
}
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 pub fn working_directory_path(&self) -> &Path {
&self.working_directory
}
Simon Sapin
rhg: initial support for shared repositories...
r47190 pub fn requirements(&self) -> &HashSet<String> {
&self.requirements
}
Simon Sapin
rhg: Parse per-repository configuration...
r47215 pub fn config(&self) -> &Config {
&self.config
}
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 /// For accessing repository files (in `.hg`), except for the store
/// (`.hg/store`).
Simon Sapin
rust: Add a log file rotation utility...
r47341 pub fn hg_vfs(&self) -> Vfs<'_> {
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 Vfs { base: &self.dot_hg }
}
/// For accessing repository store files (in `.hg/store`)
Simon Sapin
rust: Add a log file rotation utility...
r47341 pub fn store_vfs(&self) -> Vfs<'_> {
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 Vfs { base: &self.store }
}
/// For accessing the working copy
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 pub fn working_directory_vfs(&self) -> Vfs<'_> {
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 Vfs {
base: &self.working_directory,
}
}
Simon Sapin
rhg: Add support for the blackbox extension...
r47343
Simon Sapin
rhg: Initial repository locking...
r49245 pub fn try_with_wlock_no_wait<R>(
&self,
f: impl FnOnce() -> R,
) -> Result<R, LockError> {
try_with_lock_no_wait(self.hg_vfs(), "wlock", f)
}
Simon Sapin
rhg: Add support for dirstate-v2...
r48165 pub fn has_dirstate_v2(&self) -> bool {
self.requirements
.contains(requirements::DIRSTATE_V2_REQUIREMENT)
}
Arseniy Alekseyev
rhg: add support for narrow clones and sparse checkouts...
r49238 pub fn has_sparse(&self) -> bool {
self.requirements.contains(requirements::SPARSE_REQUIREMENT)
}
pub fn has_narrow(&self) -> bool {
self.requirements.contains(requirements::NARROW_REQUIREMENT)
}
Martin von Zweigbergk
rust-repo: extract a function for checking nodemap requirement...
r49982 pub fn has_nodemap(&self) -> bool {
self.requirements
.contains(requirements::NODEMAP_REQUIREMENT)
}
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 fn dirstate_file_contents(&self) -> Result<Vec<u8>, HgError> {
Ok(self
.hg_vfs()
.read("dirstate")
.io_not_found_as_none()?
.unwrap_or(Vec::new()))
}
pub fn dirstate_parents(&self) -> Result<DirstateParents, HgError> {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 Ok(*self
.dirstate_parents
.get_or_init(|| self.read_dirstate_parents())?)
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 }
fn read_dirstate_parents(&self) -> Result<DirstateParents, HgError> {
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 let dirstate = self.dirstate_file_contents()?;
let parents = if dirstate.is_empty() {
Simon Sapin
rhg: Add lazy/cached dirstate data file ID parsing on Repo...
r49248 if self.has_dirstate_v2() {
self.dirstate_data_file_uuid.set(None);
}
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 DirstateParents::NULL
} else if self.has_dirstate_v2() {
Simon Sapin
rhg: Add lazy/cached dirstate data file ID parsing on Repo...
r49248 let docket =
crate::dirstate_tree::on_disk::read_docket(&dirstate)?;
self.dirstate_data_file_uuid
.set(Some(docket.uuid.to_owned()));
docket.parents()
Simon Sapin
rhg: Add support for dirstate-v2...
r48165 } else {
crate::dirstate::parsers::parse_dirstate_parents(&dirstate)?
Simon Sapin
dirstate-v2: Introduce a docket file...
r48474 .clone()
Simon Sapin
rhg: Add support for dirstate-v2...
r48165 };
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 self.dirstate_parents.set(parents);
Simon Sapin
dirstate-v2: Introduce a docket file...
r48474 Ok(parents)
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 }
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768
Simon Sapin
rhg: Add lazy/cached dirstate data file ID parsing on Repo...
r49248 fn read_dirstate_data_file_uuid(
&self,
) -> Result<Option<Vec<u8>>, HgError> {
assert!(
self.has_dirstate_v2(),
"accessing dirstate data file ID without dirstate-v2"
);
let dirstate = self.dirstate_file_contents()?;
if dirstate.is_empty() {
self.dirstate_parents.set(DirstateParents::NULL);
Ok(None)
} else {
let docket =
crate::dirstate_tree::on_disk::read_docket(&dirstate)?;
self.dirstate_parents.set(docket.parents());
Ok(Some(docket.uuid.to_owned()))
}
}
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> {
let dirstate_file_contents = self.dirstate_file_contents()?;
if dirstate_file_contents.is_empty() {
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 self.dirstate_parents.set(DirstateParents::NULL);
Simon Sapin
rhg: Add lazy/cached dirstate data file ID parsing on Repo...
r49248 if self.has_dirstate_v2() {
self.dirstate_data_file_uuid.set(None);
}
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 Ok(OwningDirstateMap::new_empty(Vec::new()))
} else if self.has_dirstate_v2() {
let docket = crate::dirstate_tree::on_disk::read_docket(
&dirstate_file_contents,
)?;
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 self.dirstate_parents.set(docket.parents());
Simon Sapin
rhg: Add lazy/cached dirstate data file ID parsing on Repo...
r49248 self.dirstate_data_file_uuid
.set(Some(docket.uuid.to_owned()));
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 let data_size = docket.data_size();
let metadata = docket.tree_metadata();
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 if let Some(data_mmap) = self
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 .hg_vfs()
.mmap_open(docket.data_filename())
.io_not_found_as_none()?
{
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 } else {
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 OwningDirstateMap::new_v2(Vec::new(), data_size, metadata)
}
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 } else {
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 let (map, parents) =
OwningDirstateMap::new_v1(dirstate_file_contents)?;
self.dirstate_parents.set(parents);
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 Ok(map)
}
}
pub fn dirstate_map(
&self,
) -> Result<Ref<OwningDirstateMap>, DirstateError> {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 self.dirstate_map.get_or_init(|| self.new_dirstate_map())
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 }
pub fn dirstate_map_mut(
&self,
) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 self.dirstate_map
.get_mut_or_init(|| self.new_dirstate_map())
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 }
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773
Martin von Zweigbergk
rust-revlog: make `Changelog` and `ManifestLog` unaware of `Repo`...
r49981 fn new_changelog(&self) -> Result<Changelog, HgError> {
Martin von Zweigbergk
rust-repo: extract a function for checking nodemap requirement...
r49982 Changelog::open(&self.store_vfs(), self.has_nodemap())
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 }
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 pub fn changelog(&self) -> Result<Ref<Changelog>, HgError> {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 self.changelog.get_or_init(|| self.new_changelog())
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 }
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 pub fn changelog_mut(&self) -> Result<RefMut<Changelog>, HgError> {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 self.changelog.get_mut_or_init(|| self.new_changelog())
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 }
Martin von Zweigbergk
rust-revlog: make `Changelog` and `ManifestLog` unaware of `Repo`...
r49981 fn new_manifestlog(&self) -> Result<Manifestlog, HgError> {
Martin von Zweigbergk
rust-repo: extract a function for checking nodemap requirement...
r49982 Manifestlog::open(&self.store_vfs(), self.has_nodemap())
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 }
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 pub fn manifestlog(&self) -> Result<Ref<Manifestlog>, HgError> {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 self.manifestlog.get_or_init(|| self.new_manifestlog())
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 }
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 pub fn manifestlog_mut(&self) -> Result<RefMut<Manifestlog>, HgError> {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 self.manifestlog.get_mut_or_init(|| self.new_manifestlog())
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 }
Simon Sapin
rust: Add Repo::manifest(revision)...
r48774
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 /// Returns the manifest of the *changeset* with the given node ID
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 pub fn manifest_for_node(
&self,
node: impl Into<NodePrefix>,
) -> Result<Manifest, RevlogError> {
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 self.manifestlog()?.data_for_node(
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 self.changelog()?
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 .data_for_node(node.into())?
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 .manifest_node()?
.into(),
)
}
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 /// Returns the manifest of the *changeset* with the given revision number
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 pub fn manifest_for_rev(
Simon Sapin
rust: Add Repo::manifest(revision)...
r48774 &self,
revision: Revision,
) -> Result<Manifest, RevlogError> {
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 self.manifestlog()?.data_for_node(
self.changelog()?
.data_for_rev(revision)?
.manifest_node()?
.into(),
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 )
Simon Sapin
rust: Add Repo::manifest(revision)...
r48774 }
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775
Simon Sapin
rhg: Sub-repositories are not supported...
r49341 pub fn has_subrepos(&self) -> Result<bool, DirstateError> {
if let Some(entry) = self.dirstate_map()?.get(HgPath::new(".hgsub"))? {
Raphaël Gomès
rust: use `entry.tracked()` directly...
r50027 Ok(entry.tracked())
Simon Sapin
rhg: Sub-repositories are not supported...
r49341 } else {
Ok(false)
}
}
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
Simon Sapin
rust: Add a Filelog struct that wraps Revlog...
r48775 Filelog::open(self, path)
}
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249
/// 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()?;
Raphaël Gomès
rust-dirstate-v2: clean up previous data file after the docket is written...
r50038 let (packed_dirstate, old_uuid_to_remove) = if self.has_dirstate_v2() {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 let uuid_opt = self
.dirstate_data_file_uuid
.get_or_init(|| self.read_dirstate_data_file_uuid())?;
Raphaël Gomès
rhg: fix dirstate-v2 data file removal system...
r50044 let uuid_opt = uuid_opt.as_ref();
let can_append = uuid_opt.is_some();
Raphaël Gomès
rust-dirstate-v2: save proper data size if no new data on append...
r50037 let (data, tree_metadata, append, old_data_size) =
map.pack_v2(can_append)?;
Raphaël Gomès
rhg: fix dirstate-v2 data file removal system...
r50044
// Reuse the uuid, or generate a new one, keeping the old for
// deletion.
let (uuid, old_uuid) = match uuid_opt {
Some(uuid) => {
let as_str = std::str::from_utf8(uuid)
.map_err(|_| {
HgError::corrupted(
"non-UTF-8 dirstate data file ID",
)
})?
.to_owned();
if append {
(as_str, None)
} else {
(DirstateDocket::new_uid(), Some(as_str))
}
}
None => (DirstateDocket::new_uid(), None),
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 };
Raphaël Gomès
rhg: fix dirstate-v2 data file removal system...
r50044
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 let data_filename = format!("dirstate.{}", uuid);
let data_filename = self.hg_vfs().join(data_filename);
let mut options = std::fs::OpenOptions::new();
Arseniy Alekseyev
rhg: align the dirstate v2 writing algorithm with python...
r50097 options.write(true);
// Why are we not using the O_APPEND flag when appending?
//
// - O_APPEND makes it trickier to deal with garbage at the end of
// the file, left by a previous uncommitted transaction. By
// starting the write at [old_data_size] we make sure we erase
// all such garbage.
//
// - O_APPEND requires to special-case 0-byte writes, whereas we
// don't need that.
//
// - Some OSes have bugs in implementation O_APPEND:
// revlog.py talks about a Solaris bug, but we also saw some ZFS
// bug: https://github.com/openzfs/zfs/pull/3124,
// https://github.com/openzfs/zfs/issues/13370
//
if !append {
options.create_new(true);
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 }
Arseniy Alekseyev
rhg: align the dirstate v2 writing algorithm with python...
r50097
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 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)?;
Arseniy Alekseyev
rhg: align the dirstate v2 writing algorithm with python...
r50097 if append {
file.seek(SeekFrom::Start(old_data_size as u64))?;
Raphaël Gomès
rust-dirstate-v2: save proper data size if no new data on append...
r50037 }
Arseniy Alekseyev
rhg: align the dirstate v2 writing algorithm with python...
r50097 file.write_all(&data)?;
file.flush()?;
file.seek(SeekFrom::Current(0))
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 })()
.when_writing_file(&data_filename)?;
Raphaël Gomès
rust-dirstate-v2: clean up previous data file after the docket is written...
r50038
let packed_dirstate = DirstateDocket::serialize(
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 parents,
tree_metadata,
data_size,
uuid.as_bytes(),
)
.map_err(|_: std::num::TryFromIntError| {
HgError::corrupted("overflow in dirstate docket serialization")
Raphaël Gomès
rust-dirstate-v2: clean up previous data file after the docket is written...
r50038 })?;
(packed_dirstate, old_uuid)
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 } else {
Raphaël Gomès
rust-dirstate-v2: clean up previous data file after the docket is written...
r50038 (map.pack_v1(parents)?, None)
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 };
Raphaël Gomès
rust-dirstate-v2: clean up previous data file after the docket is written...
r50038
let vfs = self.hg_vfs();
vfs.atomic_write("dirstate", &packed_dirstate)?;
if let Some(uuid) = old_uuid_to_remove {
// Remove the old data file after the new docket pointing to the
// new data file was written.
vfs.remove_file(format!("dirstate.{}", uuid))?;
}
Simon Sapin
rhg: Add Repo::write_dirstate...
r49249 Ok(())
}
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 }
/// Lazily-initialized component of `Repo` with interior mutability
///
/// This differs from `OnceCell` in that the value can still be "deinitialized"
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 /// later by setting its inner `Option` to `None`. It also takes the
/// initialization function as an argument when the value is requested, not
/// when the instance is created.
struct LazyCell<T> {
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 value: RefCell<Option<T>>,
}
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 impl<T> LazyCell<T> {
fn new() -> Self {
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 Self {
value: RefCell::new(None),
}
}
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 fn set(&self, value: T) {
*self.value.borrow_mut() = Some(value)
}
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 fn get_or_init<E>(
&self,
init: impl Fn() -> Result<T, E>,
) -> Result<Ref<T>, E> {
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 let mut borrowed = self.value.borrow();
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 if borrowed.is_none() {
drop(borrowed);
// Only use `borrow_mut` if it is really needed to avoid panic in
// case there is another outstanding borrow but mutation is not
// needed.
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 *self.value.borrow_mut() = Some(init()?);
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 borrowed = self.value.borrow()
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 }
Ok(Ref::map(borrowed, |option| option.as_ref().unwrap()))
}
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 fn get_mut_or_init<E>(
&self,
init: impl Fn() -> Result<T, E>,
) -> Result<RefMut<T>, E> {
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 let mut borrowed = self.value.borrow_mut();
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 if borrowed.is_none() {
Martin von Zweigbergk
rust-repo: make `Send` by not storing functions in `LazyCell`...
r50072 *borrowed = Some(init()?);
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 }
Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap()))
}
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 }