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 | ||||