##// END OF EJS Templates
rhg: desambiguate status without decompressing filelog if possible...
rhg: desambiguate status without decompressing filelog if possible When status is unsure based on `stat()` and the dirstate if a file is clean or modified, we need to compare it against the filelog. This comparison can skip looking at contents if the lengths differ. This changeset optimize this further to deduce what we can about the length if the filelog without decompressing it or resolving deltas. Differential Revision: https://phab.mercurial-scm.org/D11965

File last commit:

r49341:7f633432 default
r49378:e91aa800 default
Show More
repo.rs
540 lines | 18.7 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;
use crate::dirstate_tree::dirstate_map::DirstateMap;
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: Reuse manifest when checking status of multiple ambiguous files...
r48778 use crate::exit_codes;
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,
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 dirstate_parents: LazyCell<DirstateParents, HgError>,
Simon Sapin
rhg: Add lazy/cached dirstate data file ID parsing on Repo...
r49248 dirstate_data_file_uuid: LazyCell<Option<Vec<u8>>, HgError>,
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 dirstate_map: LazyCell<OwningDirstateMap, DirstateError>,
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 changelog: LazyCell<Changelog, HgError>,
manifestlog: LazyCell<Manifestlog, HgError>,
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);
if share_safe && !source_is_share_safe {
Simon Sapin
rhg: Parse per-repository configuration...
r47215 return Err(match config
Simon Sapin
rhg: Align with Python on some more error messages...
r47469 .get(b"share", b"safe-mismatch.source-not-safe")
Simon Sapin
rhg: Parse per-repository configuration...
r47215 {
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 Some(b"abort") | None => HgError::abort(
Simon Sapin
rhg: Align with Python on some more error messages...
r47469 "abort: share source does not support share-safe requirement\n\
(see `hg help config.format.use-share-safe` for more information)",
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 exit_codes::ABORT,
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 ),
Simon Sapin
rhg: Parse per-repository configuration...
r47215 _ => HgError::unsupported("share-safe downgrade"),
}
.into());
Simon Sapin
rhg: add support for share-safe...
r47191 } else if source_is_share_safe && !share_safe {
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 return Err(
Simon Sapin
rhg: Align with Python on some more error messages...
r47469 match config.get(b"share", b"safe-mismatch.source-safe") {
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 Some(b"abort") | None => HgError::abort(
Simon Sapin
rhg: Align with Python on some more error messages...
r47469 "abort: version mismatch: source uses share-safe \
functionality while the current share does not\n\
(see `hg help config.format.use-share-safe` for more information)",
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 exit_codes::ABORT,
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 ),
_ => HgError::unsupported("share-safe upgrade"),
Simon Sapin
rhg: Parse per-repository configuration...
r47215 }
.into(),
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 );
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,
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 dirstate_parents: LazyCell::new(Self::read_dirstate_parents),
Simon Sapin
rhg: Add lazy/cached dirstate data file ID parsing on Repo...
r49248 dirstate_data_file_uuid: LazyCell::new(
Self::read_dirstate_data_file_uuid,
),
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 dirstate_map: LazyCell::new(Self::new_dirstate_map),
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 changelog: LazyCell::new(Changelog::open),
manifestlog: LazyCell::new(Manifestlog::open),
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)
}
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> {
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 Ok(*self.dirstate_parents.get_or_init(self)?)
}
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();
let mut map = if let Some(data_mmap) = self
.hg_vfs()
.mmap_open(docket.data_filename())
.io_not_found_as_none()?
{
Simon Sapin
rust: Update the memmap2 crate to version 0.4.0...
r48795 OwningDirstateMap::new_empty(data_mmap)
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 } else {
OwningDirstateMap::new_empty(Vec::new())
};
Simon Sapin
dirstate: Remove the Rust abstraction DirstateMapMethods...
r48883 let (on_disk, placeholder) = map.get_pair_mut();
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?;
Ok(map)
} else {
let mut map = OwningDirstateMap::new_empty(dirstate_file_contents);
Simon Sapin
dirstate: Remove the Rust abstraction DirstateMapMethods...
r48883 let (on_disk, placeholder) = map.get_pair_mut();
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 let (inner, parents) = DirstateMap::new_v1(on_disk)?;
self.dirstate_parents
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 .set(parents.unwrap_or(DirstateParents::NULL));
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 *placeholder = inner;
Ok(map)
}
}
pub fn dirstate_map(
&self,
) -> Result<Ref<OwningDirstateMap>, DirstateError> {
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 self.dirstate_map.get_or_init(self)
}
pub fn dirstate_map_mut(
&self,
) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
self.dirstate_map.get_mut_or_init(self)
}
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> {
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 self.changelog.get_or_init(self)
}
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 pub fn changelog_mut(&self) -> Result<RefMut<Changelog>, HgError> {
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 self.changelog.get_mut_or_init(self)
}
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 pub fn manifestlog(&self) -> Result<Ref<Manifestlog>, HgError> {
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 self.manifestlog.get_or_init(self)
}
Simon Sapin
rust: Return HgError instead of RevlogError in revlog constructors...
r48777 pub fn manifestlog_mut(&self) -> Result<RefMut<Manifestlog>, HgError> {
Simon Sapin
rust: Keep lazily-initialized Changelog and Manifest log on the Repo object...
r48773 self.manifestlog.get_mut_or_init(self)
}
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"))? {
Ok(entry.state().is_tracked())
} 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()?;
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(())
}
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"
/// later by setting its inner `Option` to `None`.
struct LazyCell<T, E> {
value: RefCell<Option<T>>,
// `Fn`s that don’t capture environment are zero-size, so this box does
// not allocate:
init: Box<dyn Fn(&Repo) -> Result<T, E>>,
}
impl<T, E> LazyCell<T, E> {
fn new(init: impl Fn(&Repo) -> Result<T, E> + 'static) -> Self {
Self {
value: RefCell::new(None),
init: Box::new(init),
}
}
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 fn set(&self, value: T) {
*self.value.borrow_mut() = Some(value)
}
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 fn get_or_init(&self, repo: &Repo) -> Result<Ref<T>, E> {
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.
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 *self.value.borrow_mut() = Some((self.init)(repo)?);
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()))
}
Simon Sapin
rhg: Make Repo::dirstate_parents a LazyCell...
r49247 fn get_mut_or_init(&self, repo: &Repo) -> 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() {
Simon Sapin
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
r48772 *borrowed = Some((self.init)(repo)?);
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 }