requirements.rs
183 lines
| 6.7 KiB
| application/rls-services+xml
|
RustLexer
Simon Sapin
|
r47172 | use crate::errors::{HgError, HgResultExt}; | ||
Simon Sapin
|
r48764 | use crate::repo::Repo; | ||
Simon Sapin
|
r47358 | use crate::utils::join_display; | ||
Simon Sapin
|
r48764 | use crate::vfs::Vfs; | ||
Simon Sapin
|
r47190 | use std::collections::HashSet; | ||
Simon Sapin
|
r46536 | |||
Simon Sapin
|
r47190 | fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> { | ||
Simon Sapin
|
r46536 | // The Python code reading this file uses `str.splitlines` | ||
// which looks for a number of line separators (even including a couple of | ||||
// non-ASCII ones), but Python code writing it always uses `\n`. | ||||
let lines = bytes.split(|&byte| byte == b'\n'); | ||||
lines | ||||
.filter(|line| !line.is_empty()) | ||||
.map(|line| { | ||||
// Python uses Unicode `str.isalnum` but feature names are all | ||||
// ASCII | ||||
Simon Sapin
|
r46550 | if line[0].is_ascii_alphanumeric() && line.is_ascii() { | ||
Simon Sapin
|
r46536 | Ok(String::from_utf8(line.into()).unwrap()) | ||
} else { | ||||
Simon Sapin
|
r47171 | Err(HgError::corrupted("parse error in 'requires' file")) | ||
Simon Sapin
|
r46536 | } | ||
}) | ||||
.collect() | ||||
} | ||||
Simon Sapin
|
r47191 | pub(crate) fn load(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> { | ||
parse(&hg_vfs.read("requires")?) | ||||
} | ||||
Simon Sapin
|
r47190 | pub(crate) fn load_if_exists(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> { | ||
if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? { | ||||
Simon Sapin
|
r47171 | parse(&bytes) | ||
} else { | ||||
Simon Sapin
|
r46536 | // Treat a missing file the same as an empty file. | ||
// From `mercurial/localrepo.py`: | ||||
// > requires file contains a newline-delimited list of | ||||
// > features/capabilities the opener (us) must have in order to use | ||||
// > the repository. This file was introduced in Mercurial 0.9.2, | ||||
// > which means very old repositories may not have one. We assume | ||||
// > a missing file translates to no requirements. | ||||
Simon Sapin
|
r47190 | Ok(HashSet::new()) | ||
Simon Sapin
|
r46536 | } | ||
} | ||||
Simon Sapin
|
r46549 | |||
Simon Sapin
|
r47190 | pub(crate) fn check(repo: &Repo) -> Result<(), HgError> { | ||
Simon Sapin
|
r47358 | let unknown: Vec<_> = repo | ||
.requirements() | ||||
.iter() | ||||
.map(String::as_str) | ||||
// .filter(|feature| !ALL_SUPPORTED.contains(feature.as_str())) | ||||
.filter(|feature| { | ||||
!REQUIRED.contains(feature) && !SUPPORTED.contains(feature) | ||||
}) | ||||
.collect(); | ||||
if !unknown.is_empty() { | ||||
return Err(HgError::unsupported(format!( | ||||
"repository requires feature unknown to this Mercurial: {}", | ||||
join_display(&unknown, ", ") | ||||
))); | ||||
} | ||||
let missing: Vec<_> = REQUIRED | ||||
.iter() | ||||
.filter(|&&feature| !repo.requirements().contains(feature)) | ||||
.collect(); | ||||
if !missing.is_empty() { | ||||
return Err(HgError::unsupported(format!( | ||||
"repository is missing feature required by this Mercurial: {}", | ||||
join_display(&missing, ", ") | ||||
))); | ||||
Simon Sapin
|
r46549 | } | ||
Ok(()) | ||||
} | ||||
Simon Sapin
|
r47358 | /// rhg does not support repositories that are *missing* any of these features | ||
const REQUIRED: &[&str] = &["revlogv1", "store", "fncache", "dotencode"]; | ||||
/// rhg supports repository with or without these | ||||
Simon Sapin
|
r46549 | const SUPPORTED: &[&str] = &[ | ||
Raphaël Gomès
|
r52084 | GENERALDELTA_REQUIREMENT, | ||
Simon Sapin
|
r47190 | SHARED_REQUIREMENT, | ||
Simon Sapin
|
r47191 | SHARESAFE_REQUIREMENT, | ||
Simon Sapin
|
r47190 | SPARSEREVLOG_REQUIREMENT, | ||
RELATIVE_SHARED_REQUIREMENT, | ||||
r47635 | REVLOG_COMPRESSION_ZSTD, | |||
Simon Sapin
|
r48165 | DIRSTATE_V2_REQUIREMENT, | ||
Arseniy Alekseyev
|
r50395 | DIRSTATE_TRACKED_HINT_V1, | ||
Simon Sapin
|
r46706 | // As of this writing everything rhg does is read-only. | ||
// When it starts writing to the repository, it’ll need to either keep the | ||||
// persistent nodemap up to date or remove this entry: | ||||
Simon Sapin
|
r47358 | NODEMAP_REQUIREMENT, | ||
Arseniy Alekseyev
|
r49238 | // Not all commands support `sparse` and `narrow`. The commands that do | ||
// not should opt out by checking `has_sparse` and `has_narrow`. | ||||
SPARSE_REQUIREMENT, | ||||
NARROW_REQUIREMENT, | ||||
Martin von Zweigbergk
|
r49935 | // rhg doesn't care about bookmarks at all yet | ||
BOOKMARKS_IN_STORE_REQUIREMENT, | ||||
Simon Sapin
|
r46549 | ]; | ||
Simon Sapin
|
r47190 | |||
// Copied from mercurial/requirements.py: | ||||
Raphaël Gomès
|
r50086 | pub const DIRSTATE_V2_REQUIREMENT: &str = "dirstate-v2"; | ||
Raphaël Gomès
|
r52084 | pub const GENERALDELTA_REQUIREMENT: &str = "generaldelta"; | ||
Simon Sapin
|
r48165 | |||
r50089 | /// A repository that uses the tracked hint dirstate file | |||
#[allow(unused)] | ||||
pub const DIRSTATE_TRACKED_HINT_V1: &str = "dirstate-tracked-key-v1"; | ||||
Simon Sapin
|
r47190 | /// When narrowing is finalized and no longer subject to format changes, | ||
/// we should move this to just "narrow" or similar. | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const NARROW_REQUIREMENT: &str = "narrowhg-experimental"; | ||
Simon Sapin
|
r47190 | |||
Martin von Zweigbergk
|
r49935 | /// Bookmarks must be stored in the `store` part of the repository and will be | ||
/// share accross shares | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const BOOKMARKS_IN_STORE_REQUIREMENT: &str = "bookmarksinstore"; | ||
Martin von Zweigbergk
|
r49935 | |||
Simon Sapin
|
r47190 | /// Enables sparse working directory usage | ||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const SPARSE_REQUIREMENT: &str = "exp-sparse"; | ||
Simon Sapin
|
r47190 | |||
/// Enables the internal phase which is used to hide changesets instead | ||||
/// of stripping them | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const INTERNAL_PHASE_REQUIREMENT: &str = "internal-phase"; | ||
Simon Sapin
|
r47190 | |||
/// Stores manifest in Tree structure | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const TREEMANIFEST_REQUIREMENT: &str = "treemanifest"; | ||
Simon Sapin
|
r47190 | |||
Raphaël Gomès
|
r52084 | /// Whether to use the "RevlogNG" or V1 of the revlog format | ||
#[allow(unused)] | ||||
pub const REVLOGV1_REQUIREMENT: &str = "revlogv1"; | ||||
Simon Sapin
|
r47190 | /// Increment the sub-version when the revlog v2 format changes to lock out old | ||
/// clients. | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const REVLOGV2_REQUIREMENT: &str = "exp-revlogv2.1"; | ||
Simon Sapin
|
r47190 | |||
Raphaël Gomès
|
r52084 | /// Increment the sub-version when the revlog v2 format changes to lock out old | ||
/// clients. | ||||
#[allow(unused)] | ||||
pub const CHANGELOGV2_REQUIREMENT: &str = "exp-changelog-v2"; | ||||
Simon Sapin
|
r47190 | /// A repository with the sparserevlog feature will have delta chains that | ||
/// can spread over a larger span. Sparse reading cuts these large spans into | ||||
/// pieces, so that each piece isn't too big. | ||||
/// Without the sparserevlog capability, reading from the repository could use | ||||
/// huge amounts of memory, because the whole span would be read at once, | ||||
/// including all the intermediate revisions that aren't pertinent for the | ||||
/// chain. This is why once a repository has enabled sparse-read, it becomes | ||||
/// required. | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const SPARSEREVLOG_REQUIREMENT: &str = "sparserevlog"; | ||
Simon Sapin
|
r47190 | |||
/// A repository with the the copies-sidedata-changeset requirement will store | ||||
/// copies related information in changeset's sidedata. | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const COPIESSDC_REQUIREMENT: &str = "exp-copies-sidedata-changeset"; | ||
Simon Sapin
|
r47190 | |||
/// The repository use persistent nodemap for the changelog and the manifest. | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const NODEMAP_REQUIREMENT: &str = "persistent-nodemap"; | ||
Simon Sapin
|
r47190 | |||
/// Denotes that the current repository is a share | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const SHARED_REQUIREMENT: &str = "shared"; | ||
Simon Sapin
|
r47190 | |||
/// Denotes that current repository is a share and the shared source path is | ||||
/// relative to the current repository root path | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const RELATIVE_SHARED_REQUIREMENT: &str = "relshared"; | ||
Simon Sapin
|
r47190 | |||
/// A repository with share implemented safely. The repository has different | ||||
/// store and working copy requirements i.e. both `.hg/requires` and | ||||
/// `.hg/store/requires` are present. | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const SHARESAFE_REQUIREMENT: &str = "share-safe"; | ||
r47635 | ||||
/// A repository that use zstd compression inside its revlog | ||||
#[allow(unused)] | ||||
Raphaël Gomès
|
r50086 | pub const REVLOG_COMPRESSION_ZSTD: &str = "revlog-compression-zstd"; | ||