##// END OF EJS Templates
rhg: Align with Python on some revset parsing corner cases...
Simon Sapin -
r48776:8c29af0f default
parent child Browse files
Show More
@@ -1,396 +1,401 b''
1 1 use std::borrow::Cow;
2 2 use std::io::Read;
3 3 use std::ops::Deref;
4 4 use std::path::Path;
5 5
6 6 use byteorder::{BigEndian, ByteOrder};
7 7 use flate2::read::ZlibDecoder;
8 8 use micro_timer::timed;
9 9 use sha1::{Digest, Sha1};
10 10 use zstd;
11 11
12 12 use super::index::Index;
13 13 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
14 14 use super::nodemap;
15 15 use super::nodemap::{NodeMap, NodeMapError};
16 16 use super::nodemap_docket::NodeMapDocket;
17 17 use super::patch;
18 18 use crate::errors::HgError;
19 19 use crate::repo::Repo;
20 20 use crate::revlog::Revision;
21 use crate::NULL_REVISION;
21 22
22 23 #[derive(derive_more::From)]
23 24 pub enum RevlogError {
24 25 InvalidRevision,
25 26 /// Working directory is not supported
26 27 WDirUnsupported,
27 28 /// Found more than one entry whose ID match the requested prefix
28 29 AmbiguousPrefix,
29 30 #[from]
30 31 Other(HgError),
31 32 }
32 33
33 34 impl From<NodeMapError> for RevlogError {
34 35 fn from(error: NodeMapError) -> Self {
35 36 match error {
36 37 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
37 38 NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(),
38 39 }
39 40 }
40 41 }
41 42
42 43 impl RevlogError {
43 44 fn corrupted() -> Self {
44 45 RevlogError::Other(HgError::corrupted("corrupted revlog"))
45 46 }
46 47 }
47 48
48 49 /// Read only implementation of revlog.
49 50 pub struct Revlog {
50 51 /// When index and data are not interleaved: bytes of the revlog index.
51 52 /// When index and data are interleaved: bytes of the revlog index and
52 53 /// data.
53 54 pub(crate) index: Index,
54 55 /// When index and data are not interleaved: bytes of the revlog data
55 56 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
56 57 /// When present on disk: the persistent nodemap for this revlog
57 58 nodemap: Option<nodemap::NodeTree>,
58 59 }
59 60
60 61 impl Revlog {
61 62 /// Open a revlog index file.
62 63 ///
63 64 /// It will also open the associated data file if index and data are not
64 65 /// interleaved.
65 66 #[timed]
66 67 pub fn open(
67 68 repo: &Repo,
68 69 index_path: impl AsRef<Path>,
69 70 data_path: Option<&Path>,
70 71 ) -> Result<Self, RevlogError> {
71 72 let index_path = index_path.as_ref();
72 73 let index_mmap = repo.store_vfs().mmap_open(&index_path)?;
73 74
74 75 let version = get_version(&index_mmap);
75 76 if version != 1 {
76 77 // A proper new version should have had a repo/store requirement.
77 78 return Err(RevlogError::corrupted());
78 79 }
79 80
80 81 let index = Index::new(Box::new(index_mmap))?;
81 82
82 83 let default_data_path = index_path.with_extension("d");
83 84
84 85 // type annotation required
85 86 // won't recognize Mmap as Deref<Target = [u8]>
86 87 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
87 88 if index.is_inline() {
88 89 None
89 90 } else {
90 91 let data_path = data_path.unwrap_or(&default_data_path);
91 92 let data_mmap = repo.store_vfs().mmap_open(data_path)?;
92 93 Some(Box::new(data_mmap))
93 94 };
94 95
95 96 let nodemap = NodeMapDocket::read_from_file(repo, index_path)?.map(
96 97 |(docket, data)| {
97 98 nodemap::NodeTree::load_bytes(
98 99 Box::new(data),
99 100 docket.data_length,
100 101 )
101 102 },
102 103 );
103 104
104 105 Ok(Revlog {
105 106 index,
106 107 data_bytes,
107 108 nodemap,
108 109 })
109 110 }
110 111
111 112 /// Return number of entries of the `Revlog`.
112 113 pub fn len(&self) -> usize {
113 114 self.index.len()
114 115 }
115 116
116 117 /// Returns `true` if the `Revlog` has zero `entries`.
117 118 pub fn is_empty(&self) -> bool {
118 119 self.index.is_empty()
119 120 }
120 121
121 122 /// Return the full data associated to a node.
122 123 #[timed]
123 124 pub fn get_node_rev(
124 125 &self,
125 126 node: NodePrefix,
126 127 ) -> Result<Revision, RevlogError> {
128 if node.is_prefix_of(&NULL_NODE) {
129 return Ok(NULL_REVISION);
130 }
131
127 132 if let Some(nodemap) = &self.nodemap {
128 133 return nodemap
129 134 .find_bin(&self.index, node)?
130 135 .ok_or(RevlogError::InvalidRevision);
131 136 }
132 137
133 138 // Fallback to linear scan when a persistent nodemap is not present.
134 139 // This happens when the persistent-nodemap experimental feature is not
135 140 // enabled, or for small revlogs.
136 141 //
137 142 // TODO: consider building a non-persistent nodemap in memory to
138 143 // optimize these cases.
139 144 let mut found_by_prefix = None;
140 145 for rev in (0..self.len() as Revision).rev() {
141 146 let index_entry =
142 147 self.index.get_entry(rev).ok_or(HgError::corrupted(
143 148 "revlog references a revision not in the index",
144 149 ))?;
145 150 if node == *index_entry.hash() {
146 151 return Ok(rev);
147 152 }
148 153 if node.is_prefix_of(index_entry.hash()) {
149 154 if found_by_prefix.is_some() {
150 155 return Err(RevlogError::AmbiguousPrefix);
151 156 }
152 157 found_by_prefix = Some(rev)
153 158 }
154 159 }
155 160 found_by_prefix.ok_or(RevlogError::InvalidRevision)
156 161 }
157 162
158 163 /// Returns whether the given revision exists in this revlog.
159 164 pub fn has_rev(&self, rev: Revision) -> bool {
160 165 self.index.get_entry(rev).is_some()
161 166 }
162 167
163 168 /// Return the full data associated to a revision.
164 169 ///
165 170 /// All entries required to build the final data out of deltas will be
166 171 /// retrieved as needed, and the deltas will be applied to the inital
167 172 /// snapshot to rebuild the final data.
168 173 #[timed]
169 174 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
170 175 // Todo return -> Cow
171 176 let mut entry = self.get_entry(rev)?;
172 177 let mut delta_chain = vec![];
173 178 while let Some(base_rev) = entry.base_rev {
174 179 delta_chain.push(entry);
175 180 entry = self
176 181 .get_entry(base_rev)
177 182 .map_err(|_| RevlogError::corrupted())?;
178 183 }
179 184
180 185 // TODO do not look twice in the index
181 186 let index_entry = self
182 187 .index
183 188 .get_entry(rev)
184 189 .ok_or(RevlogError::InvalidRevision)?;
185 190
186 191 let data: Vec<u8> = if delta_chain.is_empty() {
187 192 entry.data()?.into()
188 193 } else {
189 194 Revlog::build_data_from_deltas(entry, &delta_chain)?
190 195 };
191 196
192 197 if self.check_hash(
193 198 index_entry.p1(),
194 199 index_entry.p2(),
195 200 index_entry.hash().as_bytes(),
196 201 &data,
197 202 ) {
198 203 Ok(data)
199 204 } else {
200 205 Err(RevlogError::corrupted())
201 206 }
202 207 }
203 208
204 209 /// Check the hash of some given data against the recorded hash.
205 210 pub fn check_hash(
206 211 &self,
207 212 p1: Revision,
208 213 p2: Revision,
209 214 expected: &[u8],
210 215 data: &[u8],
211 216 ) -> bool {
212 217 let e1 = self.index.get_entry(p1);
213 218 let h1 = match e1 {
214 219 Some(ref entry) => entry.hash(),
215 220 None => &NULL_NODE,
216 221 };
217 222 let e2 = self.index.get_entry(p2);
218 223 let h2 = match e2 {
219 224 Some(ref entry) => entry.hash(),
220 225 None => &NULL_NODE,
221 226 };
222 227
223 228 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
224 229 }
225 230
226 231 /// Build the full data of a revision out its snapshot
227 232 /// and its deltas.
228 233 #[timed]
229 234 fn build_data_from_deltas(
230 235 snapshot: RevlogEntry,
231 236 deltas: &[RevlogEntry],
232 237 ) -> Result<Vec<u8>, RevlogError> {
233 238 let snapshot = snapshot.data()?;
234 239 let deltas = deltas
235 240 .iter()
236 241 .rev()
237 242 .map(RevlogEntry::data)
238 243 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
239 244 let patches: Vec<_> =
240 245 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
241 246 let patch = patch::fold_patch_lists(&patches);
242 247 Ok(patch.apply(&snapshot))
243 248 }
244 249
245 250 /// Return the revlog data.
246 251 fn data(&self) -> &[u8] {
247 252 match self.data_bytes {
248 253 Some(ref data_bytes) => &data_bytes,
249 254 None => panic!(
250 255 "forgot to load the data or trying to access inline data"
251 256 ),
252 257 }
253 258 }
254 259
255 260 /// Get an entry of the revlog.
256 261 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
257 262 let index_entry = self
258 263 .index
259 264 .get_entry(rev)
260 265 .ok_or(RevlogError::InvalidRevision)?;
261 266 let start = index_entry.offset();
262 267 let end = start + index_entry.compressed_len();
263 268 let data = if self.index.is_inline() {
264 269 self.index.data(start, end)
265 270 } else {
266 271 &self.data()[start..end]
267 272 };
268 273 let entry = RevlogEntry {
269 274 rev,
270 275 bytes: data,
271 276 compressed_len: index_entry.compressed_len(),
272 277 uncompressed_len: index_entry.uncompressed_len(),
273 278 base_rev: if index_entry.base_revision() == rev {
274 279 None
275 280 } else {
276 281 Some(index_entry.base_revision())
277 282 },
278 283 };
279 284 Ok(entry)
280 285 }
281 286 }
282 287
283 288 /// The revlog entry's bytes and the necessary informations to extract
284 289 /// the entry's data.
285 290 #[derive(Debug)]
286 291 pub struct RevlogEntry<'a> {
287 292 rev: Revision,
288 293 bytes: &'a [u8],
289 294 compressed_len: usize,
290 295 uncompressed_len: usize,
291 296 base_rev: Option<Revision>,
292 297 }
293 298
294 299 impl<'a> RevlogEntry<'a> {
295 300 /// Extract the data contained in the entry.
296 301 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
297 302 if self.bytes.is_empty() {
298 303 return Ok(Cow::Borrowed(&[]));
299 304 }
300 305 match self.bytes[0] {
301 306 // Revision data is the entirety of the entry, including this
302 307 // header.
303 308 b'\0' => Ok(Cow::Borrowed(self.bytes)),
304 309 // Raw revision data follows.
305 310 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
306 311 // zlib (RFC 1950) data.
307 312 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
308 313 // zstd data.
309 314 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
310 315 // A proper new format should have had a repo/store requirement.
311 316 _format_type => Err(RevlogError::corrupted()),
312 317 }
313 318 }
314 319
315 320 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
316 321 let mut decoder = ZlibDecoder::new(self.bytes);
317 322 if self.is_delta() {
318 323 let mut buf = Vec::with_capacity(self.compressed_len);
319 324 decoder
320 325 .read_to_end(&mut buf)
321 326 .map_err(|_| RevlogError::corrupted())?;
322 327 Ok(buf)
323 328 } else {
324 329 let mut buf = vec![0; self.uncompressed_len];
325 330 decoder
326 331 .read_exact(&mut buf)
327 332 .map_err(|_| RevlogError::corrupted())?;
328 333 Ok(buf)
329 334 }
330 335 }
331 336
332 337 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
333 338 if self.is_delta() {
334 339 let mut buf = Vec::with_capacity(self.compressed_len);
335 340 zstd::stream::copy_decode(self.bytes, &mut buf)
336 341 .map_err(|_| RevlogError::corrupted())?;
337 342 Ok(buf)
338 343 } else {
339 344 let mut buf = vec![0; self.uncompressed_len];
340 345 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
341 346 .map_err(|_| RevlogError::corrupted())?;
342 347 if len != self.uncompressed_len {
343 348 Err(RevlogError::corrupted())
344 349 } else {
345 350 Ok(buf)
346 351 }
347 352 }
348 353 }
349 354
350 355 /// Tell if the entry is a snapshot or a delta
351 356 /// (influences on decompression).
352 357 fn is_delta(&self) -> bool {
353 358 self.base_rev.is_some()
354 359 }
355 360 }
356 361
357 362 /// Format version of the revlog.
358 363 pub fn get_version(index_bytes: &[u8]) -> u16 {
359 364 BigEndian::read_u16(&index_bytes[2..=3])
360 365 }
361 366
362 367 /// Calculate the hash of a revision given its data and its parents.
363 368 fn hash(
364 369 data: &[u8],
365 370 p1_hash: &[u8],
366 371 p2_hash: &[u8],
367 372 ) -> [u8; NODE_BYTES_LENGTH] {
368 373 let mut hasher = Sha1::new();
369 374 let (a, b) = (p1_hash, p2_hash);
370 375 if a > b {
371 376 hasher.update(b);
372 377 hasher.update(a);
373 378 } else {
374 379 hasher.update(a);
375 380 hasher.update(b);
376 381 }
377 382 hasher.update(data);
378 383 *hasher.finalize().as_ref()
379 384 }
380 385
381 386 #[cfg(test)]
382 387 mod tests {
383 388 use super::*;
384 389
385 390 use super::super::index::IndexEntryBuilder;
386 391
387 392 #[test]
388 393 fn version_test() {
389 394 let bytes = IndexEntryBuilder::new()
390 395 .is_first(true)
391 396 .with_version(1)
392 397 .build();
393 398
394 399 assert_eq!(get_version(&bytes), 1)
395 400 }
396 401 }
@@ -1,61 +1,67 b''
1 1 //! The revset query language
2 2 //!
3 3 //! <https://www.mercurial-scm.org/repo/hg/help/revsets>
4 4
5 5 use crate::errors::HgError;
6 6 use crate::repo::Repo;
7 7 use crate::revlog::revlog::{Revlog, RevlogError};
8 8 use crate::revlog::NodePrefix;
9 9 use crate::revlog::{Revision, NULL_REVISION, WORKING_DIRECTORY_HEX};
10 10 use crate::Node;
11 11
12 12 /// Resolve a query string into a single revision.
13 13 ///
14 14 /// Only some of the revset language is implemented yet.
15 15 pub fn resolve_single(
16 16 input: &str,
17 17 repo: &Repo,
18 18 ) -> Result<Revision, RevlogError> {
19 19 let changelog = repo.changelog()?;
20 20
21 21 match resolve_rev_number_or_hex_prefix(input, &changelog.revlog) {
22 22 Err(RevlogError::InvalidRevision) => {} // Try other syntax
23 23 result => return result,
24 24 }
25 25
26 26 if input == "null" {
27 27 return Ok(NULL_REVISION);
28 28 }
29 29
30 30 // TODO: support for the rest of the language here.
31 31
32 32 Err(
33 33 HgError::unsupported(format!("cannot parse revset '{}'", input))
34 34 .into(),
35 35 )
36 36 }
37 37
38 38 /// Resolve the small subset of the language suitable for revlogs other than
39 39 /// the changelog, such as in `hg debugdata --manifest` CLI argument.
40 40 ///
41 41 /// * A non-negative decimal integer for a revision number, or
42 42 /// * An hexadecimal string, for the unique node ID that starts with this
43 43 /// prefix
44 44 pub fn resolve_rev_number_or_hex_prefix(
45 45 input: &str,
46 46 revlog: &Revlog,
47 47 ) -> Result<Revision, RevlogError> {
48 // The Python equivalent of this is part of `revsymbol` in
49 // `mercurial/scmutil.py`
50
48 51 if let Ok(integer) = input.parse::<i32>() {
49 if integer >= 0 && revlog.has_rev(integer) {
52 if integer.to_string() == input
53 && integer >= 0
54 && revlog.has_rev(integer)
55 {
50 56 return Ok(integer);
51 57 }
52 58 }
53 59 if let Ok(prefix) = NodePrefix::from_hex(input) {
54 60 if prefix.is_prefix_of(&Node::from_hex(WORKING_DIRECTORY_HEX).unwrap())
55 61 {
56 62 return Err(RevlogError::WDirUnsupported);
57 63 }
58 64 return revlog.get_node_rev(prefix);
59 65 }
60 66 Err(RevlogError::InvalidRevision)
61 67 }
@@ -1,309 +1,312 b''
1 1 #require rhg
2 2
3 3 $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
4 4
5 5 Unimplemented command
6 6 $ $NO_FALLBACK rhg unimplemented-command
7 7 unsupported feature: error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
8 8
9 9 USAGE:
10 10 rhg [OPTIONS] <SUBCOMMAND>
11 11
12 12 For more information try --help
13 13
14 14 [252]
15 15 $ rhg unimplemented-command --config rhg.on-unsupported=abort-silent
16 16 [252]
17 17
18 18 Finding root
19 19 $ $NO_FALLBACK rhg root
20 20 abort: no repository found in '$TESTTMP' (.hg not found)!
21 21 [255]
22 22
23 23 $ hg init repository
24 24 $ cd repository
25 25 $ $NO_FALLBACK rhg root
26 26 $TESTTMP/repository
27 27
28 28 Reading and setting configuration
29 29 $ echo "[ui]" >> $HGRCPATH
30 30 $ echo "username = user1" >> $HGRCPATH
31 31 $ $NO_FALLBACK rhg config ui.username
32 32 user1
33 33 $ echo "[ui]" >> .hg/hgrc
34 34 $ echo "username = user2" >> .hg/hgrc
35 35 $ $NO_FALLBACK rhg config ui.username
36 36 user2
37 37 $ $NO_FALLBACK rhg --config ui.username=user3 config ui.username
38 38 user3
39 39
40 40 Unwritable file descriptor
41 41 $ $NO_FALLBACK rhg root > /dev/full
42 42 abort: No space left on device (os error 28)
43 43 [255]
44 44
45 45 Deleted repository
46 46 $ rm -rf `pwd`
47 47 $ $NO_FALLBACK rhg root
48 48 abort: error getting current working directory: $ENOENT$
49 49 [255]
50 50
51 51 Listing tracked files
52 52 $ cd $TESTTMP
53 53 $ hg init repository
54 54 $ cd repository
55 55 $ for i in 1 2 3; do
56 56 > echo $i >> file$i
57 57 > hg add file$i
58 58 > done
59 59 > hg commit -m "commit $i" -q
60 60
61 61 Listing tracked files from root
62 62 $ $NO_FALLBACK rhg files
63 63 file1
64 64 file2
65 65 file3
66 66
67 67 Listing tracked files from subdirectory
68 68 $ mkdir -p path/to/directory
69 69 $ cd path/to/directory
70 70 $ $NO_FALLBACK rhg files
71 71 ../../../file1
72 72 ../../../file2
73 73 ../../../file3
74 74
75 75 Listing tracked files through broken pipe
76 76 $ $NO_FALLBACK rhg files | head -n 1
77 77 ../../../file1
78 78
79 79 Debuging data in inline index
80 80 $ cd $TESTTMP
81 81 $ rm -rf repository
82 82 $ hg init repository
83 83 $ cd repository
84 84 $ for i in 1 2 3 4 5 6; do
85 85 > echo $i >> file-$i
86 86 > hg add file-$i
87 87 > hg commit -m "Commit $i" -q
88 88 > done
89 89 $ $NO_FALLBACK rhg debugdata -c 2
90 90 8d0267cb034247ebfa5ee58ce59e22e57a492297
91 91 test
92 92 0 0
93 93 file-3
94 94
95 95 Commit 3 (no-eol)
96 96 $ $NO_FALLBACK rhg debugdata -m 2
97 97 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
98 98 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
99 99 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
100 100
101 101 Debuging with full node id
102 102 $ $NO_FALLBACK rhg debugdata -c `hg log -r 0 -T '{node}'`
103 103 d1d1c679d3053e8926061b6f45ca52009f011e3f
104 104 test
105 105 0 0
106 106 file-1
107 107
108 108 Commit 1 (no-eol)
109 109
110 110 Specifying revisions by changeset ID
111 111 $ hg log -T '{node}\n'
112 112 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
113 113 d654274993d0149eecc3cc03214f598320211900
114 114 f646af7e96481d3a5470b695cf30ad8e3ab6c575
115 115 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
116 116 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
117 117 6ae9681c6d30389694d8701faf24b583cf3ccafe
118 118 $ $NO_FALLBACK rhg files -r cf8b83
119 119 file-1
120 120 file-2
121 121 file-3
122 122 $ $NO_FALLBACK rhg cat -r cf8b83 file-2
123 123 2
124 124 $ $NO_FALLBACK rhg cat -r c file-2
125 125 abort: ambiguous revision identifier: c
126 126 [255]
127 127 $ $NO_FALLBACK rhg cat -r d file-2
128 128 2
129 $ $NO_FALLBACK rhg cat -r 0000 file-2
130 abort: invalid revision identifier: 0000
131 [255]
129 132
130 133 Cat files
131 134 $ cd $TESTTMP
132 135 $ rm -rf repository
133 136 $ hg init repository
134 137 $ cd repository
135 138 $ echo "original content" > original
136 139 $ hg add original
137 140 $ hg commit -m "add original" original
138 141 $ $NO_FALLBACK rhg cat -r 0 original
139 142 original content
140 143 Cat copied file should not display copy metadata
141 144 $ hg copy original copy_of_original
142 145 $ hg commit -m "add copy of original"
143 146 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
144 147 original content
145 148
146 149 Fallback to Python
147 150 $ $NO_FALLBACK rhg cat original
148 151 unsupported feature: `rhg cat` without `--rev` / `-r`
149 152 [252]
150 153 $ rhg cat original
151 154 original content
152 155
153 156 $ FALLBACK_EXE="$RHG_FALLBACK_EXECUTABLE"
154 157 $ unset RHG_FALLBACK_EXECUTABLE
155 158 $ rhg cat original
156 159 abort: 'rhg.on-unsupported=fallback' without 'rhg.fallback-executable' set.
157 160 [255]
158 161 $ RHG_FALLBACK_EXECUTABLE="$FALLBACK_EXE"
159 162 $ export RHG_FALLBACK_EXECUTABLE
160 163
161 164 $ rhg cat original --config rhg.fallback-executable=false
162 165 [1]
163 166
164 167 $ rhg cat original --config rhg.fallback-executable=hg-non-existent
165 168 tried to fall back to a 'hg-non-existent' sub-process but got error $ENOENT$
166 169 unsupported feature: `rhg cat` without `--rev` / `-r`
167 170 [252]
168 171
169 172 $ rhg cat original --config rhg.fallback-executable=rhg
170 173 Blocking recursive fallback. The 'rhg.fallback-executable = rhg' config points to `rhg` itself.
171 174 unsupported feature: `rhg cat` without `--rev` / `-r`
172 175 [252]
173 176
174 177 Requirements
175 178 $ $NO_FALLBACK rhg debugrequirements
176 179 dotencode
177 180 fncache
178 181 generaldelta
179 182 persistent-nodemap
180 183 revlog-compression-zstd (zstd !)
181 184 revlogv1
182 185 sparserevlog
183 186 store
184 187
185 188 $ echo indoor-pool >> .hg/requires
186 189 $ $NO_FALLBACK rhg files
187 190 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
188 191 [252]
189 192
190 193 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
191 194 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
192 195 [252]
193 196
194 197 $ $NO_FALLBACK rhg debugrequirements
195 198 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
196 199 [252]
197 200
198 201 $ echo -e '\xFF' >> .hg/requires
199 202 $ $NO_FALLBACK rhg debugrequirements
200 203 abort: parse error in 'requires' file
201 204 [255]
202 205
203 206 Persistent nodemap
204 207 $ cd $TESTTMP
205 208 $ rm -rf repository
206 209 $ hg --config format.use-persistent-nodemap=no init repository
207 210 $ cd repository
208 211 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
209 212 [1]
210 213 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
211 214 $ hg id -r tip
212 215 c3ae8dec9fad tip
213 216 $ ls .hg/store/00changelog*
214 217 .hg/store/00changelog.d
215 218 .hg/store/00changelog.i
216 219 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
217 220 of
218 221
219 222 $ cd $TESTTMP
220 223 $ rm -rf repository
221 224 $ hg --config format.use-persistent-nodemap=True init repository
222 225 $ cd repository
223 226 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
224 227 persistent-nodemap
225 228 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
226 229 $ hg id -r tip
227 230 c3ae8dec9fad tip
228 231 $ ls .hg/store/00changelog*
229 232 .hg/store/00changelog-*.nd (glob)
230 233 .hg/store/00changelog.d
231 234 .hg/store/00changelog.i
232 235 .hg/store/00changelog.n
233 236
234 237 Specifying revisions by changeset ID
235 238 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
236 239 of
237 240 $ $NO_FALLBACK rhg cat -r c3ae8dec9fad of
238 241 r5000
239 242
240 243 Crate a shared repository
241 244
242 245 $ echo "[extensions]" >> $HGRCPATH
243 246 $ echo "share = " >> $HGRCPATH
244 247
245 248 $ cd $TESTTMP
246 249 $ hg init repo1
247 250 $ echo a > repo1/a
248 251 $ hg -R repo1 commit -A -m'init'
249 252 adding a
250 253
251 254 $ hg share repo1 repo2
252 255 updating working directory
253 256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 257
255 258 And check that basic rhg commands work with sharing
256 259
257 260 $ $NO_FALLBACK rhg files -R repo2
258 261 repo2/a
259 262 $ $NO_FALLBACK rhg -R repo2 cat -r 0 repo2/a
260 263 a
261 264
262 265 Same with relative sharing
263 266
264 267 $ hg share repo2 repo3 --relative
265 268 updating working directory
266 269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 270
268 271 $ $NO_FALLBACK rhg files -R repo3
269 272 repo3/a
270 273 $ $NO_FALLBACK rhg -R repo3 cat -r 0 repo3/a
271 274 a
272 275
273 276 Same with share-safe
274 277
275 278 $ echo "[format]" >> $HGRCPATH
276 279 $ echo "use-share-safe = True" >> $HGRCPATH
277 280
278 281 $ cd $TESTTMP
279 282 $ hg init repo4
280 283 $ cd repo4
281 284 $ echo a > a
282 285 $ hg commit -A -m'init'
283 286 adding a
284 287
285 288 $ cd ..
286 289 $ hg share repo4 repo5
287 290 updating working directory
288 291 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
289 292
290 293 And check that basic rhg commands work with sharing
291 294
292 295 $ cd repo5
293 296 $ $NO_FALLBACK rhg files
294 297 a
295 298 $ $NO_FALLBACK rhg cat -r 0 a
296 299 a
297 300
298 301 The blackbox extension is supported
299 302
300 303 $ echo "[extensions]" >> $HGRCPATH
301 304 $ echo "blackbox =" >> $HGRCPATH
302 305 $ echo "[blackbox]" >> $HGRCPATH
303 306 $ echo "maxsize = 1" >> $HGRCPATH
304 307 $ $NO_FALLBACK rhg files > /dev/null
305 308 $ cat .hg/blackbox.log
306 309 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after 0.??? seconds (glob)
307 310 $ cat .hg/blackbox.log.1
308 311 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob)
309 312
General Comments 0
You need to be logged in to leave comments. Login now