##// END OF EJS Templates
rhg: replace command structs with functions...
rhg: replace command structs with functions The `Command` trait was not used in any generic context, and the struct where nothing more than holders for values parsed from CLI arguments to be available to a `run` method. Differential Revision: https://phab.mercurial-scm.org/D9967

File last commit:

r47215:d7685105 default
r47228:b1921234 default
Show More
repo.rs
216 lines | 7.0 KiB | application/rls-services+xml | RustLexer
Simon Sapin
rhg: Parse per-repository configuration...
r47215 use crate::config::{Config, ConfigError, ConfigParseError};
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 use crate::errors::{HgError, IoResultExt};
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 use crate::requirements;
Simon Sapin
rhg: initial support for shared repositories...
r47190 use crate::utils::files::get_path_from_bytes;
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 use memmap::{Mmap, MmapOptions};
Simon Sapin
rhg: initial support for shared repositories...
r47190 use std::collections::HashSet;
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
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
rust: Fold find_root and check_requirements into Repo::find...
r47175 current_directory: PathBuf,
},
#[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 /// Filesystem access abstraction for the contents of a given "base" diretory
#[derive(Clone, Copy)]
pub(crate) struct Vfs<'a> {
base: &'a Path,
}
impl Repo {
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 /// Search the current directory and its ancestores for a repository:
/// a working directory that contains a `.hg` sub-directory.
Simon Sapin
rhg: Parse per-repository configuration...
r47215 pub fn find(config: &Config) -> Result<Self, RepoError> {
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 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: initial support for shared repositories...
r47190 if ancestor.join(".hg").is_dir() {
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 return Ok(Self::new_at_path(ancestor.to_owned(), config)?);
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 }
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 }
Simon Sapin
rhg: Parse per-repository configuration...
r47215 Err(RepoError::NotFound { current_directory })
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");
Simon Sapin
rhg: add support for share-safe...
r47191 if share_safe {
reqs.extend(requirements::load(Vfs { base: &store_path })?);
}
Simon Sapin
rhg: initial support for shared repositories...
r47190 } else {
let bytes = hg_vfs.read("sharedpath")?;
let mut shared_path = get_path_from_bytes(&bytes).to_owned();
if relative {
shared_path = dot_hg.join(shared_path)
}
if !shared_path.is_dir() {
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
.get(b"safe-mismatch", b"source-not-safe")
{
Simon Sapin
rhg: Abort based on config on share-safe mismatch...
r47214 Some(b"abort") | None => HgError::abort(
Simon Sapin
rhg: Parse per-repository configuration...
r47215 "share source does not support share-safe requirement",
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(
match config.get(b"safe-mismatch", b"source-safe") {
Some(b"abort") | None => HgError::abort(
"version mismatch: source uses share-safe \
functionality while the current share does not",
),
_ => 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: Parse per-repository configuration...
r47215 let repo_config = config.combine_with_repo(&repo_config_files)?;
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: 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`).
pub(crate) fn hg_vfs(&self) -> Vfs<'_> {
Vfs { base: &self.dot_hg }
}
/// For accessing repository store files (in `.hg/store`)
pub(crate) fn store_vfs(&self) -> Vfs<'_> {
Vfs { base: &self.store }
}
/// For accessing the working copy
// The undescore prefix silences the "never used" warning. Remove before
// using.
pub(crate) fn _working_directory_vfs(&self) -> Vfs<'_> {
Vfs {
base: &self.working_directory,
}
}
}
impl Vfs<'_> {
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 pub(crate) fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
self.base.join(relative_path)
}
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 pub(crate) fn read(
&self,
relative_path: impl AsRef<Path>,
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 ) -> Result<Vec<u8>, HgError> {
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 let path = self.join(relative_path);
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 std::fs::read(&path).for_file(&path)
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 }
pub(crate) fn mmap_open(
&self,
relative_path: impl AsRef<Path>,
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 ) -> Result<Mmap, HgError> {
let path = self.base.join(relative_path);
let file = std::fs::File::open(&path).for_file(&path)?;
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 // TODO: what are the safety requirements here?
Simon Sapin
rust: use HgError in RevlogError and Vfs...
r47172 let mmap = unsafe { MmapOptions::new().map(&file) }.for_file(&path)?;
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 Ok(mmap)
}
}