Show More
@@ -1,161 +1,168 b'' | |||||
1 | use crate::errors::{HgError, HgResultExt}; |
|
1 | use crate::errors::{HgError, HgResultExt}; | |
2 | use crate::repo::Repo; |
|
2 | use crate::repo::Repo; | |
3 | use crate::utils::join_display; |
|
3 | use crate::utils::join_display; | |
4 | use crate::vfs::Vfs; |
|
4 | use crate::vfs::Vfs; | |
5 | use std::collections::HashSet; |
|
5 | use std::collections::HashSet; | |
6 |
|
6 | |||
7 | fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> { |
|
7 | fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> { | |
8 | // The Python code reading this file uses `str.splitlines` |
|
8 | // The Python code reading this file uses `str.splitlines` | |
9 | // which looks for a number of line separators (even including a couple of |
|
9 | // which looks for a number of line separators (even including a couple of | |
10 | // non-ASCII ones), but Python code writing it always uses `\n`. |
|
10 | // non-ASCII ones), but Python code writing it always uses `\n`. | |
11 | let lines = bytes.split(|&byte| byte == b'\n'); |
|
11 | let lines = bytes.split(|&byte| byte == b'\n'); | |
12 |
|
12 | |||
13 | lines |
|
13 | lines | |
14 | .filter(|line| !line.is_empty()) |
|
14 | .filter(|line| !line.is_empty()) | |
15 | .map(|line| { |
|
15 | .map(|line| { | |
16 | // Python uses Unicode `str.isalnum` but feature names are all |
|
16 | // Python uses Unicode `str.isalnum` but feature names are all | |
17 | // ASCII |
|
17 | // ASCII | |
18 | if line[0].is_ascii_alphanumeric() && line.is_ascii() { |
|
18 | if line[0].is_ascii_alphanumeric() && line.is_ascii() { | |
19 | Ok(String::from_utf8(line.into()).unwrap()) |
|
19 | Ok(String::from_utf8(line.into()).unwrap()) | |
20 | } else { |
|
20 | } else { | |
21 | Err(HgError::corrupted("parse error in 'requires' file")) |
|
21 | Err(HgError::corrupted("parse error in 'requires' file")) | |
22 | } |
|
22 | } | |
23 | }) |
|
23 | }) | |
24 | .collect() |
|
24 | .collect() | |
25 | } |
|
25 | } | |
26 |
|
26 | |||
27 | pub(crate) fn load(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> { |
|
27 | pub(crate) fn load(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> { | |
28 | parse(&hg_vfs.read("requires")?) |
|
28 | parse(&hg_vfs.read("requires")?) | |
29 | } |
|
29 | } | |
30 |
|
30 | |||
31 | pub(crate) fn load_if_exists(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> { |
|
31 | pub(crate) fn load_if_exists(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> { | |
32 | if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? { |
|
32 | if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? { | |
33 | parse(&bytes) |
|
33 | parse(&bytes) | |
34 | } else { |
|
34 | } else { | |
35 | // Treat a missing file the same as an empty file. |
|
35 | // Treat a missing file the same as an empty file. | |
36 | // From `mercurial/localrepo.py`: |
|
36 | // From `mercurial/localrepo.py`: | |
37 | // > requires file contains a newline-delimited list of |
|
37 | // > requires file contains a newline-delimited list of | |
38 | // > features/capabilities the opener (us) must have in order to use |
|
38 | // > features/capabilities the opener (us) must have in order to use | |
39 | // > the repository. This file was introduced in Mercurial 0.9.2, |
|
39 | // > the repository. This file was introduced in Mercurial 0.9.2, | |
40 | // > which means very old repositories may not have one. We assume |
|
40 | // > which means very old repositories may not have one. We assume | |
41 | // > a missing file translates to no requirements. |
|
41 | // > a missing file translates to no requirements. | |
42 | Ok(HashSet::new()) |
|
42 | Ok(HashSet::new()) | |
43 | } |
|
43 | } | |
44 | } |
|
44 | } | |
45 |
|
45 | |||
46 | pub(crate) fn check(repo: &Repo) -> Result<(), HgError> { |
|
46 | pub(crate) fn check(repo: &Repo) -> Result<(), HgError> { | |
47 | let unknown: Vec<_> = repo |
|
47 | let unknown: Vec<_> = repo | |
48 | .requirements() |
|
48 | .requirements() | |
49 | .iter() |
|
49 | .iter() | |
50 | .map(String::as_str) |
|
50 | .map(String::as_str) | |
51 | // .filter(|feature| !ALL_SUPPORTED.contains(feature.as_str())) |
|
51 | // .filter(|feature| !ALL_SUPPORTED.contains(feature.as_str())) | |
52 | .filter(|feature| { |
|
52 | .filter(|feature| { | |
53 | !REQUIRED.contains(feature) && !SUPPORTED.contains(feature) |
|
53 | !REQUIRED.contains(feature) && !SUPPORTED.contains(feature) | |
54 | }) |
|
54 | }) | |
55 | .collect(); |
|
55 | .collect(); | |
56 | if !unknown.is_empty() { |
|
56 | if !unknown.is_empty() { | |
57 | return Err(HgError::unsupported(format!( |
|
57 | return Err(HgError::unsupported(format!( | |
58 | "repository requires feature unknown to this Mercurial: {}", |
|
58 | "repository requires feature unknown to this Mercurial: {}", | |
59 | join_display(&unknown, ", ") |
|
59 | join_display(&unknown, ", ") | |
60 | ))); |
|
60 | ))); | |
61 | } |
|
61 | } | |
62 | let missing: Vec<_> = REQUIRED |
|
62 | let missing: Vec<_> = REQUIRED | |
63 | .iter() |
|
63 | .iter() | |
64 | .filter(|&&feature| !repo.requirements().contains(feature)) |
|
64 | .filter(|&&feature| !repo.requirements().contains(feature)) | |
65 | .collect(); |
|
65 | .collect(); | |
66 | if !missing.is_empty() { |
|
66 | if !missing.is_empty() { | |
67 | return Err(HgError::unsupported(format!( |
|
67 | return Err(HgError::unsupported(format!( | |
68 | "repository is missing feature required by this Mercurial: {}", |
|
68 | "repository is missing feature required by this Mercurial: {}", | |
69 | join_display(&missing, ", ") |
|
69 | join_display(&missing, ", ") | |
70 | ))); |
|
70 | ))); | |
71 | } |
|
71 | } | |
72 | Ok(()) |
|
72 | Ok(()) | |
73 | } |
|
73 | } | |
74 |
|
74 | |||
75 | /// rhg does not support repositories that are *missing* any of these features |
|
75 | /// rhg does not support repositories that are *missing* any of these features | |
76 | const REQUIRED: &[&str] = &["revlogv1", "store", "fncache", "dotencode"]; |
|
76 | const REQUIRED: &[&str] = &["revlogv1", "store", "fncache", "dotencode"]; | |
77 |
|
77 | |||
78 | /// rhg supports repository with or without these |
|
78 | /// rhg supports repository with or without these | |
79 | const SUPPORTED: &[&str] = &[ |
|
79 | const SUPPORTED: &[&str] = &[ | |
80 | "generaldelta", |
|
80 | "generaldelta", | |
81 | SHARED_REQUIREMENT, |
|
81 | SHARED_REQUIREMENT, | |
82 | SHARESAFE_REQUIREMENT, |
|
82 | SHARESAFE_REQUIREMENT, | |
83 | SPARSEREVLOG_REQUIREMENT, |
|
83 | SPARSEREVLOG_REQUIREMENT, | |
84 | RELATIVE_SHARED_REQUIREMENT, |
|
84 | RELATIVE_SHARED_REQUIREMENT, | |
85 | REVLOG_COMPRESSION_ZSTD, |
|
85 | REVLOG_COMPRESSION_ZSTD, | |
86 | DIRSTATE_V2_REQUIREMENT, |
|
86 | DIRSTATE_V2_REQUIREMENT, | |
87 | // As of this writing everything rhg does is read-only. |
|
87 | // As of this writing everything rhg does is read-only. | |
88 | // When it starts writing to the repository, itβll need to either keep the |
|
88 | // When it starts writing to the repository, itβll need to either keep the | |
89 | // persistent nodemap up to date or remove this entry: |
|
89 | // persistent nodemap up to date or remove this entry: | |
90 | NODEMAP_REQUIREMENT, |
|
90 | NODEMAP_REQUIREMENT, | |
91 | // Not all commands support `sparse` and `narrow`. The commands that do |
|
91 | // Not all commands support `sparse` and `narrow`. The commands that do | |
92 | // not should opt out by checking `has_sparse` and `has_narrow`. |
|
92 | // not should opt out by checking `has_sparse` and `has_narrow`. | |
93 | SPARSE_REQUIREMENT, |
|
93 | SPARSE_REQUIREMENT, | |
94 | NARROW_REQUIREMENT, |
|
94 | NARROW_REQUIREMENT, | |
|
95 | // rhg doesn't care about bookmarks at all yet | |||
|
96 | BOOKMARKS_IN_STORE_REQUIREMENT, | |||
95 | ]; |
|
97 | ]; | |
96 |
|
98 | |||
97 | // Copied from mercurial/requirements.py: |
|
99 | // Copied from mercurial/requirements.py: | |
98 |
|
100 | |||
99 | pub(crate) const DIRSTATE_V2_REQUIREMENT: &str = "dirstate-v2"; |
|
101 | pub(crate) const DIRSTATE_V2_REQUIREMENT: &str = "dirstate-v2"; | |
100 |
|
102 | |||
101 | /// When narrowing is finalized and no longer subject to format changes, |
|
103 | /// When narrowing is finalized and no longer subject to format changes, | |
102 | /// we should move this to just "narrow" or similar. |
|
104 | /// we should move this to just "narrow" or similar. | |
103 | #[allow(unused)] |
|
105 | #[allow(unused)] | |
104 | pub(crate) const NARROW_REQUIREMENT: &str = "narrowhg-experimental"; |
|
106 | pub(crate) const NARROW_REQUIREMENT: &str = "narrowhg-experimental"; | |
105 |
|
107 | |||
|
108 | /// Bookmarks must be stored in the `store` part of the repository and will be | |||
|
109 | /// share accross shares | |||
|
110 | #[allow(unused)] | |||
|
111 | pub(crate) const BOOKMARKS_IN_STORE_REQUIREMENT: &str = "bookmarksinstore"; | |||
|
112 | ||||
106 | /// Enables sparse working directory usage |
|
113 | /// Enables sparse working directory usage | |
107 | #[allow(unused)] |
|
114 | #[allow(unused)] | |
108 | pub(crate) const SPARSE_REQUIREMENT: &str = "exp-sparse"; |
|
115 | pub(crate) const SPARSE_REQUIREMENT: &str = "exp-sparse"; | |
109 |
|
116 | |||
110 | /// Enables the internal phase which is used to hide changesets instead |
|
117 | /// Enables the internal phase which is used to hide changesets instead | |
111 | /// of stripping them |
|
118 | /// of stripping them | |
112 | #[allow(unused)] |
|
119 | #[allow(unused)] | |
113 | pub(crate) const INTERNAL_PHASE_REQUIREMENT: &str = "internal-phase"; |
|
120 | pub(crate) const INTERNAL_PHASE_REQUIREMENT: &str = "internal-phase"; | |
114 |
|
121 | |||
115 | /// Stores manifest in Tree structure |
|
122 | /// Stores manifest in Tree structure | |
116 | #[allow(unused)] |
|
123 | #[allow(unused)] | |
117 | pub(crate) const TREEMANIFEST_REQUIREMENT: &str = "treemanifest"; |
|
124 | pub(crate) const TREEMANIFEST_REQUIREMENT: &str = "treemanifest"; | |
118 |
|
125 | |||
119 | /// Increment the sub-version when the revlog v2 format changes to lock out old |
|
126 | /// Increment the sub-version when the revlog v2 format changes to lock out old | |
120 | /// clients. |
|
127 | /// clients. | |
121 | #[allow(unused)] |
|
128 | #[allow(unused)] | |
122 | pub(crate) const REVLOGV2_REQUIREMENT: &str = "exp-revlogv2.1"; |
|
129 | pub(crate) const REVLOGV2_REQUIREMENT: &str = "exp-revlogv2.1"; | |
123 |
|
130 | |||
124 | /// A repository with the sparserevlog feature will have delta chains that |
|
131 | /// A repository with the sparserevlog feature will have delta chains that | |
125 | /// can spread over a larger span. Sparse reading cuts these large spans into |
|
132 | /// can spread over a larger span. Sparse reading cuts these large spans into | |
126 | /// pieces, so that each piece isn't too big. |
|
133 | /// pieces, so that each piece isn't too big. | |
127 | /// Without the sparserevlog capability, reading from the repository could use |
|
134 | /// Without the sparserevlog capability, reading from the repository could use | |
128 | /// huge amounts of memory, because the whole span would be read at once, |
|
135 | /// huge amounts of memory, because the whole span would be read at once, | |
129 | /// including all the intermediate revisions that aren't pertinent for the |
|
136 | /// including all the intermediate revisions that aren't pertinent for the | |
130 | /// chain. This is why once a repository has enabled sparse-read, it becomes |
|
137 | /// chain. This is why once a repository has enabled sparse-read, it becomes | |
131 | /// required. |
|
138 | /// required. | |
132 | #[allow(unused)] |
|
139 | #[allow(unused)] | |
133 | pub(crate) const SPARSEREVLOG_REQUIREMENT: &str = "sparserevlog"; |
|
140 | pub(crate) const SPARSEREVLOG_REQUIREMENT: &str = "sparserevlog"; | |
134 |
|
141 | |||
135 | /// A repository with the the copies-sidedata-changeset requirement will store |
|
142 | /// A repository with the the copies-sidedata-changeset requirement will store | |
136 | /// copies related information in changeset's sidedata. |
|
143 | /// copies related information in changeset's sidedata. | |
137 | #[allow(unused)] |
|
144 | #[allow(unused)] | |
138 | pub(crate) const COPIESSDC_REQUIREMENT: &str = "exp-copies-sidedata-changeset"; |
|
145 | pub(crate) const COPIESSDC_REQUIREMENT: &str = "exp-copies-sidedata-changeset"; | |
139 |
|
146 | |||
140 | /// The repository use persistent nodemap for the changelog and the manifest. |
|
147 | /// The repository use persistent nodemap for the changelog and the manifest. | |
141 | #[allow(unused)] |
|
148 | #[allow(unused)] | |
142 | pub(crate) const NODEMAP_REQUIREMENT: &str = "persistent-nodemap"; |
|
149 | pub(crate) const NODEMAP_REQUIREMENT: &str = "persistent-nodemap"; | |
143 |
|
150 | |||
144 | /// Denotes that the current repository is a share |
|
151 | /// Denotes that the current repository is a share | |
145 | #[allow(unused)] |
|
152 | #[allow(unused)] | |
146 | pub(crate) const SHARED_REQUIREMENT: &str = "shared"; |
|
153 | pub(crate) const SHARED_REQUIREMENT: &str = "shared"; | |
147 |
|
154 | |||
148 | /// Denotes that current repository is a share and the shared source path is |
|
155 | /// Denotes that current repository is a share and the shared source path is | |
149 | /// relative to the current repository root path |
|
156 | /// relative to the current repository root path | |
150 | #[allow(unused)] |
|
157 | #[allow(unused)] | |
151 | pub(crate) const RELATIVE_SHARED_REQUIREMENT: &str = "relshared"; |
|
158 | pub(crate) const RELATIVE_SHARED_REQUIREMENT: &str = "relshared"; | |
152 |
|
159 | |||
153 | /// A repository with share implemented safely. The repository has different |
|
160 | /// A repository with share implemented safely. The repository has different | |
154 | /// store and working copy requirements i.e. both `.hg/requires` and |
|
161 | /// store and working copy requirements i.e. both `.hg/requires` and | |
155 | /// `.hg/store/requires` are present. |
|
162 | /// `.hg/store/requires` are present. | |
156 | #[allow(unused)] |
|
163 | #[allow(unused)] | |
157 | pub(crate) const SHARESAFE_REQUIREMENT: &str = "share-safe"; |
|
164 | pub(crate) const SHARESAFE_REQUIREMENT: &str = "share-safe"; | |
158 |
|
165 | |||
159 | /// A repository that use zstd compression inside its revlog |
|
166 | /// A repository that use zstd compression inside its revlog | |
160 | #[allow(unused)] |
|
167 | #[allow(unused)] | |
161 | pub(crate) const REVLOG_COMPRESSION_ZSTD: &str = "revlog-compression-zstd"; |
|
168 | pub(crate) const REVLOG_COMPRESSION_ZSTD: &str = "revlog-compression-zstd"; |
General Comments 0
You need to be logged in to leave comments.
Login now