##// END OF EJS Templates
pyoxidizer: add user-site to `sys.path` on Windows...
pyoxidizer: add user-site to `sys.path` on Windows This is a port of 53221078e0de to Windows to allow pip-installed extensions to be loaded without specifying a path. It's a major headache to have an hg.exe on `PATH` that needs to have the path to the extensions specified, because WSL doesn't see the same path. This is only for Windows for now, to match the currently shipping py2 behavior. There is a better solution with using the `site` package, but this needs support in PyOxidizer[1]. [1] https://github.com/indygreg/PyOxidizer/issues/430 Differential Revision: https://phab.mercurial-scm.org/D11308

File last commit:

r48584:cf5f8da2 stable
r48679:95af358f stable
Show More
repo.rs
315 lines | 10.3 KiB | application/rls-services+xml | RustLexer
Simon Sapin
rhg: Parse per-repository configuration...
r47215 use crate::config::{Config, ConfigError, ConfigParseError};
Simon Sapin
rust: Add a log file rotation utility...
r47341 use crate::errors::{HgError, IoErrorContext, IoResultExt};
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 use crate::exit_codes;
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
rhg: Don’t make repository path absolute too early...
r47474 use crate::utils::SliceExt;
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
rhg: Propagate permission errors when finding a repository...
r48584 use std::io::ErrorKind;
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
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 /// Filesystem access abstraction for the contents of a given "base" diretory
#[derive(Clone, Copy)]
Simon Sapin
rust: Add a log file rotation utility...
r47341 pub struct Vfs<'a> {
pub(crate) base: &'a Path,
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 =
get_path_from_bytes(bytes.trim_end_newlines()).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: 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: Add support for dirstate-v2...
r48165 pub fn has_dirstate_v2(&self) -> bool {
self.requirements
.contains(requirements::DIRSTATE_V2_REQUIREMENT)
}
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 pub fn dirstate_parents(
&self,
) -> Result<crate::dirstate::DirstateParents, HgError> {
let dirstate = self.hg_vfs().mmap_open("dirstate")?;
Simon Sapin
rhg: Add support for dirstate-v2...
r48165 if dirstate.is_empty() {
return Ok(crate::dirstate::DirstateParents::NULL);
}
let parents = if self.has_dirstate_v2() {
Simon Sapin
dirstate-v2: Introduce a docket file...
r48474 crate::dirstate_tree::on_disk::read_docket(&dirstate)?.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
dirstate-v2: Introduce a docket file...
r48474 Ok(parents)
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 }
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 }
impl Vfs<'_> {
Simon Sapin
rust: Add a log file rotation utility...
r47341 pub fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
Simon Sapin
rust: Fold find_root and check_requirements into Repo::find...
r47175 self.base.join(relative_path)
}
Simon Sapin
rust: Add a log file rotation utility...
r47341 pub fn read(
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 &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: Add a log file rotation utility...
r47341 std::fs::read(&path).when_reading_file(&path)
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 }
Simon Sapin
rust: Add a log file rotation utility...
r47341 pub fn mmap_open(
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 &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);
Simon Sapin
rust: Add a log file rotation utility...
r47341 let file = std::fs::File::open(&path).when_reading_file(&path)?;
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 // TODO: what are the safety requirements here?
Simon Sapin
rust: Add a log file rotation utility...
r47341 let mmap = unsafe { MmapOptions::new().map(&file) }
.when_reading_file(&path)?;
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 Ok(mmap)
}
Simon Sapin
rust: Add a log file rotation utility...
r47341
pub fn rename(
&self,
relative_from: impl AsRef<Path>,
relative_to: impl AsRef<Path>,
) -> Result<(), HgError> {
let from = self.join(relative_from);
let to = self.join(relative_to);
std::fs::rename(&from, &to)
.with_context(|| IoErrorContext::RenamingFile { from, to })
}
Simon Sapin
rust: introduce Repo and Vfs types for filesystem abstraction...
r46782 }
Simon Sapin
rhg: Propagate permission errors when finding a repository...
r48584
fn fs_metadata(
path: impl AsRef<Path>,
) -> Result<Option<std::fs::Metadata>, HgError> {
let path = path.as_ref();
match std::fs::metadata(path) {
Ok(meta) => Ok(Some(meta)),
Err(error) => match error.kind() {
// TODO: when we require a Rust version where `NotADirectory` is
// stable, invert this logic and return None for it and `NotFound`
// and propagate any other error.
ErrorKind::PermissionDenied => Err(error).with_context(|| {
IoErrorContext::ReadingMetadata(path.to_owned())
}),
_ => Ok(None),
},
}
}
fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> {
Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir()))
}
fn is_file(path: impl AsRef<Path>) -> Result<bool, HgError> {
Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_file()))
}