##// END OF EJS Templates
rust-nodemap: falling back to C impl as mitigation...
Georges Racinet -
r48600:3fffb485 stable
parent child Browse files
Show More
@@ -1,512 +1,529 b''
1 1 // revlog.rs
2 2 //
3 3 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::{
9 9 cindex,
10 10 utils::{node_from_py_bytes, node_from_py_object},
11 11 };
12 12 use cpython::{
13 13 buffer::{Element, PyBuffer},
14 14 exc::{IndexError, ValueError},
15 15 ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyInt, PyModule,
16 16 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
17 17 };
18 18 use hg::{
19 19 nodemap::{Block, NodeMapError, NodeTree},
20 20 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
21 21 Revision,
22 22 };
23 23 use std::cell::RefCell;
24 24
25 25 /// Return a Struct implementing the Graph trait
26 26 pub(crate) fn pyindex_to_graph(
27 27 py: Python,
28 28 index: PyObject,
29 29 ) -> PyResult<cindex::Index> {
30 30 match index.extract::<MixedIndex>(py) {
31 31 Ok(midx) => Ok(midx.clone_cindex(py)),
32 32 Err(_) => cindex::Index::new(py, index),
33 33 }
34 34 }
35 35
36 36 py_class!(pub class MixedIndex |py| {
37 37 data cindex: RefCell<cindex::Index>;
38 38 data nt: RefCell<Option<NodeTree>>;
39 39 data docket: RefCell<Option<PyObject>>;
40 40 // Holds a reference to the mmap'ed persistent nodemap data
41 41 data mmap: RefCell<Option<PyBuffer>>;
42 42
43 43 def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> {
44 44 Self::new(py, cindex)
45 45 }
46 46
47 47 /// Compatibility layer used for Python consumers needing access to the C index
48 48 ///
49 49 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
50 50 /// that may need to build a custom `nodetree`, based on a specified revset.
51 51 /// With a Rust implementation of the nodemap, we will be able to get rid of
52 52 /// this, by exposing our own standalone nodemap class,
53 53 /// ready to accept `MixedIndex`.
54 54 def get_cindex(&self) -> PyResult<PyObject> {
55 55 Ok(self.cindex(py).borrow().inner().clone_ref(py))
56 56 }
57 57
58 58 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
59 59
60 60 /// Return Revision if found, raises a bare `error.RevlogError`
61 61 /// in case of ambiguity, same as C version does
62 def get_rev(&self, node: PyBytes) -> PyResult<Option<Revision>> {
62 def get_rev(&self, pynode: PyBytes) -> PyResult<Option<Revision>> {
63 63 let opt = self.get_nodetree(py)?.borrow();
64 64 let nt = opt.as_ref().unwrap();
65 65 let idx = &*self.cindex(py).borrow();
66 let node = node_from_py_bytes(py, &node)?;
67 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))
66 let node = node_from_py_bytes(py, &pynode)?;
67 match nt.find_bin(idx, node.into())
68 {
69 Ok(None) =>
70 // fallback to C implementation, remove once
71 // https://bz.mercurial-scm.org/show_bug.cgi?id=6554
72 // is fixed (a simple backout should do)
73 self.call_cindex(py, "get_rev", &PyTuple::new(py, &[pynode.into_object()]), None)?
74 .extract(py),
75 Ok(Some(rev)) => Ok(Some(rev)),
76 Err(e) => Err(nodemap_error(py, e)),
77 }
68 78 }
69 79
70 80 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
71 81 /// is not found.
72 82 ///
73 83 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
74 84 /// will catch and rewrap with it
75 85 def rev(&self, node: PyBytes) -> PyResult<Revision> {
76 86 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
77 87 }
78 88
79 89 /// return True if the node exist in the index
80 90 def has_node(&self, node: PyBytes) -> PyResult<bool> {
81 91 self.get_rev(py, node).map(|opt| opt.is_some())
82 92 }
83 93
84 94 /// find length of shortest hex nodeid of a binary ID
85 95 def shortest(&self, node: PyBytes) -> PyResult<usize> {
86 96 let opt = self.get_nodetree(py)?.borrow();
87 97 let nt = opt.as_ref().unwrap();
88 98 let idx = &*self.cindex(py).borrow();
89 99 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
90 100 {
91 101 Ok(Some(l)) => Ok(l),
92 102 Ok(None) => Err(revlog_error(py)),
93 103 Err(e) => Err(nodemap_error(py, e)),
94 104 }
95 105 }
96 106
97 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
107 def partialmatch(&self, pynode: PyObject) -> PyResult<Option<PyBytes>> {
98 108 let opt = self.get_nodetree(py)?.borrow();
99 109 let nt = opt.as_ref().unwrap();
100 110 let idx = &*self.cindex(py).borrow();
101 111
102 112 let node_as_string = if cfg!(feature = "python3-sys") {
103 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
113 pynode.cast_as::<PyString>(py)?.to_string(py)?.to_string()
104 114 }
105 115 else {
106 let node = node.extract::<PyBytes>(py)?;
116 let node = pynode.extract::<PyBytes>(py)?;
107 117 String::from_utf8_lossy(node.data(py)).to_string()
108 118 };
109 119
110 120 let prefix = NodePrefix::from_hex(&node_as_string).map_err(|_| PyErr::new::<ValueError, _>(py, "Invalid node or prefix"))?;
111 121
112 nt.find_bin(idx, prefix)
113 // TODO make an inner API returning the node directly
114 .map(|opt| opt.map(
115 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
116 .map_err(|e| nodemap_error(py, e))
117
122 match nt.find_bin(idx, prefix) {
123 Ok(None) =>
124 // fallback to C implementation, remove once
125 // https://bz.mercurial-scm.org/show_bug.cgi?id=6554
126 // is fixed (a simple backout should do)
127 self.call_cindex(
128 py, "partialmatch",
129 &PyTuple::new(py, &[pynode]), None
130 )?.extract(py),
131 Ok(Some(rev)) =>
132 Ok(Some(PyBytes::new(py, idx.node(rev).unwrap().as_bytes()))),
133 Err(e) => Err(nodemap_error(py, e)),
134 }
118 135 }
119 136
120 137 /// append an index entry
121 138 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
122 139 if tup.len(py) < 8 {
123 140 // this is better than the panic promised by tup.get_item()
124 141 return Err(
125 142 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
126 143 }
127 144 let node_bytes = tup.get_item(py, 7).extract(py)?;
128 145 let node = node_from_py_object(py, &node_bytes)?;
129 146
130 147 let mut idx = self.cindex(py).borrow_mut();
131 148 let rev = idx.len() as Revision;
132 149
133 150 idx.append(py, tup)?;
134 151 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
135 152 .insert(&*idx, &node, rev)
136 153 .map_err(|e| nodemap_error(py, e))?;
137 154 Ok(py.None())
138 155 }
139 156
140 157 def __delitem__(&self, key: PyObject) -> PyResult<()> {
141 158 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
142 159 self.cindex(py).borrow().inner().del_item(py, key)?;
143 160 let mut opt = self.get_nodetree(py)?.borrow_mut();
144 161 let mut nt = opt.as_mut().unwrap();
145 162 nt.invalidate_all();
146 163 self.fill_nodemap(py, &mut nt)?;
147 164 Ok(())
148 165 }
149 166
150 167 //
151 168 // Reforwarded C index API
152 169 //
153 170
154 171 // index_methods (tp_methods). Same ordering as in revlog.c
155 172
156 173 /// return the gca set of the given revs
157 174 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
158 175 self.call_cindex(py, "ancestors", args, kw)
159 176 }
160 177
161 178 /// return the heads of the common ancestors of the given revs
162 179 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
163 180 self.call_cindex(py, "commonancestorsheads", args, kw)
164 181 }
165 182
166 183 /// Clear the index caches and inner py_class data.
167 184 /// It is Python's responsibility to call `update_nodemap_data` again.
168 185 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
169 186 self.nt(py).borrow_mut().take();
170 187 self.docket(py).borrow_mut().take();
171 188 self.mmap(py).borrow_mut().take();
172 189 self.call_cindex(py, "clearcaches", args, kw)
173 190 }
174 191
175 192 /// return the raw binary string representing a revision
176 193 def entry_binary(&self, *args, **kw) -> PyResult<PyObject> {
177 194 self.call_cindex(py, "entry_binary", args, kw)
178 195 }
179 196
180 197 /// return a binary packed version of the header
181 198 def pack_header(&self, *args, **kw) -> PyResult<PyObject> {
182 199 self.call_cindex(py, "pack_header", args, kw)
183 200 }
184 201
185 202 /// get an index entry
186 203 def get(&self, *args, **kw) -> PyResult<PyObject> {
187 204 self.call_cindex(py, "get", args, kw)
188 205 }
189 206
190 207 /// compute phases
191 208 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
192 209 self.call_cindex(py, "computephasesmapsets", args, kw)
193 210 }
194 211
195 212 /// reachableroots
196 213 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
197 214 self.call_cindex(py, "reachableroots2", args, kw)
198 215 }
199 216
200 217 /// get head revisions
201 218 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
202 219 self.call_cindex(py, "headrevs", args, kw)
203 220 }
204 221
205 222 /// get filtered head revisions
206 223 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
207 224 self.call_cindex(py, "headrevsfiltered", args, kw)
208 225 }
209 226
210 227 /// True if the object is a snapshot
211 228 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
212 229 self.call_cindex(py, "issnapshot", args, kw)
213 230 }
214 231
215 232 /// Gather snapshot data in a cache dict
216 233 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
217 234 self.call_cindex(py, "findsnapshots", args, kw)
218 235 }
219 236
220 237 /// determine revisions with deltas to reconstruct fulltext
221 238 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
222 239 self.call_cindex(py, "deltachain", args, kw)
223 240 }
224 241
225 242 /// slice planned chunk read to reach a density threshold
226 243 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
227 244 self.call_cindex(py, "slicechunktodensity", args, kw)
228 245 }
229 246
230 247 /// stats for the index
231 248 def stats(&self, *args, **kw) -> PyResult<PyObject> {
232 249 self.call_cindex(py, "stats", args, kw)
233 250 }
234 251
235 252 // index_sequence_methods and index_mapping_methods.
236 253 //
237 254 // Since we call back through the high level Python API,
238 255 // there's no point making a distinction between index_get
239 256 // and index_getitem.
240 257
241 258 def __len__(&self) -> PyResult<usize> {
242 259 self.cindex(py).borrow().inner().len(py)
243 260 }
244 261
245 262 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
246 263 // this conversion seems needless, but that's actually because
247 264 // `index_getitem` does not handle conversion from PyLong,
248 265 // which expressions such as [e for e in index] internally use.
249 266 // Note that we don't seem to have a direct way to call
250 267 // PySequence_GetItem (does the job), which would possibly be better
251 268 // for performance
252 269 let key = match key.extract::<Revision>(py) {
253 270 Ok(rev) => rev.to_py_object(py).into_object(),
254 271 Err(_) => key,
255 272 };
256 273 self.cindex(py).borrow().inner().get_item(py, key)
257 274 }
258 275
259 276 def __setitem__(&self, key: PyObject, value: PyObject) -> PyResult<()> {
260 277 self.cindex(py).borrow().inner().set_item(py, key, value)
261 278 }
262 279
263 280 def __contains__(&self, item: PyObject) -> PyResult<bool> {
264 281 // ObjectProtocol does not seem to provide contains(), so
265 282 // this is an equivalent implementation of the index_contains()
266 283 // defined in revlog.c
267 284 let cindex = self.cindex(py).borrow();
268 285 match item.extract::<Revision>(py) {
269 286 Ok(rev) => {
270 287 Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision)
271 288 }
272 289 Err(_) => {
273 290 cindex.inner().call_method(
274 291 py,
275 292 "has_node",
276 293 PyTuple::new(py, &[item]),
277 294 None)?
278 295 .extract(py)
279 296 }
280 297 }
281 298 }
282 299
283 300 def nodemap_data_all(&self) -> PyResult<PyBytes> {
284 301 self.inner_nodemap_data_all(py)
285 302 }
286 303
287 304 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
288 305 self.inner_nodemap_data_incremental(py)
289 306 }
290 307 def update_nodemap_data(
291 308 &self,
292 309 docket: PyObject,
293 310 nm_data: PyObject
294 311 ) -> PyResult<PyObject> {
295 312 self.inner_update_nodemap_data(py, docket, nm_data)
296 313 }
297 314
298 315 @property
299 316 def entry_size(&self) -> PyResult<PyInt> {
300 317 self.cindex(py).borrow().inner().getattr(py, "entry_size")?.extract::<PyInt>(py)
301 318 }
302 319
303 320 @property
304 321 def rust_ext_compat(&self) -> PyResult<PyInt> {
305 322 self.cindex(py).borrow().inner().getattr(py, "rust_ext_compat")?.extract::<PyInt>(py)
306 323 }
307 324
308 325 });
309 326
310 327 impl MixedIndex {
311 328 fn new(py: Python, cindex: PyObject) -> PyResult<MixedIndex> {
312 329 Self::create_instance(
313 330 py,
314 331 RefCell::new(cindex::Index::new(py, cindex)?),
315 332 RefCell::new(None),
316 333 RefCell::new(None),
317 334 RefCell::new(None),
318 335 )
319 336 }
320 337
321 338 /// This is scaffolding at this point, but it could also become
322 339 /// a way to start a persistent nodemap or perform a
323 340 /// vacuum / repack operation
324 341 fn fill_nodemap(
325 342 &self,
326 343 py: Python,
327 344 nt: &mut NodeTree,
328 345 ) -> PyResult<PyObject> {
329 346 let index = self.cindex(py).borrow();
330 347 for r in 0..index.len() {
331 348 let rev = r as Revision;
332 349 // in this case node() won't ever return None
333 350 nt.insert(&*index, index.node(rev).unwrap(), rev)
334 351 .map_err(|e| nodemap_error(py, e))?
335 352 }
336 353 Ok(py.None())
337 354 }
338 355
339 356 fn get_nodetree<'a>(
340 357 &'a self,
341 358 py: Python<'a>,
342 359 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
343 360 if self.nt(py).borrow().is_none() {
344 361 let readonly = Box::new(Vec::new());
345 362 let mut nt = NodeTree::load_bytes(readonly, 0);
346 363 self.fill_nodemap(py, &mut nt)?;
347 364 self.nt(py).borrow_mut().replace(nt);
348 365 }
349 366 Ok(self.nt(py))
350 367 }
351 368
352 369 /// forward a method call to the underlying C index
353 370 fn call_cindex(
354 371 &self,
355 372 py: Python,
356 373 name: &str,
357 374 args: &PyTuple,
358 375 kwargs: Option<&PyDict>,
359 376 ) -> PyResult<PyObject> {
360 377 self.cindex(py)
361 378 .borrow()
362 379 .inner()
363 380 .call_method(py, name, args, kwargs)
364 381 }
365 382
366 383 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
367 384 self.cindex(py).borrow().clone_ref(py)
368 385 }
369 386
370 387 /// Returns the full nodemap bytes to be written as-is to disk
371 388 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
372 389 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
373 390 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
374 391
375 392 // If there's anything readonly, we need to build the data again from
376 393 // scratch
377 394 let bytes = if readonly.len() > 0 {
378 395 let mut nt = NodeTree::load_bytes(Box::new(vec![]), 0);
379 396 self.fill_nodemap(py, &mut nt)?;
380 397
381 398 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
382 399 assert_eq!(readonly.len(), 0);
383 400
384 401 bytes
385 402 } else {
386 403 bytes
387 404 };
388 405
389 406 let bytes = PyBytes::new(py, &bytes);
390 407 Ok(bytes)
391 408 }
392 409
393 410 /// Returns the last saved docket along with the size of any changed data
394 411 /// (in number of blocks), and said data as bytes.
395 412 fn inner_nodemap_data_incremental(
396 413 &self,
397 414 py: Python,
398 415 ) -> PyResult<PyObject> {
399 416 let docket = self.docket(py).borrow();
400 417 let docket = match docket.as_ref() {
401 418 Some(d) => d,
402 419 None => return Ok(py.None()),
403 420 };
404 421
405 422 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
406 423 let masked_blocks = node_tree.masked_readonly_blocks();
407 424 let (_, data) = node_tree.into_readonly_and_added_bytes();
408 425 let changed = masked_blocks * std::mem::size_of::<Block>();
409 426
410 427 Ok((docket, changed, PyBytes::new(py, &data))
411 428 .to_py_object(py)
412 429 .into_object())
413 430 }
414 431
415 432 /// Update the nodemap from the new (mmaped) data.
416 433 /// The docket is kept as a reference for later incremental calls.
417 434 fn inner_update_nodemap_data(
418 435 &self,
419 436 py: Python,
420 437 docket: PyObject,
421 438 nm_data: PyObject,
422 439 ) -> PyResult<PyObject> {
423 440 let buf = PyBuffer::get(py, &nm_data)?;
424 441 let len = buf.item_count();
425 442
426 443 // Build a slice from the mmap'ed buffer data
427 444 let cbuf = buf.buf_ptr();
428 445 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
429 446 && buf.is_c_contiguous()
430 447 && u8::is_compatible_format(buf.format())
431 448 {
432 449 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
433 450 } else {
434 451 return Err(PyErr::new::<ValueError, _>(
435 452 py,
436 453 "Nodemap data buffer has an invalid memory representation"
437 454 .to_string(),
438 455 ));
439 456 };
440 457
441 458 // Keep a reference to the mmap'ed buffer, otherwise we get a dangling
442 459 // pointer.
443 460 self.mmap(py).borrow_mut().replace(buf);
444 461
445 462 let mut nt = NodeTree::load_bytes(Box::new(bytes), len);
446 463
447 464 let data_tip =
448 465 docket.getattr(py, "tip_rev")?.extract::<Revision>(py)?;
449 466 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
450 467 let idx = self.cindex(py).borrow();
451 468 let current_tip = idx.len();
452 469
453 470 for r in (data_tip + 1)..current_tip as Revision {
454 471 let rev = r as Revision;
455 472 // in this case node() won't ever return None
456 473 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
457 474 .map_err(|e| nodemap_error(py, e))?
458 475 }
459 476
460 477 *self.nt(py).borrow_mut() = Some(nt);
461 478
462 479 Ok(py.None())
463 480 }
464 481 }
465 482
466 483 fn revlog_error(py: Python) -> PyErr {
467 484 match py
468 485 .import("mercurial.error")
469 486 .and_then(|m| m.get(py, "RevlogError"))
470 487 {
471 488 Err(e) => e,
472 489 Ok(cls) => PyErr::from_instance(
473 490 py,
474 491 cls.call(py, (py.None(),), None).ok().into_py_object(py),
475 492 ),
476 493 }
477 494 }
478 495
479 496 fn rev_not_in_index(py: Python, rev: Revision) -> PyErr {
480 497 PyErr::new::<ValueError, _>(
481 498 py,
482 499 format!(
483 500 "Inconsistency: Revision {} found in nodemap \
484 501 is not in revlog index",
485 502 rev
486 503 ),
487 504 )
488 505 }
489 506
490 507 /// Standard treatment of NodeMapError
491 508 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
492 509 match err {
493 510 NodeMapError::MultipleResults => revlog_error(py),
494 511 NodeMapError::RevisionNotInIndex(r) => rev_not_in_index(py, r),
495 512 }
496 513 }
497 514
498 515 /// Create the module, with __package__ given from parent
499 516 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
500 517 let dotted_name = &format!("{}.revlog", package);
501 518 let m = PyModule::new(py, dotted_name)?;
502 519 m.add(py, "__package__", package)?;
503 520 m.add(py, "__doc__", "RevLog - Rust implementations")?;
504 521
505 522 m.add_class::<MixedIndex>(py)?;
506 523
507 524 let sys = PyModule::import(py, "sys")?;
508 525 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
509 526 sys_modules.set_item(py, dotted_name, &m)?;
510 527
511 528 Ok(m)
512 529 }
@@ -1,1067 +1,1107 b''
1 1 ===================================
2 2 Test the persistent on-disk nodemap
3 3 ===================================
4 4
5 5
6 6 #if no-rust
7 7
8 8 $ cat << EOF >> $HGRCPATH
9 9 > [format]
10 10 > use-persistent-nodemap=yes
11 11 > [devel]
12 12 > persistent-nodemap=yes
13 13 > EOF
14 14
15 15 #endif
16 16
17 17 $ hg init test-repo --config storage.revlog.persistent-nodemap.slow-path=allow
18 18 $ cd test-repo
19 19
20 20 Check handling of the default slow-path value
21 21
22 22 #if no-pure no-rust
23 23
24 24 $ hg id
25 25 abort: accessing `persistent-nodemap` repository without associated fast implementation.
26 26 (check `hg help config.format.use-persistent-nodemap` for details)
27 27 [255]
28 28
29 29 Unlock further check (we are here to test the feature)
30 30
31 31 $ cat << EOF >> $HGRCPATH
32 32 > [storage]
33 33 > # to avoid spamming the test
34 34 > revlog.persistent-nodemap.slow-path=allow
35 35 > EOF
36 36
37 37 #endif
38 38
39 39 #if rust
40 40
41 41 Regression test for a previous bug in Rust/C FFI for the `Revlog_CAPI` capsule:
42 42 in places where `mercurial/cext/revlog.c` function signatures use `Py_ssize_t`
43 43 (64 bits on Linux x86_64), corresponding declarations in `rust/hg-cpython/src/cindex.rs`
44 44 incorrectly used `libc::c_int` (32 bits).
45 45 As a result, -1 passed from Rust for the null revision became 4294967295 in C.
46 46
47 47 $ hg log -r 00000000
48 48 changeset: -1:000000000000
49 49 tag: tip
50 50 user:
51 51 date: Thu Jan 01 00:00:00 1970 +0000
52 52
53 53
54 54 #endif
55 55
56 56
57 57 $ hg debugformat
58 58 format-variant repo
59 59 fncache: yes
60 60 dirstate-v2: no
61 61 dotencode: yes
62 62 generaldelta: yes
63 63 share-safe: no
64 64 sparserevlog: yes
65 65 persistent-nodemap: yes
66 66 copies-sdc: no
67 67 revlog-v2: no
68 68 changelog-v2: no
69 69 plain-cl-delta: yes
70 70 compression: zlib (no-zstd !)
71 71 compression: zstd (zstd !)
72 72 compression-level: default
73 73 $ hg debugbuilddag .+5000 --new-file
74 74
75 75 $ hg debugnodemap --metadata
76 76 uid: ???????? (glob)
77 77 tip-rev: 5000
78 78 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
79 79 data-length: 121088
80 80 data-unused: 0
81 81 data-unused: 0.000%
82 82 $ f --size .hg/store/00changelog.n
83 83 .hg/store/00changelog.n: size=62
84 84
85 85 Simple lookup works
86 86
87 87 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
88 88 $ hg log -r "$ANYNODE" --template '{rev}\n'
89 89 5000
90 90
91 91
92 92 #if rust
93 93
94 94 $ f --sha256 .hg/store/00changelog-*.nd
95 95 .hg/store/00changelog-????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
96 96
97 97 $ f --sha256 .hg/store/00manifest-*.nd
98 98 .hg/store/00manifest-????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
99 99 $ hg debugnodemap --dump-new | f --sha256 --size
100 100 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
101 101 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
102 102 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
103 103 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
104 104 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
105 105 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
106 106 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
107 107 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
108 108 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
109 109 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
110 110 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
111 111 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
112 112 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
113 113 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
114 114 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
115 115 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
116 116 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
117 117 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
118 118 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
119 119
120 120
121 121 #else
122 122
123 123 $ f --sha256 .hg/store/00changelog-*.nd
124 124 .hg/store/00changelog-????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
125 125 $ hg debugnodemap --dump-new | f --sha256 --size
126 126 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
127 127 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
128 128 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
129 129 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
130 130 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
131 131 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
132 132 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
133 133 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
134 134 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
135 135 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
136 136 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
137 137 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
138 138 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
139 139 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
140 140 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
141 141 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
142 142 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
143 143 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
144 144 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
145 145
146 146 #endif
147 147
148 148 $ hg debugnodemap --check
149 149 revision in index: 5001
150 150 revision in nodemap: 5001
151 151
152 152 add a new commit
153 153
154 154 $ hg up
155 155 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
156 156 $ echo foo > foo
157 157 $ hg add foo
158 158
159 159
160 160 Check slow-path config value handling
161 161 -------------------------------------
162 162
163 163 #if no-pure no-rust
164 164
165 165 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
166 166 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
167 167 falling back to default value: abort
168 168 abort: accessing `persistent-nodemap` repository without associated fast implementation.
169 169 (check `hg help config.format.use-persistent-nodemap` for details)
170 170 [255]
171 171
172 172 $ hg log -r . --config "storage.revlog.persistent-nodemap.slow-path=warn"
173 173 warning: accessing `persistent-nodemap` repository without associated fast implementation.
174 174 (check `hg help config.format.use-persistent-nodemap` for details)
175 175 changeset: 5000:6b02b8c7b966
176 176 tag: tip
177 177 user: debugbuilddag
178 178 date: Thu Jan 01 01:23:20 1970 +0000
179 179 summary: r5000
180 180
181 181 $ hg ci -m 'foo' --config "storage.revlog.persistent-nodemap.slow-path=abort"
182 182 abort: accessing `persistent-nodemap` repository without associated fast implementation.
183 183 (check `hg help config.format.use-persistent-nodemap` for details)
184 184 [255]
185 185
186 186 #else
187 187
188 188 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
189 189 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
190 190 falling back to default value: abort
191 191 6b02b8c7b966+ tip
192 192
193 193 #endif
194 194
195 195 $ hg ci -m 'foo'
196 196
197 197 #if no-pure no-rust
198 198 $ hg debugnodemap --metadata
199 199 uid: ???????? (glob)
200 200 tip-rev: 5001
201 201 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
202 202 data-length: 121088
203 203 data-unused: 0
204 204 data-unused: 0.000%
205 205 #else
206 206 $ hg debugnodemap --metadata
207 207 uid: ???????? (glob)
208 208 tip-rev: 5001
209 209 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
210 210 data-length: 121344
211 211 data-unused: 256
212 212 data-unused: 0.211%
213 213 #endif
214 214
215 215 $ f --size .hg/store/00changelog.n
216 216 .hg/store/00changelog.n: size=62
217 217
218 218 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
219 219
220 220 #if pure
221 221 $ f --sha256 .hg/store/00changelog-*.nd --size
222 222 .hg/store/00changelog-????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
223 223 #endif
224 224
225 225 #if rust
226 226 $ f --sha256 .hg/store/00changelog-*.nd --size
227 227 .hg/store/00changelog-????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
228 228 #endif
229 229
230 230 #if no-pure no-rust
231 231 $ f --sha256 .hg/store/00changelog-*.nd --size
232 232 .hg/store/00changelog-????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
233 233 #endif
234 234
235 235 $ hg debugnodemap --check
236 236 revision in index: 5002
237 237 revision in nodemap: 5002
238 238
239 239 Test code path without mmap
240 240 ---------------------------
241 241
242 242 $ echo bar > bar
243 243 $ hg add bar
244 244 $ hg ci -m 'bar' --config storage.revlog.persistent-nodemap.mmap=no
245 245
246 246 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=yes
247 247 revision in index: 5003
248 248 revision in nodemap: 5003
249 249 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=no
250 250 revision in index: 5003
251 251 revision in nodemap: 5003
252 252
253 253
254 254 #if pure
255 255 $ hg debugnodemap --metadata
256 256 uid: ???????? (glob)
257 257 tip-rev: 5002
258 258 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
259 259 data-length: 121600
260 260 data-unused: 512
261 261 data-unused: 0.421%
262 262 $ f --sha256 .hg/store/00changelog-*.nd --size
263 263 .hg/store/00changelog-????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
264 264 #endif
265 265 #if rust
266 266 $ hg debugnodemap --metadata
267 267 uid: ???????? (glob)
268 268 tip-rev: 5002
269 269 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
270 270 data-length: 121600
271 271 data-unused: 512
272 272 data-unused: 0.421%
273 273 $ f --sha256 .hg/store/00changelog-*.nd --size
274 274 .hg/store/00changelog-????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
275 275 #endif
276 276 #if no-pure no-rust
277 277 $ hg debugnodemap --metadata
278 278 uid: ???????? (glob)
279 279 tip-rev: 5002
280 280 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
281 281 data-length: 121088
282 282 data-unused: 0
283 283 data-unused: 0.000%
284 284 $ f --sha256 .hg/store/00changelog-*.nd --size
285 285 .hg/store/00changelog-????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
286 286 #endif
287 287
288 288 Test force warming the cache
289 289
290 290 $ rm .hg/store/00changelog.n
291 291 $ hg debugnodemap --metadata
292 292 $ hg debugupdatecache
293 293 #if pure
294 294 $ hg debugnodemap --metadata
295 295 uid: ???????? (glob)
296 296 tip-rev: 5002
297 297 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
298 298 data-length: 121088
299 299 data-unused: 0
300 300 data-unused: 0.000%
301 301 #else
302 302 $ hg debugnodemap --metadata
303 303 uid: ???????? (glob)
304 304 tip-rev: 5002
305 305 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
306 306 data-length: 121088
307 307 data-unused: 0
308 308 data-unused: 0.000%
309 309 #endif
310 310
311 311 Check out of sync nodemap
312 312 =========================
313 313
314 314 First copy old data on the side.
315 315
316 316 $ mkdir ../tmp-copies
317 317 $ cp .hg/store/00changelog-????????.nd .hg/store/00changelog.n ../tmp-copies
318 318
319 319 Nodemap lagging behind
320 320 ----------------------
321 321
322 322 make a new commit
323 323
324 324 $ echo bar2 > bar
325 325 $ hg ci -m 'bar2'
326 326 $ NODE=`hg log -r tip -T '{node}\n'`
327 327 $ hg log -r "$NODE" -T '{rev}\n'
328 328 5003
329 329
330 330 If the nodemap is lagging behind, it can catch up fine
331 331
332 332 $ hg debugnodemap --metadata
333 333 uid: ???????? (glob)
334 334 tip-rev: 5003
335 335 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
336 336 data-length: 121344 (pure !)
337 337 data-length: 121344 (rust !)
338 338 data-length: 121152 (no-rust no-pure !)
339 339 data-unused: 192 (pure !)
340 340 data-unused: 192 (rust !)
341 341 data-unused: 0 (no-rust no-pure !)
342 342 data-unused: 0.158% (pure !)
343 343 data-unused: 0.158% (rust !)
344 344 data-unused: 0.000% (no-rust no-pure !)
345 345 $ cp -f ../tmp-copies/* .hg/store/
346 346 $ hg debugnodemap --metadata
347 347 uid: ???????? (glob)
348 348 tip-rev: 5002
349 349 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
350 350 data-length: 121088
351 351 data-unused: 0
352 352 data-unused: 0.000%
353 353 $ hg log -r "$NODE" -T '{rev}\n'
354 354 5003
355 355
356 356 changelog altered
357 357 -----------------
358 358
359 359 If the nodemap is not gated behind a requirements, an unaware client can alter
360 360 the repository so the revlog used to generate the nodemap is not longer
361 361 compatible with the persistent nodemap. We need to detect that.
362 362
363 363 $ hg up "$NODE~5"
364 364 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
365 365 $ echo bar > babar
366 366 $ hg add babar
367 367 $ hg ci -m 'babar'
368 368 created new head
369 369 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
370 370 $ hg log -r "$OTHERNODE" -T '{rev}\n'
371 371 5004
372 372
373 373 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
374 374
375 375 the nodemap should detect the changelog have been tampered with and recover.
376 376
377 377 $ hg debugnodemap --metadata
378 378 uid: ???????? (glob)
379 379 tip-rev: 5002
380 380 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
381 381 data-length: 121536 (pure !)
382 382 data-length: 121088 (rust !)
383 383 data-length: 121088 (no-pure no-rust !)
384 384 data-unused: 448 (pure !)
385 385 data-unused: 0 (rust !)
386 386 data-unused: 0 (no-pure no-rust !)
387 387 data-unused: 0.000% (rust !)
388 388 data-unused: 0.369% (pure !)
389 389 data-unused: 0.000% (no-pure no-rust !)
390 390
391 391 $ cp -f ../tmp-copies/* .hg/store/
392 392 $ hg debugnodemap --metadata
393 393 uid: ???????? (glob)
394 394 tip-rev: 5002
395 395 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
396 396 data-length: 121088
397 397 data-unused: 0
398 398 data-unused: 0.000%
399 399 $ hg log -r "$OTHERNODE" -T '{rev}\n'
400 400 5002
401 401
402 402 missing data file
403 403 -----------------
404 404
405 405 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
406 406 > sed 's/uid: //'`
407 407 $ FILE=.hg/store/00changelog-"${UUID}".nd
408 408 $ mv $FILE ../tmp-data-file
409 409 $ cp .hg/store/00changelog.n ../tmp-docket
410 410
411 411 mercurial don't crash
412 412
413 413 $ hg log -r .
414 414 changeset: 5002:b355ef8adce0
415 415 tag: tip
416 416 parent: 4998:d918ad6d18d3
417 417 user: test
418 418 date: Thu Jan 01 00:00:00 1970 +0000
419 419 summary: babar
420 420
421 421 $ hg debugnodemap --metadata
422 422
423 423 $ hg debugupdatecache
424 424 $ hg debugnodemap --metadata
425 425 uid: * (glob)
426 426 tip-rev: 5002
427 427 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
428 428 data-length: 121088
429 429 data-unused: 0
430 430 data-unused: 0.000%
431
432 Sub-case: fallback for corrupted data file
433 ------------------------------------------
434
435 Sabotaging the data file so that nodemap resolutions fail, triggering fallback to
436 (non-persistent) C implementation.
437
438
439 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
440 > sed 's/uid: //'`
441 $ FILE=.hg/store/00changelog-"${UUID}".nd
442 $ python -c "fobj = open('$FILE', 'r+b'); fobj.write(b'\xff' * 121088); fobj.close()"
443
444 The nodemap data file is still considered in sync with the docket. This
445 would fail without the fallback to the (non-persistent) C implementation:
446
447 $ hg log -r b355ef8adce0949b8bdf6afc72ca853740d65944 -T '{rev}\n' --traceback
448 5002
449
450 The nodemap data file hasn't been fixed, more tests can be inserted:
451
452 $ hg debugnodemap --dump-disk | f --bytes=256 --hexdump --size
453 size=121088
454 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
455 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
456 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
457 0030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
458 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
459 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
460 0060: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
461 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
462 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
463 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
464 00a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
465 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
466 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
467 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
468 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
469 00f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
470
431 471 $ mv ../tmp-data-file $FILE
432 472 $ mv ../tmp-docket .hg/store/00changelog.n
433 473
434 474 Check transaction related property
435 475 ==================================
436 476
437 477 An up to date nodemap should be available to shell hooks,
438 478
439 479 $ echo dsljfl > a
440 480 $ hg add a
441 481 $ hg ci -m a
442 482 $ hg debugnodemap --metadata
443 483 uid: ???????? (glob)
444 484 tip-rev: 5003
445 485 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
446 486 data-length: 121088
447 487 data-unused: 0
448 488 data-unused: 0.000%
449 489 $ echo babar2 > babar
450 490 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
451 491 uid: ???????? (glob)
452 492 tip-rev: 5004
453 493 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
454 494 data-length: 121280 (pure !)
455 495 data-length: 121280 (rust !)
456 496 data-length: 121088 (no-pure no-rust !)
457 497 data-unused: 192 (pure !)
458 498 data-unused: 192 (rust !)
459 499 data-unused: 0 (no-pure no-rust !)
460 500 data-unused: 0.158% (pure !)
461 501 data-unused: 0.158% (rust !)
462 502 data-unused: 0.000% (no-pure no-rust !)
463 503 $ hg debugnodemap --metadata
464 504 uid: ???????? (glob)
465 505 tip-rev: 5004
466 506 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
467 507 data-length: 121280 (pure !)
468 508 data-length: 121280 (rust !)
469 509 data-length: 121088 (no-pure no-rust !)
470 510 data-unused: 192 (pure !)
471 511 data-unused: 192 (rust !)
472 512 data-unused: 0 (no-pure no-rust !)
473 513 data-unused: 0.158% (pure !)
474 514 data-unused: 0.158% (rust !)
475 515 data-unused: 0.000% (no-pure no-rust !)
476 516
477 517 Another process does not see the pending nodemap content during run.
478 518
479 519 $ echo qpoasp > a
480 520 $ hg ci -m a2 \
481 521 > --config "hooks.pretxnclose=sh \"$RUNTESTDIR/testlib/wait-on-file\" 20 sync-repo-read sync-txn-pending" \
482 522 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
483 523
484 524 (read the repository while the commit transaction is pending)
485 525
486 526 $ sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-pending && \
487 527 > hg debugnodemap --metadata && \
488 528 > sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-close sync-repo-read
489 529 uid: ???????? (glob)
490 530 tip-rev: 5004
491 531 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
492 532 data-length: 121280 (pure !)
493 533 data-length: 121280 (rust !)
494 534 data-length: 121088 (no-pure no-rust !)
495 535 data-unused: 192 (pure !)
496 536 data-unused: 192 (rust !)
497 537 data-unused: 0 (no-pure no-rust !)
498 538 data-unused: 0.158% (pure !)
499 539 data-unused: 0.158% (rust !)
500 540 data-unused: 0.000% (no-pure no-rust !)
501 541 $ hg debugnodemap --metadata
502 542 uid: ???????? (glob)
503 543 tip-rev: 5005
504 544 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
505 545 data-length: 121536 (pure !)
506 546 data-length: 121536 (rust !)
507 547 data-length: 121088 (no-pure no-rust !)
508 548 data-unused: 448 (pure !)
509 549 data-unused: 448 (rust !)
510 550 data-unused: 0 (no-pure no-rust !)
511 551 data-unused: 0.369% (pure !)
512 552 data-unused: 0.369% (rust !)
513 553 data-unused: 0.000% (no-pure no-rust !)
514 554
515 555 $ cat output.txt
516 556
517 557 Check that a failing transaction will properly revert the data
518 558
519 559 $ echo plakfe > a
520 560 $ f --size --sha256 .hg/store/00changelog-*.nd
521 561 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
522 562 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
523 563 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
524 564 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
525 565 transaction abort!
526 566 rollback completed
527 567 abort: This is a late abort
528 568 [255]
529 569 $ hg debugnodemap --metadata
530 570 uid: ???????? (glob)
531 571 tip-rev: 5005
532 572 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
533 573 data-length: 121536 (pure !)
534 574 data-length: 121536 (rust !)
535 575 data-length: 121088 (no-pure no-rust !)
536 576 data-unused: 448 (pure !)
537 577 data-unused: 448 (rust !)
538 578 data-unused: 0 (no-pure no-rust !)
539 579 data-unused: 0.369% (pure !)
540 580 data-unused: 0.369% (rust !)
541 581 data-unused: 0.000% (no-pure no-rust !)
542 582 $ f --size --sha256 .hg/store/00changelog-*.nd
543 583 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
544 584 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
545 585 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
546 586
547 587 Check that removing content does not confuse the nodemap
548 588 --------------------------------------------------------
549 589
550 590 removing data with rollback
551 591
552 592 $ echo aso > a
553 593 $ hg ci -m a4
554 594 $ hg rollback
555 595 repository tip rolled back to revision 5005 (undo commit)
556 596 working directory now based on revision 5005
557 597 $ hg id -r .
558 598 90d5d3ba2fc4 tip
559 599
560 600 roming data with strip
561 601
562 602 $ echo aso > a
563 603 $ hg ci -m a4
564 604 $ hg --config extensions.strip= strip -r . --no-backup
565 605 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
566 606 $ hg id -r . --traceback
567 607 90d5d3ba2fc4 tip
568 608
569 609 Test upgrade / downgrade
570 610 ========================
571 611
572 612 downgrading
573 613
574 614 $ cat << EOF >> .hg/hgrc
575 615 > [format]
576 616 > use-persistent-nodemap=no
577 617 > EOF
578 618 $ hg debugformat -v
579 619 format-variant repo config default
580 620 fncache: yes yes yes
581 621 dirstate-v2: no no no
582 622 dotencode: yes yes yes
583 623 generaldelta: yes yes yes
584 624 share-safe: no no no
585 625 sparserevlog: yes yes yes
586 626 persistent-nodemap: yes no no
587 627 copies-sdc: no no no
588 628 revlog-v2: no no no
589 629 changelog-v2: no no no
590 630 plain-cl-delta: yes yes yes
591 631 compression: zlib zlib zlib (no-zstd !)
592 632 compression: zstd zstd zstd (zstd !)
593 633 compression-level: default default default
594 634 $ hg debugupgraderepo --run --no-backup
595 635 upgrade will perform the following actions:
596 636
597 637 requirements
598 638 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-zstd no-dirstate-v2 !)
599 639 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd no-dirstate-v2 !)
600 640 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd dirstate-v2 !)
601 641 removed: persistent-nodemap
602 642
603 643 processed revlogs:
604 644 - all-filelogs
605 645 - changelog
606 646 - manifest
607 647
608 648 beginning upgrade...
609 649 repository locked and read-only
610 650 creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
611 651 (it is safe to interrupt this process any time before data migration completes)
612 652 downgrading repository to not use persistent nodemap feature
613 653 removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
614 654 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
615 655 00changelog-*.nd (glob)
616 656 00manifest-*.nd (glob)
617 657 undo.backup.00changelog.n
618 658 undo.backup.00manifest.n
619 659 $ hg debugnodemap --metadata
620 660
621 661
622 662 upgrading
623 663
624 664 $ cat << EOF >> .hg/hgrc
625 665 > [format]
626 666 > use-persistent-nodemap=yes
627 667 > EOF
628 668 $ hg debugformat -v
629 669 format-variant repo config default
630 670 fncache: yes yes yes
631 671 dirstate-v2: no no no
632 672 dotencode: yes yes yes
633 673 generaldelta: yes yes yes
634 674 share-safe: no no no
635 675 sparserevlog: yes yes yes
636 676 persistent-nodemap: no yes no
637 677 copies-sdc: no no no
638 678 revlog-v2: no no no
639 679 changelog-v2: no no no
640 680 plain-cl-delta: yes yes yes
641 681 compression: zlib zlib zlib (no-zstd !)
642 682 compression: zstd zstd zstd (zstd !)
643 683 compression-level: default default default
644 684 $ hg debugupgraderepo --run --no-backup
645 685 upgrade will perform the following actions:
646 686
647 687 requirements
648 688 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store (no-zstd no-dirstate-v2 !)
649 689 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd no-dirstate-v2 !)
650 690 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd dirstate-v2 !)
651 691 added: persistent-nodemap
652 692
653 693 persistent-nodemap
654 694 Speedup revision lookup by node id.
655 695
656 696 processed revlogs:
657 697 - all-filelogs
658 698 - changelog
659 699 - manifest
660 700
661 701 beginning upgrade...
662 702 repository locked and read-only
663 703 creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
664 704 (it is safe to interrupt this process any time before data migration completes)
665 705 upgrading repository to use persistent nodemap feature
666 706 removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
667 707 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
668 708 00changelog-*.nd (glob)
669 709 00changelog.n
670 710 00manifest-*.nd (glob)
671 711 00manifest.n
672 712 undo.backup.00changelog.n
673 713 undo.backup.00manifest.n
674 714
675 715 $ hg debugnodemap --metadata
676 716 uid: * (glob)
677 717 tip-rev: 5005
678 718 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
679 719 data-length: 121088
680 720 data-unused: 0
681 721 data-unused: 0.000%
682 722
683 723 Running unrelated upgrade
684 724
685 725 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
686 726 upgrade will perform the following actions:
687 727
688 728 requirements
689 729 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store (no-zstd no-dirstate-v2 !)
690 730 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd no-dirstate-v2 !)
691 731 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, sparserevlog, store (zstd dirstate-v2 !)
692 732
693 733 optimisations: re-delta-all
694 734
695 735 processed revlogs:
696 736 - all-filelogs
697 737 - changelog
698 738 - manifest
699 739
700 740 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
701 741 00changelog-*.nd (glob)
702 742 00changelog.n
703 743 00manifest-*.nd (glob)
704 744 00manifest.n
705 745
706 746 $ hg debugnodemap --metadata
707 747 uid: * (glob)
708 748 tip-rev: 5005
709 749 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
710 750 data-length: 121088
711 751 data-unused: 0
712 752 data-unused: 0.000%
713 753
714 754 Persistent nodemap and local/streaming clone
715 755 ============================================
716 756
717 757 $ cd ..
718 758
719 759 standard clone
720 760 --------------
721 761
722 762 The persistent nodemap should exist after a streaming clone
723 763
724 764 $ hg clone --pull --quiet -U test-repo standard-clone
725 765 $ ls -1 standard-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
726 766 00changelog-*.nd (glob)
727 767 00changelog.n
728 768 00manifest-*.nd (glob)
729 769 00manifest.n
730 770 $ hg -R standard-clone debugnodemap --metadata
731 771 uid: * (glob)
732 772 tip-rev: 5005
733 773 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
734 774 data-length: 121088
735 775 data-unused: 0
736 776 data-unused: 0.000%
737 777
738 778
739 779 local clone
740 780 ------------
741 781
742 782 The persistent nodemap should exist after a streaming clone
743 783
744 784 $ hg clone -U test-repo local-clone
745 785 $ ls -1 local-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
746 786 00changelog-*.nd (glob)
747 787 00changelog.n
748 788 00manifest-*.nd (glob)
749 789 00manifest.n
750 790 $ hg -R local-clone debugnodemap --metadata
751 791 uid: * (glob)
752 792 tip-rev: 5005
753 793 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
754 794 data-length: 121088
755 795 data-unused: 0
756 796 data-unused: 0.000%
757 797
758 798 Test various corruption case
759 799 ============================
760 800
761 801 Missing datafile
762 802 ----------------
763 803
764 804 Test behavior with a missing datafile
765 805
766 806 $ hg clone --quiet --pull test-repo corruption-test-repo
767 807 $ ls -1 corruption-test-repo/.hg/store/00changelog*
768 808 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
769 809 corruption-test-repo/.hg/store/00changelog.d
770 810 corruption-test-repo/.hg/store/00changelog.i
771 811 corruption-test-repo/.hg/store/00changelog.n
772 812 $ rm corruption-test-repo/.hg/store/00changelog*.nd
773 813 $ hg log -R corruption-test-repo -r .
774 814 changeset: 5005:90d5d3ba2fc4
775 815 tag: tip
776 816 user: test
777 817 date: Thu Jan 01 00:00:00 1970 +0000
778 818 summary: a2
779 819
780 820 $ ls -1 corruption-test-repo/.hg/store/00changelog*
781 821 corruption-test-repo/.hg/store/00changelog.d
782 822 corruption-test-repo/.hg/store/00changelog.i
783 823 corruption-test-repo/.hg/store/00changelog.n
784 824
785 825 Truncated data file
786 826 -------------------
787 827
788 828 Test behavior with a too short datafile
789 829
790 830 rebuild the missing data
791 831 $ hg -R corruption-test-repo debugupdatecache
792 832 $ ls -1 corruption-test-repo/.hg/store/00changelog*
793 833 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
794 834 corruption-test-repo/.hg/store/00changelog.d
795 835 corruption-test-repo/.hg/store/00changelog.i
796 836 corruption-test-repo/.hg/store/00changelog.n
797 837
798 838 truncate the file
799 839
800 840 $ datafilepath=`ls corruption-test-repo/.hg/store/00changelog*.nd`
801 841 $ f -s $datafilepath
802 842 corruption-test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
803 843 $ dd if=$datafilepath bs=1000 count=10 of=$datafilepath-tmp status=noxfer
804 844 10+0 records in
805 845 10+0 records out
806 846 $ mv $datafilepath-tmp $datafilepath
807 847 $ f -s $datafilepath
808 848 corruption-test-repo/.hg/store/00changelog-*.nd: size=10000 (glob)
809 849
810 850 Check that Mercurial reaction to this event
811 851
812 852 $ hg -R corruption-test-repo log -r . --traceback
813 853 changeset: 5005:90d5d3ba2fc4
814 854 tag: tip
815 855 user: test
816 856 date: Thu Jan 01 00:00:00 1970 +0000
817 857 summary: a2
818 858
819 859
820 860
821 861 stream clone
822 862 ============
823 863
824 864 The persistent nodemap should exist after a streaming clone
825 865
826 866 Simple case
827 867 -----------
828 868
829 869 No race condition
830 870
831 871 $ hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone --debug | egrep '00(changelog|manifest)'
832 872 adding [s] 00manifest.n (62 bytes)
833 873 adding [s] 00manifest-*.nd (118 KB) (glob)
834 874 adding [s] 00changelog.n (62 bytes)
835 875 adding [s] 00changelog-*.nd (118 KB) (glob)
836 876 adding [s] 00manifest.d (452 KB) (no-zstd !)
837 877 adding [s] 00manifest.d (491 KB) (zstd !)
838 878 adding [s] 00changelog.d (360 KB) (no-zstd !)
839 879 adding [s] 00changelog.d (368 KB) (zstd !)
840 880 adding [s] 00manifest.i (313 KB)
841 881 adding [s] 00changelog.i (313 KB)
842 882 $ ls -1 stream-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
843 883 00changelog-*.nd (glob)
844 884 00changelog.n
845 885 00manifest-*.nd (glob)
846 886 00manifest.n
847 887 $ hg -R stream-clone debugnodemap --metadata
848 888 uid: * (glob)
849 889 tip-rev: 5005
850 890 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
851 891 data-length: 121088
852 892 data-unused: 0
853 893 data-unused: 0.000%
854 894
855 895 new data appened
856 896 -----------------
857 897
858 898 Other commit happening on the server during the stream clone
859 899
860 900 setup the step-by-step stream cloning
861 901
862 902 $ HG_TEST_STREAM_WALKED_FILE_1="$TESTTMP/sync_file_walked_1"
863 903 $ export HG_TEST_STREAM_WALKED_FILE_1
864 904 $ HG_TEST_STREAM_WALKED_FILE_2="$TESTTMP/sync_file_walked_2"
865 905 $ export HG_TEST_STREAM_WALKED_FILE_2
866 906 $ HG_TEST_STREAM_WALKED_FILE_3="$TESTTMP/sync_file_walked_3"
867 907 $ export HG_TEST_STREAM_WALKED_FILE_3
868 908 $ cat << EOF >> test-repo/.hg/hgrc
869 909 > [extensions]
870 910 > steps=$RUNTESTDIR/testlib/ext-stream-clone-steps.py
871 911 > EOF
872 912
873 913 Check and record file state beforehand
874 914
875 915 $ f --size test-repo/.hg/store/00changelog*
876 916 test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
877 917 test-repo/.hg/store/00changelog.d: size=376891 (zstd !)
878 918 test-repo/.hg/store/00changelog.d: size=368890 (no-zstd !)
879 919 test-repo/.hg/store/00changelog.i: size=320384
880 920 test-repo/.hg/store/00changelog.n: size=62
881 921 $ hg -R test-repo debugnodemap --metadata | tee server-metadata.txt
882 922 uid: * (glob)
883 923 tip-rev: 5005
884 924 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
885 925 data-length: 121088
886 926 data-unused: 0
887 927 data-unused: 0.000%
888 928
889 929 Prepare a commit
890 930
891 931 $ echo foo >> test-repo/foo
892 932 $ hg -R test-repo/ add test-repo/foo
893 933
894 934 Do a mix of clone and commit at the same time so that the file listed on disk differ at actual transfer time.
895 935
896 936 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-1 --debug 2>> clone-output | egrep '00(changelog|manifest)' >> clone-output; touch $HG_TEST_STREAM_WALKED_FILE_3) &
897 937 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
898 938 $ hg -R test-repo/ commit -m foo
899 939 $ touch $HG_TEST_STREAM_WALKED_FILE_2
900 940 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
901 941 $ cat clone-output
902 942 adding [s] 00manifest.n (62 bytes)
903 943 adding [s] 00manifest-*.nd (118 KB) (glob)
904 944 adding [s] 00changelog.n (62 bytes)
905 945 adding [s] 00changelog-*.nd (118 KB) (glob)
906 946 adding [s] 00manifest.d (452 KB) (no-zstd !)
907 947 adding [s] 00manifest.d (491 KB) (zstd !)
908 948 adding [s] 00changelog.d (360 KB) (no-zstd !)
909 949 adding [s] 00changelog.d (368 KB) (zstd !)
910 950 adding [s] 00manifest.i (313 KB)
911 951 adding [s] 00changelog.i (313 KB)
912 952
913 953 Check the result state
914 954
915 955 $ f --size stream-clone-race-1/.hg/store/00changelog*
916 956 stream-clone-race-1/.hg/store/00changelog-*.nd: size=121088 (glob)
917 957 stream-clone-race-1/.hg/store/00changelog.d: size=368890 (no-zstd !)
918 958 stream-clone-race-1/.hg/store/00changelog.d: size=376891 (zstd !)
919 959 stream-clone-race-1/.hg/store/00changelog.i: size=320384
920 960 stream-clone-race-1/.hg/store/00changelog.n: size=62
921 961
922 962 $ hg -R stream-clone-race-1 debugnodemap --metadata | tee client-metadata.txt
923 963 uid: * (glob)
924 964 tip-rev: 5005
925 965 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
926 966 data-length: 121088
927 967 data-unused: 0
928 968 data-unused: 0.000%
929 969
930 970 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
931 971 (ie: the following diff should be empty)
932 972
933 973 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
934 974
935 975 #if no-rust no-pure
936 976 $ diff -u server-metadata.txt client-metadata.txt
937 977 --- server-metadata.txt * (glob)
938 978 +++ client-metadata.txt * (glob)
939 979 @@ -1,4 +1,4 @@
940 980 -uid: * (glob)
941 981 +uid: * (glob)
942 982 tip-rev: 5005
943 983 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
944 984 data-length: 121088
945 985 [1]
946 986 #else
947 987 $ diff -u server-metadata.txt client-metadata.txt
948 988 #endif
949 989
950 990
951 991 Clean up after the test.
952 992
953 993 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_1"
954 994 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_2"
955 995 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_3"
956 996
957 997 full regeneration
958 998 -----------------
959 999
960 1000 A full nodemap is generated
961 1001
962 1002 (ideally this test would append enough data to make sure the nodemap data file
963 1003 get changed, however to make thing simpler we will force the regeneration for
964 1004 this test.
965 1005
966 1006 Check the initial state
967 1007
968 1008 $ f --size test-repo/.hg/store/00changelog*
969 1009 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
970 1010 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
971 1011 test-repo/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
972 1012 test-repo/.hg/store/00changelog.d: size=376950 (zstd !)
973 1013 test-repo/.hg/store/00changelog.d: size=368949 (no-zstd !)
974 1014 test-repo/.hg/store/00changelog.i: size=320448
975 1015 test-repo/.hg/store/00changelog.n: size=62
976 1016 $ hg -R test-repo debugnodemap --metadata | tee server-metadata-2.txt
977 1017 uid: * (glob)
978 1018 tip-rev: 5006
979 1019 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
980 1020 data-length: 121344 (rust !)
981 1021 data-length: 121344 (pure !)
982 1022 data-length: 121152 (no-rust no-pure !)
983 1023 data-unused: 192 (rust !)
984 1024 data-unused: 192 (pure !)
985 1025 data-unused: 0 (no-rust no-pure !)
986 1026 data-unused: 0.158% (rust !)
987 1027 data-unused: 0.158% (pure !)
988 1028 data-unused: 0.000% (no-rust no-pure !)
989 1029
990 1030 Performe the mix of clone and full refresh of the nodemap, so that the files
991 1031 (and filenames) are different between listing time and actual transfer time.
992 1032
993 1033 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-2 --debug 2>> clone-output-2 | egrep '00(changelog|manifest)' >> clone-output-2; touch $HG_TEST_STREAM_WALKED_FILE_3) &
994 1034 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
995 1035 $ rm test-repo/.hg/store/00changelog.n
996 1036 $ rm test-repo/.hg/store/00changelog-*.nd
997 1037 $ hg -R test-repo/ debugupdatecache
998 1038 $ touch $HG_TEST_STREAM_WALKED_FILE_2
999 1039 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
1000 1040
1001 1041 (note: the stream clone code wronly pick the `undo.` files)
1002 1042
1003 1043 $ cat clone-output-2
1004 1044 adding [s] undo.backup.00manifest.n (62 bytes) (known-bad-output !)
1005 1045 adding [s] undo.backup.00changelog.n (62 bytes) (known-bad-output !)
1006 1046 adding [s] 00manifest.n (62 bytes)
1007 1047 adding [s] 00manifest-*.nd (118 KB) (glob)
1008 1048 adding [s] 00changelog.n (62 bytes)
1009 1049 adding [s] 00changelog-*.nd (118 KB) (glob)
1010 1050 adding [s] 00manifest.d (492 KB) (zstd !)
1011 1051 adding [s] 00manifest.d (452 KB) (no-zstd !)
1012 1052 adding [s] 00changelog.d (360 KB) (no-zstd !)
1013 1053 adding [s] 00changelog.d (368 KB) (zstd !)
1014 1054 adding [s] 00manifest.i (313 KB)
1015 1055 adding [s] 00changelog.i (313 KB)
1016 1056
1017 1057 Check the result.
1018 1058
1019 1059 $ f --size stream-clone-race-2/.hg/store/00changelog*
1020 1060 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1021 1061 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1022 1062 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1023 1063 stream-clone-race-2/.hg/store/00changelog.d: size=376950 (zstd !)
1024 1064 stream-clone-race-2/.hg/store/00changelog.d: size=368949 (no-zstd !)
1025 1065 stream-clone-race-2/.hg/store/00changelog.i: size=320448
1026 1066 stream-clone-race-2/.hg/store/00changelog.n: size=62
1027 1067
1028 1068 $ hg -R stream-clone-race-2 debugnodemap --metadata | tee client-metadata-2.txt
1029 1069 uid: * (glob)
1030 1070 tip-rev: 5006
1031 1071 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1032 1072 data-length: 121344 (rust !)
1033 1073 data-unused: 192 (rust !)
1034 1074 data-unused: 0.158% (rust !)
1035 1075 data-length: 121152 (no-rust no-pure !)
1036 1076 data-unused: 0 (no-rust no-pure !)
1037 1077 data-unused: 0.000% (no-rust no-pure !)
1038 1078 data-length: 121344 (pure !)
1039 1079 data-unused: 192 (pure !)
1040 1080 data-unused: 0.158% (pure !)
1041 1081
1042 1082 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1043 1083 (ie: the following diff should be empty)
1044 1084
1045 1085 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1046 1086
1047 1087 #if no-rust no-pure
1048 1088 $ diff -u server-metadata-2.txt client-metadata-2.txt
1049 1089 --- server-metadata-2.txt * (glob)
1050 1090 +++ client-metadata-2.txt * (glob)
1051 1091 @@ -1,4 +1,4 @@
1052 1092 -uid: * (glob)
1053 1093 +uid: * (glob)
1054 1094 tip-rev: 5006
1055 1095 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1056 1096 data-length: 121152
1057 1097 [1]
1058 1098 #else
1059 1099 $ diff -u server-metadata-2.txt client-metadata-2.txt
1060 1100 #endif
1061 1101
1062 1102 Clean up after the test
1063 1103
1064 1104 $ rm -f $HG_TEST_STREAM_WALKED_FILE_1
1065 1105 $ rm -f $HG_TEST_STREAM_WALKED_FILE_2
1066 1106 $ rm -f $HG_TEST_STREAM_WALKED_FILE_3
1067 1107
General Comments 0
You need to be logged in to leave comments. Login now