Details
Details
- Reviewers
- None
- Group Reviewers
hg-reviewers - Commits
- rHG95b276283b67: rhg: add support for share-safe
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Branch
- default
- Lint
No Linters Available - Unit
No Unit Test Coverage
( )
hg-reviewers |
No Linters Available |
No Unit Test Coverage |
Path | Packages | |||
---|---|---|---|---|
M | rust/hg-core/src/repo.rs (32 lines) | |||
M | rust/hg-core/src/requirements.rs (7 lines) | |||
M | tests/test-rhg.t (4 lines) |
Commit | Parents | Author | Summary | Date |
---|---|---|---|---|
ad97bbd08351 | 228b75c6a619 | Simon Sapin | Feb 1 2021, 5:41 AM |
Status | Author | Revision | |
---|---|---|---|
Closed | SimonSapin | ||
Closed | SimonSapin | ||
Closed | SimonSapin |
Err(RepoFindError::NotFoundInCurrentDirectoryOrAncestors { | Err(RepoFindError::NotFoundInCurrentDirectoryOrAncestors { | ||||
current_directory, | current_directory, | ||||
}) | }) | ||||
} | } | ||||
/// To be called after checking that `.hg` is a sub-directory | /// To be called after checking that `.hg` is a sub-directory | ||||
fn new_at_path(working_directory: PathBuf) -> Result<Self, HgError> { | fn new_at_path(working_directory: PathBuf) -> Result<Self, HgError> { | ||||
let dot_hg = working_directory.join(".hg"); | let dot_hg = working_directory.join(".hg"); | ||||
let hg_vfs = Vfs { base: &dot_hg }; | let hg_vfs = Vfs { base: &dot_hg }; | ||||
let reqs = requirements::load_if_exists(hg_vfs)?; | let mut reqs = requirements::load_if_exists(hg_vfs)?; | ||||
let relative = | let relative = | ||||
reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT); | reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT); | ||||
let shared = | let shared = | ||||
reqs.contains(requirements::SHARED_REQUIREMENT) || relative; | reqs.contains(requirements::SHARED_REQUIREMENT) || relative; | ||||
// 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); | |||||
let store_path; | let store_path; | ||||
if !shared { | if !shared { | ||||
store_path = dot_hg.join("store"); | store_path = dot_hg.join("store"); | ||||
if share_safe { | |||||
reqs.extend(requirements::load(Vfs { base: &store_path })?); | |||||
} | |||||
} else { | } else { | ||||
let bytes = hg_vfs.read("sharedpath")?; | let bytes = hg_vfs.read("sharedpath")?; | ||||
let mut shared_path = get_path_from_bytes(&bytes).to_owned(); | let mut shared_path = get_path_from_bytes(&bytes).to_owned(); | ||||
if relative { | if relative { | ||||
shared_path = dot_hg.join(shared_path) | shared_path = dot_hg.join(shared_path) | ||||
} | } | ||||
if !shared_path.is_dir() { | if !shared_path.is_dir() { | ||||
return Err(HgError::corrupted(format!( | return Err(HgError::corrupted(format!( | ||||
".hg/sharedpath points to nonexistent directory {}", | ".hg/sharedpath points to nonexistent directory {}", | ||||
shared_path.display() | shared_path.display() | ||||
))); | ))); | ||||
} | } | ||||
store_path = shared_path.join("store"); | store_path = shared_path.join("store"); | ||||
let source_is_share_safe = | |||||
requirements::load(Vfs { base: &shared_path })? | |||||
.contains(requirements::SHARESAFE_REQUIREMENT); | |||||
// TODO: support for `share.safe-mismatch.*` config | |||||
if share_safe && !source_is_share_safe { | |||||
return Err(HgError::unsupported("share-safe downgrade")); | |||||
} else if source_is_share_safe && !share_safe { | |||||
return Err(HgError::unsupported("share-safe upgrade")); | |||||
} | |||||
} | } | ||||
let repo = Self { | let repo = Self { | ||||
requirements: reqs, | requirements: reqs, | ||||
working_directory, | working_directory, | ||||
store: store_path, | store: store_path, | ||||
dot_hg, | dot_hg, | ||||
}; | }; |
Ok(String::from_utf8(line.into()).unwrap()) | Ok(String::from_utf8(line.into()).unwrap()) | ||||
} else { | } else { | ||||
Err(HgError::corrupted("parse error in 'requires' file")) | Err(HgError::corrupted("parse error in 'requires' file")) | ||||
} | } | ||||
}) | }) | ||||
.collect() | .collect() | ||||
} | } | ||||
pub(crate) fn load(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> { | |||||
parse(&hg_vfs.read("requires")?) | |||||
} | |||||
pub(crate) fn load_if_exists(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> { | 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()? { | if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? { | ||||
parse(&bytes) | parse(&bytes) | ||||
} else { | } else { | ||||
// Treat a missing file the same as an empty file. | // Treat a missing file the same as an empty file. | ||||
// From `mercurial/localrepo.py`: | // From `mercurial/localrepo.py`: | ||||
// > requires file contains a newline-delimited list of | // > requires file contains a newline-delimited list of | ||||
// > features/capabilities the opener (us) must have in order to use | // > features/capabilities the opener (us) must have in order to use | ||||
// TODO: set this to actually-supported features | // TODO: set this to actually-supported features | ||||
const SUPPORTED: &[&str] = &[ | const SUPPORTED: &[&str] = &[ | ||||
"dotencode", | "dotencode", | ||||
"fncache", | "fncache", | ||||
"generaldelta", | "generaldelta", | ||||
"revlogv1", | "revlogv1", | ||||
SHARED_REQUIREMENT, | SHARED_REQUIREMENT, | ||||
SHARESAFE_REQUIREMENT, | |||||
SPARSEREVLOG_REQUIREMENT, | SPARSEREVLOG_REQUIREMENT, | ||||
RELATIVE_SHARED_REQUIREMENT, | RELATIVE_SHARED_REQUIREMENT, | ||||
"store", | "store", | ||||
// As of this writing everything rhg does is read-only. | // As of this writing everything rhg does is read-only. | ||||
// When it starts writing to the repository, it’ll need to either keep the | // When it starts writing to the repository, it’ll need to either keep the | ||||
// persistent nodemap up to date or remove this entry: | // persistent nodemap up to date or remove this entry: | ||||
"persistent-nodemap", | "persistent-nodemap", | ||||
]; | ]; | ||||
/// relative to the current repository root path | /// relative to the current repository root path | ||||
#[allow(unused)] | #[allow(unused)] | ||||
pub(crate) const RELATIVE_SHARED_REQUIREMENT: &str = "relshared"; | pub(crate) const RELATIVE_SHARED_REQUIREMENT: &str = "relshared"; | ||||
/// A repository with share implemented safely. The repository has different | /// A repository with share implemented safely. The repository has different | ||||
/// store and working copy requirements i.e. both `.hg/requires` and | /// store and working copy requirements i.e. both `.hg/requires` and | ||||
/// `.hg/store/requires` are present. | /// `.hg/store/requires` are present. | ||||
#[allow(unused)] | #[allow(unused)] | ||||
pub(crate) const SHARESAFE_REQUIREMENT: &str = "exp-sharesafe"; | pub(crate) const SHARESAFE_REQUIREMENT: &str = "share-safe"; |
$ hg share repo4 repo5 | $ hg share repo4 repo5 | ||||
updating working directory | updating working directory | ||||
1 files updated, 0 files merged, 0 files removed, 0 files unresolved | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
And check that basic rhg commands work with sharing | And check that basic rhg commands work with sharing | ||||
$ cd repo5 | $ cd repo5 | ||||
$ rhg files | $ rhg files | ||||
[252] | a | ||||
$ rhg cat -r 0 a | $ rhg cat -r 0 a | ||||
[252] | a | ||||