##// END OF EJS Templates
upgrade: support upgrade and downgrade from persistent nodemap...
marmoute -
r45356:526d69ee default
parent child Browse files
Show More
@@ -1,144 +1,157 b''
1 1 Repositories contain a file (``.hg/requires``) containing a list of
2 2 features/capabilities that are *required* for clients to interface
3 3 with the repository. This file has been present in Mercurial since
4 4 version 0.9.2 (released December 2006).
5 5
6 6 One of the first things clients do when opening a repository is read
7 7 ``.hg/requires`` and verify that all listed requirements are supported,
8 8 aborting if not. Requirements are therefore a strong mechanism to
9 9 prevent incompatible clients from reading from unknown repository
10 10 formats or even corrupting them by writing to them.
11 11
12 12 Extensions may add requirements. When they do this, clients not running
13 13 an extension will be unable to read from repositories.
14 14
15 15 The following sections describe the requirements defined by the
16 16 Mercurial core distribution.
17 17
18 18 revlogv1
19 19 ========
20 20
21 21 When present, revlogs are version 1 (RevlogNG). RevlogNG was introduced
22 22 in 2006. The ``revlogv1`` requirement has been enabled by default
23 23 since the ``requires`` file was introduced in Mercurial 0.9.2.
24 24
25 25 If this requirement is not present, version 0 revlogs are assumed.
26 26
27 27 store
28 28 =====
29 29
30 30 The *store* repository layout should be used.
31 31
32 32 This requirement has been enabled by default since the ``requires`` file
33 33 was introduced in Mercurial 0.9.2.
34 34
35 35 fncache
36 36 =======
37 37
38 38 The *fncache* repository layout should be used.
39 39
40 40 The *fncache* layout hash encodes filenames with long paths and
41 41 encodes reserved filenames.
42 42
43 43 This requirement is enabled by default when the *store* requirement is
44 44 enabled (which is the default behavior). It was introduced in Mercurial
45 45 1.1 (released December 2008).
46 46
47 47 shared
48 48 ======
49 49
50 50 Denotes that the store for a repository is shared from another location
51 51 (defined by the ``.hg/sharedpath`` file).
52 52
53 53 This requirement is set when a repository is created via :hg:`share`.
54 54
55 55 The requirement was added in Mercurial 1.3 (released July 2009).
56 56
57 57 relshared
58 58 =========
59 59
60 60 Derivative of ``shared``; the location of the store is relative to the
61 61 store of this repository.
62 62
63 63 This requirement is set when a repository is created via :hg:`share`
64 64 using the ``--relative`` option.
65 65
66 66 The requirement was added in Mercurial 4.2 (released May 2017).
67 67
68 68 dotencode
69 69 =========
70 70
71 71 The *dotencode* repository layout should be used.
72 72
73 73 The *dotencode* layout encodes the first period or space in filenames
74 74 to prevent issues on OS X and Windows.
75 75
76 76 This requirement is enabled by default when the *store* requirement
77 77 is enabled (which is the default behavior). It was introduced in
78 78 Mercurial 1.7 (released November 2010).
79 79
80 80 parentdelta
81 81 ===========
82 82
83 83 Denotes a revlog delta encoding format that was experimental and
84 84 replaced by *generaldelta*. It should not be seen in the wild because
85 85 it was never enabled by default.
86 86
87 87 This requirement was added in Mercurial 1.7 and removed in Mercurial
88 88 1.9.
89 89
90 90 generaldelta
91 91 ============
92 92
93 93 Revlogs should be created with the *generaldelta* flag enabled. The
94 94 generaldelta flag will cause deltas to be encoded against a parent
95 95 revision instead of the previous revision in the revlog.
96 96
97 97 Support for this requirement was added in Mercurial 1.9 (released
98 98 July 2011). The requirement was disabled on new repositories by
99 99 default until Mercurial 3.7 (released February 2016).
100 100
101 101 manifestv2
102 102 ==========
103 103
104 104 Denotes that version 2 of manifests are being used.
105 105
106 106 Support for this requirement was added in Mercurial 3.4 (released
107 107 May 2015). The new format failed to meet expectations and support
108 108 for the format and requirement were removed in Mercurial 4.6
109 109 (released May 2018) since the feature never graduated frome experiment
110 110 status.
111 111
112 112 treemanifest
113 113 ============
114 114
115 115 Denotes that tree manifests are being used. Tree manifests are
116 116 one manifest per directory (as opposed to a single flat manifest).
117 117
118 118 Support for this requirement was added in Mercurial 3.4 (released
119 119 August 2015). The requirement is currently experimental and is
120 120 disabled by default.
121 121
122 122 exp-sparse
123 123 ==========
124 124
125 125 The working directory is sparse (only contains a subset of files).
126 126
127 127 Support for this requirement was added in Mercurial 4.3 (released
128 128 August 2017). This requirement and feature are experimental and may
129 129 disappear in a future Mercurial release. The requirement will only
130 130 be present on repositories that have opted in to a sparse working
131 131 directory.
132 132
133 133 bookmarksinstore
134 134 ==================
135 135
136 136 Bookmarks are stored in ``.hg/store/`` instead of directly in ``.hg/``
137 137 where they used to be stored. The active bookmark is still stored
138 138 directly in ``.hg/``. This makes them always shared by ``hg share``,
139 139 whether or not ``-B`` was passed.
140 140
141 141 Support for this requirement was added in Mercurial 5.1 (released
142 142 August 2019). The requirement will only be present on repositories
143 143 that have opted in to this format (by having
144 144 ``format.bookmarks-in-store=true`` set when they were created).
145
146 persistent-nodemap
147 ==================
148
149 The `nodemap` index (mapping nodeid to local revision number) is persisted on
150 disk. This provides speed benefit (if the associated native code is used). The
151 persistent nodemap is only used for two revlogs: the changelog and the
152 manifestlog.
153
154 Support for this requirement was added in Mercurial 5.5 (released August 2020).
155 Note that as of 5.5, only installations compiled with the Rust extension will
156 benefit from a speedup. The other installations will do the necessary work to
157 keep the index up to date, but will suffer a slowdown.
@@ -1,1428 +1,1431 b''
1 1 # upgrade.py - functions for in place upgrade of Mercurial repository
2 2 #
3 3 # Copyright (c) 2016-present, Gregory Szorc
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 from __future__ import absolute_import
9 9
10 10 import stat
11 11
12 12 from .i18n import _
13 13 from .pycompat import getattr
14 14 from . import (
15 15 changelog,
16 16 copies,
17 17 error,
18 18 filelog,
19 19 hg,
20 20 localrepo,
21 21 manifest,
22 22 pycompat,
23 23 revlog,
24 24 scmutil,
25 25 util,
26 26 vfs as vfsmod,
27 27 )
28 28
29 29 from .utils import compression
30 30
31 31 # list of requirements that request a clone of all revlog if added/removed
32 32 RECLONES_REQUIREMENTS = {
33 33 b'generaldelta',
34 34 localrepo.SPARSEREVLOG_REQUIREMENT,
35 35 }
36 36
37 37
38 38 def requiredsourcerequirements(repo):
39 39 """Obtain requirements required to be present to upgrade a repo.
40 40
41 41 An upgrade will not be allowed if the repository doesn't have the
42 42 requirements returned by this function.
43 43 """
44 44 return {
45 45 # Introduced in Mercurial 0.9.2.
46 46 b'revlogv1',
47 47 # Introduced in Mercurial 0.9.2.
48 48 b'store',
49 49 }
50 50
51 51
52 52 def blocksourcerequirements(repo):
53 53 """Obtain requirements that will prevent an upgrade from occurring.
54 54
55 55 An upgrade cannot be performed if the source repository contains a
56 56 requirements in the returned set.
57 57 """
58 58 return {
59 59 # The upgrade code does not yet support these experimental features.
60 60 # This is an artificial limitation.
61 61 b'treemanifest',
62 62 # This was a precursor to generaldelta and was never enabled by default.
63 63 # It should (hopefully) not exist in the wild.
64 64 b'parentdelta',
65 65 # Upgrade should operate on the actual store, not the shared link.
66 66 b'shared',
67 67 }
68 68
69 69
70 70 def supportremovedrequirements(repo):
71 71 """Obtain requirements that can be removed during an upgrade.
72 72
73 73 If an upgrade were to create a repository that dropped a requirement,
74 74 the dropped requirement must appear in the returned set for the upgrade
75 75 to be allowed.
76 76 """
77 77 supported = {
78 78 localrepo.SPARSEREVLOG_REQUIREMENT,
79 79 localrepo.SIDEDATA_REQUIREMENT,
80 80 localrepo.COPIESSDC_REQUIREMENT,
81 localrepo.NODEMAP_REQUIREMENT,
81 82 }
82 83 for name in compression.compengines:
83 84 engine = compression.compengines[name]
84 85 if engine.available() and engine.revlogheader():
85 86 supported.add(b'exp-compression-%s' % name)
86 87 if engine.name() == b'zstd':
87 88 supported.add(b'revlog-compression-zstd')
88 89 return supported
89 90
90 91
91 92 def supporteddestrequirements(repo):
92 93 """Obtain requirements that upgrade supports in the destination.
93 94
94 95 If the result of the upgrade would create requirements not in this set,
95 96 the upgrade is disallowed.
96 97
97 98 Extensions should monkeypatch this to add their custom requirements.
98 99 """
99 100 supported = {
100 101 b'dotencode',
101 102 b'fncache',
102 103 b'generaldelta',
103 104 b'revlogv1',
104 105 b'store',
105 106 localrepo.SPARSEREVLOG_REQUIREMENT,
106 107 localrepo.SIDEDATA_REQUIREMENT,
107 108 localrepo.COPIESSDC_REQUIREMENT,
109 localrepo.NODEMAP_REQUIREMENT,
108 110 }
109 111 for name in compression.compengines:
110 112 engine = compression.compengines[name]
111 113 if engine.available() and engine.revlogheader():
112 114 supported.add(b'exp-compression-%s' % name)
113 115 if engine.name() == b'zstd':
114 116 supported.add(b'revlog-compression-zstd')
115 117 return supported
116 118
117 119
118 120 def allowednewrequirements(repo):
119 121 """Obtain requirements that can be added to a repository during upgrade.
120 122
121 123 This is used to disallow proposed requirements from being added when
122 124 they weren't present before.
123 125
124 126 We use a list of allowed requirement additions instead of a list of known
125 127 bad additions because the whitelist approach is safer and will prevent
126 128 future, unknown requirements from accidentally being added.
127 129 """
128 130 supported = {
129 131 b'dotencode',
130 132 b'fncache',
131 133 b'generaldelta',
132 134 localrepo.SPARSEREVLOG_REQUIREMENT,
133 135 localrepo.SIDEDATA_REQUIREMENT,
134 136 localrepo.COPIESSDC_REQUIREMENT,
137 localrepo.NODEMAP_REQUIREMENT,
135 138 }
136 139 for name in compression.compengines:
137 140 engine = compression.compengines[name]
138 141 if engine.available() and engine.revlogheader():
139 142 supported.add(b'exp-compression-%s' % name)
140 143 if engine.name() == b'zstd':
141 144 supported.add(b'revlog-compression-zstd')
142 145 return supported
143 146
144 147
145 148 def preservedrequirements(repo):
146 149 return set()
147 150
148 151
149 152 deficiency = b'deficiency'
150 153 optimisation = b'optimization'
151 154
152 155
153 156 class improvement(object):
154 157 """Represents an improvement that can be made as part of an upgrade.
155 158
156 159 The following attributes are defined on each instance:
157 160
158 161 name
159 162 Machine-readable string uniquely identifying this improvement. It
160 163 will be mapped to an action later in the upgrade process.
161 164
162 165 type
163 166 Either ``deficiency`` or ``optimisation``. A deficiency is an obvious
164 167 problem. An optimization is an action (sometimes optional) that
165 168 can be taken to further improve the state of the repository.
166 169
167 170 description
168 171 Message intended for humans explaining the improvement in more detail,
169 172 including the implications of it. For ``deficiency`` types, should be
170 173 worded in the present tense. For ``optimisation`` types, should be
171 174 worded in the future tense.
172 175
173 176 upgrademessage
174 177 Message intended for humans explaining what an upgrade addressing this
175 178 issue will do. Should be worded in the future tense.
176 179 """
177 180
178 181 def __init__(self, name, type, description, upgrademessage):
179 182 self.name = name
180 183 self.type = type
181 184 self.description = description
182 185 self.upgrademessage = upgrademessage
183 186
184 187 def __eq__(self, other):
185 188 if not isinstance(other, improvement):
186 189 # This is what python tell use to do
187 190 return NotImplemented
188 191 return self.name == other.name
189 192
190 193 def __ne__(self, other):
191 194 return not (self == other)
192 195
193 196 def __hash__(self):
194 197 return hash(self.name)
195 198
196 199
197 200 allformatvariant = []
198 201
199 202
200 203 def registerformatvariant(cls):
201 204 allformatvariant.append(cls)
202 205 return cls
203 206
204 207
205 208 class formatvariant(improvement):
206 209 """an improvement subclass dedicated to repository format"""
207 210
208 211 type = deficiency
209 212 ### The following attributes should be defined for each class:
210 213
211 214 # machine-readable string uniquely identifying this improvement. it will be
212 215 # mapped to an action later in the upgrade process.
213 216 name = None
214 217
215 218 # message intended for humans explaining the improvement in more detail,
216 219 # including the implications of it ``deficiency`` types, should be worded
217 220 # in the present tense.
218 221 description = None
219 222
220 223 # message intended for humans explaining what an upgrade addressing this
221 224 # issue will do. should be worded in the future tense.
222 225 upgrademessage = None
223 226
224 227 # value of current Mercurial default for new repository
225 228 default = None
226 229
227 230 def __init__(self):
228 231 raise NotImplementedError()
229 232
230 233 @staticmethod
231 234 def fromrepo(repo):
232 235 """current value of the variant in the repository"""
233 236 raise NotImplementedError()
234 237
235 238 @staticmethod
236 239 def fromconfig(repo):
237 240 """current value of the variant in the configuration"""
238 241 raise NotImplementedError()
239 242
240 243
241 244 class requirementformatvariant(formatvariant):
242 245 """formatvariant based on a 'requirement' name.
243 246
244 247 Many format variant are controlled by a 'requirement'. We define a small
245 248 subclass to factor the code.
246 249 """
247 250
248 251 # the requirement that control this format variant
249 252 _requirement = None
250 253
251 254 @staticmethod
252 255 def _newreporequirements(ui):
253 256 return localrepo.newreporequirements(
254 257 ui, localrepo.defaultcreateopts(ui)
255 258 )
256 259
257 260 @classmethod
258 261 def fromrepo(cls, repo):
259 262 assert cls._requirement is not None
260 263 return cls._requirement in repo.requirements
261 264
262 265 @classmethod
263 266 def fromconfig(cls, repo):
264 267 assert cls._requirement is not None
265 268 return cls._requirement in cls._newreporequirements(repo.ui)
266 269
267 270
268 271 @registerformatvariant
269 272 class fncache(requirementformatvariant):
270 273 name = b'fncache'
271 274
272 275 _requirement = b'fncache'
273 276
274 277 default = True
275 278
276 279 description = _(
277 280 b'long and reserved filenames may not work correctly; '
278 281 b'repository performance is sub-optimal'
279 282 )
280 283
281 284 upgrademessage = _(
282 285 b'repository will be more resilient to storing '
283 286 b'certain paths and performance of certain '
284 287 b'operations should be improved'
285 288 )
286 289
287 290
288 291 @registerformatvariant
289 292 class dotencode(requirementformatvariant):
290 293 name = b'dotencode'
291 294
292 295 _requirement = b'dotencode'
293 296
294 297 default = True
295 298
296 299 description = _(
297 300 b'storage of filenames beginning with a period or '
298 301 b'space may not work correctly'
299 302 )
300 303
301 304 upgrademessage = _(
302 305 b'repository will be better able to store files '
303 306 b'beginning with a space or period'
304 307 )
305 308
306 309
307 310 @registerformatvariant
308 311 class generaldelta(requirementformatvariant):
309 312 name = b'generaldelta'
310 313
311 314 _requirement = b'generaldelta'
312 315
313 316 default = True
314 317
315 318 description = _(
316 319 b'deltas within internal storage are unable to '
317 320 b'choose optimal revisions; repository is larger and '
318 321 b'slower than it could be; interaction with other '
319 322 b'repositories may require extra network and CPU '
320 323 b'resources, making "hg push" and "hg pull" slower'
321 324 )
322 325
323 326 upgrademessage = _(
324 327 b'repository storage will be able to create '
325 328 b'optimal deltas; new repository data will be '
326 329 b'smaller and read times should decrease; '
327 330 b'interacting with other repositories using this '
328 331 b'storage model should require less network and '
329 332 b'CPU resources, making "hg push" and "hg pull" '
330 333 b'faster'
331 334 )
332 335
333 336
334 337 @registerformatvariant
335 338 class sparserevlog(requirementformatvariant):
336 339 name = b'sparserevlog'
337 340
338 341 _requirement = localrepo.SPARSEREVLOG_REQUIREMENT
339 342
340 343 default = True
341 344
342 345 description = _(
343 346 b'in order to limit disk reading and memory usage on older '
344 347 b'version, the span of a delta chain from its root to its '
345 348 b'end is limited, whatever the relevant data in this span. '
346 349 b'This can severly limit Mercurial ability to build good '
347 350 b'chain of delta resulting is much more storage space being '
348 351 b'taken and limit reusability of on disk delta during '
349 352 b'exchange.'
350 353 )
351 354
352 355 upgrademessage = _(
353 356 b'Revlog supports delta chain with more unused data '
354 357 b'between payload. These gaps will be skipped at read '
355 358 b'time. This allows for better delta chains, making a '
356 359 b'better compression and faster exchange with server.'
357 360 )
358 361
359 362
360 363 @registerformatvariant
361 364 class sidedata(requirementformatvariant):
362 365 name = b'sidedata'
363 366
364 367 _requirement = localrepo.SIDEDATA_REQUIREMENT
365 368
366 369 default = False
367 370
368 371 description = _(
369 372 b'Allows storage of extra data alongside a revision, '
370 373 b'unlocking various caching options.'
371 374 )
372 375
373 376 upgrademessage = _(b'Allows storage of extra data alongside a revision.')
374 377
375 378
376 379 @registerformatvariant
377 380 class persistentnodemap(requirementformatvariant):
378 381 name = b'persistent-nodemap'
379 382
380 383 _requirement = localrepo.NODEMAP_REQUIREMENT
381 384
382 385 default = False
383 386
384 387 description = _(
385 388 b'persist the node -> rev mapping on disk to speedup lookup'
386 389 )
387 390
388 391 upgrademessage = _(b'Speedup revision lookup by node id.')
389 392
390 393
391 394 @registerformatvariant
392 395 class copiessdc(requirementformatvariant):
393 396 name = b'copies-sdc'
394 397
395 398 _requirement = localrepo.COPIESSDC_REQUIREMENT
396 399
397 400 default = False
398 401
399 402 description = _(b'Stores copies information alongside changesets.')
400 403
401 404 upgrademessage = _(
402 405 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
403 406 )
404 407
405 408
406 409 @registerformatvariant
407 410 class removecldeltachain(formatvariant):
408 411 name = b'plain-cl-delta'
409 412
410 413 default = True
411 414
412 415 description = _(
413 416 b'changelog storage is using deltas instead of '
414 417 b'raw entries; changelog reading and any '
415 418 b'operation relying on changelog data are slower '
416 419 b'than they could be'
417 420 )
418 421
419 422 upgrademessage = _(
420 423 b'changelog storage will be reformated to '
421 424 b'store raw entries; changelog reading will be '
422 425 b'faster; changelog size may be reduced'
423 426 )
424 427
425 428 @staticmethod
426 429 def fromrepo(repo):
427 430 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
428 431 # changelogs with deltas.
429 432 cl = repo.changelog
430 433 chainbase = cl.chainbase
431 434 return all(rev == chainbase(rev) for rev in cl)
432 435
433 436 @staticmethod
434 437 def fromconfig(repo):
435 438 return True
436 439
437 440
438 441 @registerformatvariant
439 442 class compressionengine(formatvariant):
440 443 name = b'compression'
441 444 default = b'zlib'
442 445
443 446 description = _(
444 447 b'Compresion algorithm used to compress data. '
445 448 b'Some engine are faster than other'
446 449 )
447 450
448 451 upgrademessage = _(
449 452 b'revlog content will be recompressed with the new algorithm.'
450 453 )
451 454
452 455 @classmethod
453 456 def fromrepo(cls, repo):
454 457 # we allow multiple compression engine requirement to co-exist because
455 458 # strickly speaking, revlog seems to support mixed compression style.
456 459 #
457 460 # The compression used for new entries will be "the last one"
458 461 compression = b'zlib'
459 462 for req in repo.requirements:
460 463 prefix = req.startswith
461 464 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
462 465 compression = req.split(b'-', 2)[2]
463 466 return compression
464 467
465 468 @classmethod
466 469 def fromconfig(cls, repo):
467 470 compengines = repo.ui.configlist(b'format', b'revlog-compression')
468 471 # return the first valid value as the selection code would do
469 472 for comp in compengines:
470 473 if comp in util.compengines:
471 474 return comp
472 475
473 476 # no valide compression found lets display it all for clarity
474 477 return b','.join(compengines)
475 478
476 479
477 480 @registerformatvariant
478 481 class compressionlevel(formatvariant):
479 482 name = b'compression-level'
480 483 default = b'default'
481 484
482 485 description = _(b'compression level')
483 486
484 487 upgrademessage = _(b'revlog content will be recompressed')
485 488
486 489 @classmethod
487 490 def fromrepo(cls, repo):
488 491 comp = compressionengine.fromrepo(repo)
489 492 level = None
490 493 if comp == b'zlib':
491 494 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
492 495 elif comp == b'zstd':
493 496 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
494 497 if level is None:
495 498 return b'default'
496 499 return bytes(level)
497 500
498 501 @classmethod
499 502 def fromconfig(cls, repo):
500 503 comp = compressionengine.fromconfig(repo)
501 504 level = None
502 505 if comp == b'zlib':
503 506 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
504 507 elif comp == b'zstd':
505 508 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
506 509 if level is None:
507 510 return b'default'
508 511 return bytes(level)
509 512
510 513
511 514 def finddeficiencies(repo):
512 515 """returns a list of deficiencies that the repo suffer from"""
513 516 deficiencies = []
514 517
515 518 # We could detect lack of revlogv1 and store here, but they were added
516 519 # in 0.9.2 and we don't support upgrading repos without these
517 520 # requirements, so let's not bother.
518 521
519 522 for fv in allformatvariant:
520 523 if not fv.fromrepo(repo):
521 524 deficiencies.append(fv)
522 525
523 526 return deficiencies
524 527
525 528
526 529 # search without '-' to support older form on newer client.
527 530 #
528 531 # We don't enforce backward compatibility for debug command so this
529 532 # might eventually be dropped. However, having to use two different
530 533 # forms in script when comparing result is anoying enough to add
531 534 # backward compatibility for a while.
532 535 legacy_opts_map = {
533 536 b'redeltaparent': b're-delta-parent',
534 537 b'redeltamultibase': b're-delta-multibase',
535 538 b'redeltaall': b're-delta-all',
536 539 b'redeltafulladd': b're-delta-fulladd',
537 540 }
538 541
539 542
540 543 def findoptimizations(repo):
541 544 """Determine optimisation that could be used during upgrade"""
542 545 # These are unconditionally added. There is logic later that figures out
543 546 # which ones to apply.
544 547 optimizations = []
545 548
546 549 optimizations.append(
547 550 improvement(
548 551 name=b're-delta-parent',
549 552 type=optimisation,
550 553 description=_(
551 554 b'deltas within internal storage will be recalculated to '
552 555 b'choose an optimal base revision where this was not '
553 556 b'already done; the size of the repository may shrink and '
554 557 b'various operations may become faster; the first time '
555 558 b'this optimization is performed could slow down upgrade '
556 559 b'execution considerably; subsequent invocations should '
557 560 b'not run noticeably slower'
558 561 ),
559 562 upgrademessage=_(
560 563 b'deltas within internal storage will choose a new '
561 564 b'base revision if needed'
562 565 ),
563 566 )
564 567 )
565 568
566 569 optimizations.append(
567 570 improvement(
568 571 name=b're-delta-multibase',
569 572 type=optimisation,
570 573 description=_(
571 574 b'deltas within internal storage will be recalculated '
572 575 b'against multiple base revision and the smallest '
573 576 b'difference will be used; the size of the repository may '
574 577 b'shrink significantly when there are many merges; this '
575 578 b'optimization will slow down execution in proportion to '
576 579 b'the number of merges in the repository and the amount '
577 580 b'of files in the repository; this slow down should not '
578 581 b'be significant unless there are tens of thousands of '
579 582 b'files and thousands of merges'
580 583 ),
581 584 upgrademessage=_(
582 585 b'deltas within internal storage will choose an '
583 586 b'optimal delta by computing deltas against multiple '
584 587 b'parents; may slow down execution time '
585 588 b'significantly'
586 589 ),
587 590 )
588 591 )
589 592
590 593 optimizations.append(
591 594 improvement(
592 595 name=b're-delta-all',
593 596 type=optimisation,
594 597 description=_(
595 598 b'deltas within internal storage will always be '
596 599 b'recalculated without reusing prior deltas; this will '
597 600 b'likely make execution run several times slower; this '
598 601 b'optimization is typically not needed'
599 602 ),
600 603 upgrademessage=_(
601 604 b'deltas within internal storage will be fully '
602 605 b'recomputed; this will likely drastically slow down '
603 606 b'execution time'
604 607 ),
605 608 )
606 609 )
607 610
608 611 optimizations.append(
609 612 improvement(
610 613 name=b're-delta-fulladd',
611 614 type=optimisation,
612 615 description=_(
613 616 b'every revision will be re-added as if it was new '
614 617 b'content. It will go through the full storage '
615 618 b'mechanism giving extensions a chance to process it '
616 619 b'(eg. lfs). This is similar to "re-delta-all" but even '
617 620 b'slower since more logic is involved.'
618 621 ),
619 622 upgrademessage=_(
620 623 b'each revision will be added as new content to the '
621 624 b'internal storage; this will likely drastically slow '
622 625 b'down execution time, but some extensions might need '
623 626 b'it'
624 627 ),
625 628 )
626 629 )
627 630
628 631 return optimizations
629 632
630 633
631 634 def determineactions(repo, deficiencies, sourcereqs, destreqs):
632 635 """Determine upgrade actions that will be performed.
633 636
634 637 Given a list of improvements as returned by ``finddeficiencies`` and
635 638 ``findoptimizations``, determine the list of upgrade actions that
636 639 will be performed.
637 640
638 641 The role of this function is to filter improvements if needed, apply
639 642 recommended optimizations from the improvements list that make sense,
640 643 etc.
641 644
642 645 Returns a list of action names.
643 646 """
644 647 newactions = []
645 648
646 649 for d in deficiencies:
647 650 name = d._requirement
648 651
649 652 # If the action is a requirement that doesn't show up in the
650 653 # destination requirements, prune the action.
651 654 if name is not None and name not in destreqs:
652 655 continue
653 656
654 657 newactions.append(d)
655 658
656 659 # FUTURE consider adding some optimizations here for certain transitions.
657 660 # e.g. adding generaldelta could schedule parent redeltas.
658 661
659 662 return newactions
660 663
661 664
662 665 def _revlogfrompath(repo, path):
663 666 """Obtain a revlog from a repo path.
664 667
665 668 An instance of the appropriate class is returned.
666 669 """
667 670 if path == b'00changelog.i':
668 671 return changelog.changelog(repo.svfs)
669 672 elif path.endswith(b'00manifest.i'):
670 673 mandir = path[: -len(b'00manifest.i')]
671 674 return manifest.manifestrevlog(repo.svfs, tree=mandir)
672 675 else:
673 676 # reverse of "/".join(("data", path + ".i"))
674 677 return filelog.filelog(repo.svfs, path[5:-2])
675 678
676 679
677 680 def _copyrevlog(tr, destrepo, oldrl, unencodedname):
678 681 """copy all relevant files for `oldrl` into `destrepo` store
679 682
680 683 Files are copied "as is" without any transformation. The copy is performed
681 684 without extra checks. Callers are responsible for making sure the copied
682 685 content is compatible with format of the destination repository.
683 686 """
684 687 oldrl = getattr(oldrl, '_revlog', oldrl)
685 688 newrl = _revlogfrompath(destrepo, unencodedname)
686 689 newrl = getattr(newrl, '_revlog', newrl)
687 690
688 691 oldvfs = oldrl.opener
689 692 newvfs = newrl.opener
690 693 oldindex = oldvfs.join(oldrl.indexfile)
691 694 newindex = newvfs.join(newrl.indexfile)
692 695 olddata = oldvfs.join(oldrl.datafile)
693 696 newdata = newvfs.join(newrl.datafile)
694 697
695 698 with newvfs(newrl.indexfile, b'w'):
696 699 pass # create all the directories
697 700
698 701 util.copyfile(oldindex, newindex)
699 702 copydata = oldrl.opener.exists(oldrl.datafile)
700 703 if copydata:
701 704 util.copyfile(olddata, newdata)
702 705
703 706 if not (
704 707 unencodedname.endswith(b'00changelog.i')
705 708 or unencodedname.endswith(b'00manifest.i')
706 709 ):
707 710 destrepo.svfs.fncache.add(unencodedname)
708 711 if copydata:
709 712 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
710 713
711 714
712 715 UPGRADE_CHANGELOG = object()
713 716 UPGRADE_MANIFEST = object()
714 717 UPGRADE_FILELOG = object()
715 718
716 719 UPGRADE_ALL_REVLOGS = frozenset(
717 720 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOG]
718 721 )
719 722
720 723
721 724 def getsidedatacompanion(srcrepo, dstrepo):
722 725 sidedatacompanion = None
723 726 removedreqs = srcrepo.requirements - dstrepo.requirements
724 727 addedreqs = dstrepo.requirements - srcrepo.requirements
725 728 if localrepo.SIDEDATA_REQUIREMENT in removedreqs:
726 729
727 730 def sidedatacompanion(rl, rev):
728 731 rl = getattr(rl, '_revlog', rl)
729 732 if rl.flags(rev) & revlog.REVIDX_SIDEDATA:
730 733 return True, (), {}
731 734 return False, (), {}
732 735
733 736 elif localrepo.COPIESSDC_REQUIREMENT in addedreqs:
734 737 sidedatacompanion = copies.getsidedataadder(srcrepo, dstrepo)
735 738 elif localrepo.COPIESSDC_REQUIREMENT in removedreqs:
736 739 sidedatacompanion = copies.getsidedataremover(srcrepo, dstrepo)
737 740 return sidedatacompanion
738 741
739 742
740 743 def matchrevlog(revlogfilter, entry):
741 744 """check is a revlog is selected for cloning
742 745
743 746 The store entry is checked against the passed filter"""
744 747 if entry.endswith(b'00changelog.i'):
745 748 return UPGRADE_CHANGELOG in revlogfilter
746 749 elif entry.endswith(b'00manifest.i'):
747 750 return UPGRADE_MANIFEST in revlogfilter
748 751 return UPGRADE_FILELOG in revlogfilter
749 752
750 753
751 754 def _clonerevlogs(
752 755 ui,
753 756 srcrepo,
754 757 dstrepo,
755 758 tr,
756 759 deltareuse,
757 760 forcedeltabothparents,
758 761 revlogs=UPGRADE_ALL_REVLOGS,
759 762 ):
760 763 """Copy revlogs between 2 repos."""
761 764 revcount = 0
762 765 srcsize = 0
763 766 srcrawsize = 0
764 767 dstsize = 0
765 768 fcount = 0
766 769 frevcount = 0
767 770 fsrcsize = 0
768 771 frawsize = 0
769 772 fdstsize = 0
770 773 mcount = 0
771 774 mrevcount = 0
772 775 msrcsize = 0
773 776 mrawsize = 0
774 777 mdstsize = 0
775 778 crevcount = 0
776 779 csrcsize = 0
777 780 crawsize = 0
778 781 cdstsize = 0
779 782
780 783 alldatafiles = list(srcrepo.store.walk())
781 784
782 785 # Perform a pass to collect metadata. This validates we can open all
783 786 # source files and allows a unified progress bar to be displayed.
784 787 for unencoded, encoded, size in alldatafiles:
785 788 if unencoded.endswith(b'.d'):
786 789 continue
787 790
788 791 rl = _revlogfrompath(srcrepo, unencoded)
789 792
790 793 info = rl.storageinfo(
791 794 exclusivefiles=True,
792 795 revisionscount=True,
793 796 trackedsize=True,
794 797 storedsize=True,
795 798 )
796 799
797 800 revcount += info[b'revisionscount'] or 0
798 801 datasize = info[b'storedsize'] or 0
799 802 rawsize = info[b'trackedsize'] or 0
800 803
801 804 srcsize += datasize
802 805 srcrawsize += rawsize
803 806
804 807 # This is for the separate progress bars.
805 808 if isinstance(rl, changelog.changelog):
806 809 crevcount += len(rl)
807 810 csrcsize += datasize
808 811 crawsize += rawsize
809 812 elif isinstance(rl, manifest.manifestrevlog):
810 813 mcount += 1
811 814 mrevcount += len(rl)
812 815 msrcsize += datasize
813 816 mrawsize += rawsize
814 817 elif isinstance(rl, filelog.filelog):
815 818 fcount += 1
816 819 frevcount += len(rl)
817 820 fsrcsize += datasize
818 821 frawsize += rawsize
819 822 else:
820 823 error.ProgrammingError(b'unknown revlog type')
821 824
822 825 if not revcount:
823 826 return
824 827
825 828 ui.status(
826 829 _(
827 830 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
828 831 b'%d in changelog)\n'
829 832 )
830 833 % (revcount, frevcount, mrevcount, crevcount)
831 834 )
832 835 ui.status(
833 836 _(b'migrating %s in store; %s tracked data\n')
834 837 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
835 838 )
836 839
837 840 # Used to keep track of progress.
838 841 progress = None
839 842
840 843 def oncopiedrevision(rl, rev, node):
841 844 progress.increment()
842 845
843 846 sidedatacompanion = getsidedatacompanion(srcrepo, dstrepo)
844 847
845 848 # Do the actual copying.
846 849 # FUTURE this operation can be farmed off to worker processes.
847 850 seen = set()
848 851 for unencoded, encoded, size in alldatafiles:
849 852 if unencoded.endswith(b'.d'):
850 853 continue
851 854
852 855 oldrl = _revlogfrompath(srcrepo, unencoded)
853 856
854 857 if isinstance(oldrl, changelog.changelog) and b'c' not in seen:
855 858 ui.status(
856 859 _(
857 860 b'finished migrating %d manifest revisions across %d '
858 861 b'manifests; change in size: %s\n'
859 862 )
860 863 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
861 864 )
862 865
863 866 ui.status(
864 867 _(
865 868 b'migrating changelog containing %d revisions '
866 869 b'(%s in store; %s tracked data)\n'
867 870 )
868 871 % (
869 872 crevcount,
870 873 util.bytecount(csrcsize),
871 874 util.bytecount(crawsize),
872 875 )
873 876 )
874 877 seen.add(b'c')
875 878 progress = srcrepo.ui.makeprogress(
876 879 _(b'changelog revisions'), total=crevcount
877 880 )
878 881 elif isinstance(oldrl, manifest.manifestrevlog) and b'm' not in seen:
879 882 ui.status(
880 883 _(
881 884 b'finished migrating %d filelog revisions across %d '
882 885 b'filelogs; change in size: %s\n'
883 886 )
884 887 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
885 888 )
886 889
887 890 ui.status(
888 891 _(
889 892 b'migrating %d manifests containing %d revisions '
890 893 b'(%s in store; %s tracked data)\n'
891 894 )
892 895 % (
893 896 mcount,
894 897 mrevcount,
895 898 util.bytecount(msrcsize),
896 899 util.bytecount(mrawsize),
897 900 )
898 901 )
899 902 seen.add(b'm')
900 903 if progress:
901 904 progress.complete()
902 905 progress = srcrepo.ui.makeprogress(
903 906 _(b'manifest revisions'), total=mrevcount
904 907 )
905 908 elif b'f' not in seen:
906 909 ui.status(
907 910 _(
908 911 b'migrating %d filelogs containing %d revisions '
909 912 b'(%s in store; %s tracked data)\n'
910 913 )
911 914 % (
912 915 fcount,
913 916 frevcount,
914 917 util.bytecount(fsrcsize),
915 918 util.bytecount(frawsize),
916 919 )
917 920 )
918 921 seen.add(b'f')
919 922 if progress:
920 923 progress.complete()
921 924 progress = srcrepo.ui.makeprogress(
922 925 _(b'file revisions'), total=frevcount
923 926 )
924 927
925 928 if matchrevlog(revlogs, unencoded):
926 929 ui.note(
927 930 _(b'cloning %d revisions from %s\n') % (len(oldrl), unencoded)
928 931 )
929 932 newrl = _revlogfrompath(dstrepo, unencoded)
930 933 oldrl.clone(
931 934 tr,
932 935 newrl,
933 936 addrevisioncb=oncopiedrevision,
934 937 deltareuse=deltareuse,
935 938 forcedeltabothparents=forcedeltabothparents,
936 939 sidedatacompanion=sidedatacompanion,
937 940 )
938 941 else:
939 942 msg = _(b'blindly copying %s containing %i revisions\n')
940 943 ui.note(msg % (unencoded, len(oldrl)))
941 944 _copyrevlog(tr, dstrepo, oldrl, unencoded)
942 945
943 946 newrl = _revlogfrompath(dstrepo, unencoded)
944 947
945 948 info = newrl.storageinfo(storedsize=True)
946 949 datasize = info[b'storedsize'] or 0
947 950
948 951 dstsize += datasize
949 952
950 953 if isinstance(newrl, changelog.changelog):
951 954 cdstsize += datasize
952 955 elif isinstance(newrl, manifest.manifestrevlog):
953 956 mdstsize += datasize
954 957 else:
955 958 fdstsize += datasize
956 959
957 960 progress.complete()
958 961
959 962 ui.status(
960 963 _(
961 964 b'finished migrating %d changelog revisions; change in size: '
962 965 b'%s\n'
963 966 )
964 967 % (crevcount, util.bytecount(cdstsize - csrcsize))
965 968 )
966 969
967 970 ui.status(
968 971 _(
969 972 b'finished migrating %d total revisions; total change in store '
970 973 b'size: %s\n'
971 974 )
972 975 % (revcount, util.bytecount(dstsize - srcsize))
973 976 )
974 977
975 978
976 979 def _filterstorefile(srcrepo, dstrepo, requirements, path, mode, st):
977 980 """Determine whether to copy a store file during upgrade.
978 981
979 982 This function is called when migrating store files from ``srcrepo`` to
980 983 ``dstrepo`` as part of upgrading a repository.
981 984
982 985 Args:
983 986 srcrepo: repo we are copying from
984 987 dstrepo: repo we are copying to
985 988 requirements: set of requirements for ``dstrepo``
986 989 path: store file being examined
987 990 mode: the ``ST_MODE`` file type of ``path``
988 991 st: ``stat`` data structure for ``path``
989 992
990 993 Function should return ``True`` if the file is to be copied.
991 994 """
992 995 # Skip revlogs.
993 if path.endswith((b'.i', b'.d')):
996 if path.endswith((b'.i', b'.d', b'.n', b'.nd')):
994 997 return False
995 998 # Skip transaction related files.
996 999 if path.startswith(b'undo'):
997 1000 return False
998 1001 # Only copy regular files.
999 1002 if mode != stat.S_IFREG:
1000 1003 return False
1001 1004 # Skip other skipped files.
1002 1005 if path in (b'lock', b'fncache'):
1003 1006 return False
1004 1007
1005 1008 return True
1006 1009
1007 1010
1008 1011 def _finishdatamigration(ui, srcrepo, dstrepo, requirements):
1009 1012 """Hook point for extensions to perform additional actions during upgrade.
1010 1013
1011 1014 This function is called after revlogs and store files have been copied but
1012 1015 before the new store is swapped into the original location.
1013 1016 """
1014 1017
1015 1018
1016 1019 def _upgraderepo(
1017 1020 ui, srcrepo, dstrepo, requirements, actions, revlogs=UPGRADE_ALL_REVLOGS
1018 1021 ):
1019 1022 """Do the low-level work of upgrading a repository.
1020 1023
1021 1024 The upgrade is effectively performed as a copy between a source
1022 1025 repository and a temporary destination repository.
1023 1026
1024 1027 The source repository is unmodified for as long as possible so the
1025 1028 upgrade can abort at any time without causing loss of service for
1026 1029 readers and without corrupting the source repository.
1027 1030 """
1028 1031 assert srcrepo.currentwlock()
1029 1032 assert dstrepo.currentwlock()
1030 1033
1031 1034 ui.status(
1032 1035 _(
1033 1036 b'(it is safe to interrupt this process any time before '
1034 1037 b'data migration completes)\n'
1035 1038 )
1036 1039 )
1037 1040
1038 1041 if b're-delta-all' in actions:
1039 1042 deltareuse = revlog.revlog.DELTAREUSENEVER
1040 1043 elif b're-delta-parent' in actions:
1041 1044 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1042 1045 elif b're-delta-multibase' in actions:
1043 1046 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1044 1047 elif b're-delta-fulladd' in actions:
1045 1048 deltareuse = revlog.revlog.DELTAREUSEFULLADD
1046 1049 else:
1047 1050 deltareuse = revlog.revlog.DELTAREUSEALWAYS
1048 1051
1049 1052 with dstrepo.transaction(b'upgrade') as tr:
1050 1053 _clonerevlogs(
1051 1054 ui,
1052 1055 srcrepo,
1053 1056 dstrepo,
1054 1057 tr,
1055 1058 deltareuse,
1056 1059 b're-delta-multibase' in actions,
1057 1060 revlogs=revlogs,
1058 1061 )
1059 1062
1060 1063 # Now copy other files in the store directory.
1061 1064 # The sorted() makes execution deterministic.
1062 1065 for p, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
1063 1066 if not _filterstorefile(srcrepo, dstrepo, requirements, p, kind, st):
1064 1067 continue
1065 1068
1066 1069 srcrepo.ui.status(_(b'copying %s\n') % p)
1067 1070 src = srcrepo.store.rawvfs.join(p)
1068 1071 dst = dstrepo.store.rawvfs.join(p)
1069 1072 util.copyfile(src, dst, copystat=True)
1070 1073
1071 1074 _finishdatamigration(ui, srcrepo, dstrepo, requirements)
1072 1075
1073 1076 ui.status(_(b'data fully migrated to temporary repository\n'))
1074 1077
1075 1078 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
1076 1079 backupvfs = vfsmod.vfs(backuppath)
1077 1080
1078 1081 # Make a backup of requires file first, as it is the first to be modified.
1079 1082 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
1080 1083
1081 1084 # We install an arbitrary requirement that clients must not support
1082 1085 # as a mechanism to lock out new clients during the data swap. This is
1083 1086 # better than allowing a client to continue while the repository is in
1084 1087 # an inconsistent state.
1085 1088 ui.status(
1086 1089 _(
1087 1090 b'marking source repository as being upgraded; clients will be '
1088 1091 b'unable to read from repository\n'
1089 1092 )
1090 1093 )
1091 1094 scmutil.writerequires(
1092 1095 srcrepo.vfs, srcrepo.requirements | {b'upgradeinprogress'}
1093 1096 )
1094 1097
1095 1098 ui.status(_(b'starting in-place swap of repository data\n'))
1096 1099 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
1097 1100
1098 1101 # Now swap in the new store directory. Doing it as a rename should make
1099 1102 # the operation nearly instantaneous and atomic (at least in well-behaved
1100 1103 # environments).
1101 1104 ui.status(_(b'replacing store...\n'))
1102 1105 tstart = util.timer()
1103 1106 util.rename(srcrepo.spath, backupvfs.join(b'store'))
1104 1107 util.rename(dstrepo.spath, srcrepo.spath)
1105 1108 elapsed = util.timer() - tstart
1106 1109 ui.status(
1107 1110 _(
1108 1111 b'store replacement complete; repository was inconsistent for '
1109 1112 b'%0.1fs\n'
1110 1113 )
1111 1114 % elapsed
1112 1115 )
1113 1116
1114 1117 # We first write the requirements file. Any new requirements will lock
1115 1118 # out legacy clients.
1116 1119 ui.status(
1117 1120 _(
1118 1121 b'finalizing requirements file and making repository readable '
1119 1122 b'again\n'
1120 1123 )
1121 1124 )
1122 1125 scmutil.writerequires(srcrepo.vfs, requirements)
1123 1126
1124 1127 # The lock file from the old store won't be removed because nothing has a
1125 1128 # reference to its new location. So clean it up manually. Alternatively, we
1126 1129 # could update srcrepo.svfs and other variables to point to the new
1127 1130 # location. This is simpler.
1128 1131 backupvfs.unlink(b'store/lock')
1129 1132
1130 1133 return backuppath
1131 1134
1132 1135
1133 1136 def upgraderepo(
1134 1137 ui,
1135 1138 repo,
1136 1139 run=False,
1137 1140 optimize=None,
1138 1141 backup=True,
1139 1142 manifest=None,
1140 1143 changelog=None,
1141 1144 ):
1142 1145 """Upgrade a repository in place."""
1143 1146 if optimize is None:
1144 1147 optimize = []
1145 1148 optimize = {legacy_opts_map.get(o, o) for o in optimize}
1146 1149 repo = repo.unfiltered()
1147 1150
1148 1151 revlogs = set(UPGRADE_ALL_REVLOGS)
1149 1152 specentries = ((b'c', changelog), (b'm', manifest))
1150 1153 specified = [(y, x) for (y, x) in specentries if x is not None]
1151 1154 if specified:
1152 1155 # we have some limitation on revlogs to be recloned
1153 1156 if any(x for y, x in specified):
1154 1157 revlogs = set()
1155 1158 for r, enabled in specified:
1156 1159 if enabled:
1157 1160 if r == b'c':
1158 1161 revlogs.add(UPGRADE_CHANGELOG)
1159 1162 elif r == b'm':
1160 1163 revlogs.add(UPGRADE_MANIFEST)
1161 1164 else:
1162 1165 # none are enabled
1163 1166 for r, __ in specified:
1164 1167 if r == b'c':
1165 1168 revlogs.discard(UPGRADE_CHANGELOG)
1166 1169 elif r == b'm':
1167 1170 revlogs.discard(UPGRADE_MANIFEST)
1168 1171
1169 1172 # Ensure the repository can be upgraded.
1170 1173 missingreqs = requiredsourcerequirements(repo) - repo.requirements
1171 1174 if missingreqs:
1172 1175 raise error.Abort(
1173 1176 _(b'cannot upgrade repository; requirement missing: %s')
1174 1177 % _(b', ').join(sorted(missingreqs))
1175 1178 )
1176 1179
1177 1180 blockedreqs = blocksourcerequirements(repo) & repo.requirements
1178 1181 if blockedreqs:
1179 1182 raise error.Abort(
1180 1183 _(
1181 1184 b'cannot upgrade repository; unsupported source '
1182 1185 b'requirement: %s'
1183 1186 )
1184 1187 % _(b', ').join(sorted(blockedreqs))
1185 1188 )
1186 1189
1187 1190 # FUTURE there is potentially a need to control the wanted requirements via
1188 1191 # command arguments or via an extension hook point.
1189 1192 newreqs = localrepo.newreporequirements(
1190 1193 repo.ui, localrepo.defaultcreateopts(repo.ui)
1191 1194 )
1192 1195 newreqs.update(preservedrequirements(repo))
1193 1196
1194 1197 noremovereqs = (
1195 1198 repo.requirements - newreqs - supportremovedrequirements(repo)
1196 1199 )
1197 1200 if noremovereqs:
1198 1201 raise error.Abort(
1199 1202 _(
1200 1203 b'cannot upgrade repository; requirement would be '
1201 1204 b'removed: %s'
1202 1205 )
1203 1206 % _(b', ').join(sorted(noremovereqs))
1204 1207 )
1205 1208
1206 1209 noaddreqs = newreqs - repo.requirements - allowednewrequirements(repo)
1207 1210 if noaddreqs:
1208 1211 raise error.Abort(
1209 1212 _(
1210 1213 b'cannot upgrade repository; do not support adding '
1211 1214 b'requirement: %s'
1212 1215 )
1213 1216 % _(b', ').join(sorted(noaddreqs))
1214 1217 )
1215 1218
1216 1219 unsupportedreqs = newreqs - supporteddestrequirements(repo)
1217 1220 if unsupportedreqs:
1218 1221 raise error.Abort(
1219 1222 _(
1220 1223 b'cannot upgrade repository; do not support '
1221 1224 b'destination requirement: %s'
1222 1225 )
1223 1226 % _(b', ').join(sorted(unsupportedreqs))
1224 1227 )
1225 1228
1226 1229 # Find and validate all improvements that can be made.
1227 1230 alloptimizations = findoptimizations(repo)
1228 1231
1229 1232 # Apply and Validate arguments.
1230 1233 optimizations = []
1231 1234 for o in alloptimizations:
1232 1235 if o.name in optimize:
1233 1236 optimizations.append(o)
1234 1237 optimize.discard(o.name)
1235 1238
1236 1239 if optimize: # anything left is unknown
1237 1240 raise error.Abort(
1238 1241 _(b'unknown optimization action requested: %s')
1239 1242 % b', '.join(sorted(optimize)),
1240 1243 hint=_(b'run without arguments to see valid optimizations'),
1241 1244 )
1242 1245
1243 1246 deficiencies = finddeficiencies(repo)
1244 1247 actions = determineactions(repo, deficiencies, repo.requirements, newreqs)
1245 1248 actions.extend(
1246 1249 o
1247 1250 for o in sorted(optimizations)
1248 1251 # determineactions could have added optimisation
1249 1252 if o not in actions
1250 1253 )
1251 1254
1252 1255 removedreqs = repo.requirements - newreqs
1253 1256 addedreqs = newreqs - repo.requirements
1254 1257
1255 1258 if revlogs != UPGRADE_ALL_REVLOGS:
1256 1259 incompatible = RECLONES_REQUIREMENTS & (removedreqs | addedreqs)
1257 1260 if incompatible:
1258 1261 msg = _(
1259 1262 b'ignoring revlogs selection flags, format requirements '
1260 1263 b'change: %s\n'
1261 1264 )
1262 1265 ui.warn(msg % b', '.join(sorted(incompatible)))
1263 1266 revlogs = UPGRADE_ALL_REVLOGS
1264 1267
1265 1268 def write_labeled(l, label):
1266 1269 first = True
1267 1270 for r in sorted(l):
1268 1271 if not first:
1269 1272 ui.write(b', ')
1270 1273 ui.write(r, label=label)
1271 1274 first = False
1272 1275
1273 1276 def printrequirements():
1274 1277 ui.write(_(b'requirements\n'))
1275 1278 ui.write(_(b' preserved: '))
1276 1279 write_labeled(
1277 1280 newreqs & repo.requirements, "upgrade-repo.requirement.preserved"
1278 1281 )
1279 1282 ui.write((b'\n'))
1280 1283 removed = repo.requirements - newreqs
1281 1284 if repo.requirements - newreqs:
1282 1285 ui.write(_(b' removed: '))
1283 1286 write_labeled(removed, "upgrade-repo.requirement.removed")
1284 1287 ui.write((b'\n'))
1285 1288 added = newreqs - repo.requirements
1286 1289 if added:
1287 1290 ui.write(_(b' added: '))
1288 1291 write_labeled(added, "upgrade-repo.requirement.added")
1289 1292 ui.write((b'\n'))
1290 1293 ui.write(b'\n')
1291 1294
1292 1295 def printoptimisations():
1293 1296 optimisations = [a for a in actions if a.type == optimisation]
1294 1297 optimisations.sort(key=lambda a: a.name)
1295 1298 if optimisations:
1296 1299 ui.write(_(b'optimisations: '))
1297 1300 write_labeled(
1298 1301 [a.name for a in optimisations],
1299 1302 "upgrade-repo.optimisation.performed",
1300 1303 )
1301 1304 ui.write(b'\n\n')
1302 1305
1303 1306 def printupgradeactions():
1304 1307 for a in actions:
1305 1308 ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
1306 1309
1307 1310 if not run:
1308 1311 fromconfig = []
1309 1312 onlydefault = []
1310 1313
1311 1314 for d in deficiencies:
1312 1315 if d.fromconfig(repo):
1313 1316 fromconfig.append(d)
1314 1317 elif d.default:
1315 1318 onlydefault.append(d)
1316 1319
1317 1320 if fromconfig or onlydefault:
1318 1321
1319 1322 if fromconfig:
1320 1323 ui.status(
1321 1324 _(
1322 1325 b'repository lacks features recommended by '
1323 1326 b'current config options:\n\n'
1324 1327 )
1325 1328 )
1326 1329 for i in fromconfig:
1327 1330 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
1328 1331
1329 1332 if onlydefault:
1330 1333 ui.status(
1331 1334 _(
1332 1335 b'repository lacks features used by the default '
1333 1336 b'config options:\n\n'
1334 1337 )
1335 1338 )
1336 1339 for i in onlydefault:
1337 1340 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
1338 1341
1339 1342 ui.status(b'\n')
1340 1343 else:
1341 1344 ui.status(
1342 1345 _(
1343 1346 b'(no feature deficiencies found in existing '
1344 1347 b'repository)\n'
1345 1348 )
1346 1349 )
1347 1350
1348 1351 ui.status(
1349 1352 _(
1350 1353 b'performing an upgrade with "--run" will make the following '
1351 1354 b'changes:\n\n'
1352 1355 )
1353 1356 )
1354 1357
1355 1358 printrequirements()
1356 1359 printoptimisations()
1357 1360 printupgradeactions()
1358 1361
1359 1362 unusedoptimize = [i for i in alloptimizations if i not in actions]
1360 1363
1361 1364 if unusedoptimize:
1362 1365 ui.status(
1363 1366 _(
1364 1367 b'additional optimizations are available by specifying '
1365 1368 b'"--optimize <name>":\n\n'
1366 1369 )
1367 1370 )
1368 1371 for i in unusedoptimize:
1369 1372 ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
1370 1373 return
1371 1374
1372 1375 # Else we're in the run=true case.
1373 1376 ui.write(_(b'upgrade will perform the following actions:\n\n'))
1374 1377 printrequirements()
1375 1378 printoptimisations()
1376 1379 printupgradeactions()
1377 1380
1378 1381 upgradeactions = [a.name for a in actions]
1379 1382
1380 1383 ui.status(_(b'beginning upgrade...\n'))
1381 1384 with repo.wlock(), repo.lock():
1382 1385 ui.status(_(b'repository locked and read-only\n'))
1383 1386 # Our strategy for upgrading the repository is to create a new,
1384 1387 # temporary repository, write data to it, then do a swap of the
1385 1388 # data. There are less heavyweight ways to do this, but it is easier
1386 1389 # to create a new repo object than to instantiate all the components
1387 1390 # (like the store) separately.
1388 1391 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
1389 1392 backuppath = None
1390 1393 try:
1391 1394 ui.status(
1392 1395 _(
1393 1396 b'creating temporary repository to stage migrated '
1394 1397 b'data: %s\n'
1395 1398 )
1396 1399 % tmppath
1397 1400 )
1398 1401
1399 1402 # clone ui without using ui.copy because repo.ui is protected
1400 1403 repoui = repo.ui.__class__(repo.ui)
1401 1404 dstrepo = hg.repository(repoui, path=tmppath, create=True)
1402 1405
1403 1406 with dstrepo.wlock(), dstrepo.lock():
1404 1407 backuppath = _upgraderepo(
1405 1408 ui, repo, dstrepo, newreqs, upgradeactions, revlogs=revlogs
1406 1409 )
1407 1410 if not (backup or backuppath is None):
1408 1411 ui.status(
1409 1412 _(b'removing old repository content%s\n') % backuppath
1410 1413 )
1411 1414 repo.vfs.rmtree(backuppath, forcibly=True)
1412 1415 backuppath = None
1413 1416
1414 1417 finally:
1415 1418 ui.status(_(b'removing temporary repository %s\n') % tmppath)
1416 1419 repo.vfs.rmtree(tmppath, forcibly=True)
1417 1420
1418 1421 if backuppath and not ui.quiet:
1419 1422 ui.warn(
1420 1423 _(b'copy of old repository backed up at %s\n') % backuppath
1421 1424 )
1422 1425 ui.warn(
1423 1426 _(
1424 1427 b'the old repository will not be deleted; remove '
1425 1428 b'it to free up disk space once the upgraded '
1426 1429 b'repository is verified\n'
1427 1430 )
1428 1431 )
@@ -1,441 +1,537 b''
1 1 ===================================
2 2 Test the persistent on-disk nodemap
3 3 ===================================
4 4
5 5 $ cat << EOF >> $HGRCPATH
6 6 > [format]
7 7 > use-persistent-nodemap=yes
8 8 > [devel]
9 9 > persistent-nodemap=yes
10 10 > EOF
11 11 $ hg init test-repo
12 12 $ cd test-repo
13 13 $ hg debugformat
14 14 format-variant repo
15 15 fncache: yes
16 16 dotencode: yes
17 17 generaldelta: yes
18 18 sparserevlog: yes
19 19 sidedata: no
20 20 persistent-nodemap: yes
21 21 copies-sdc: no
22 22 plain-cl-delta: yes
23 23 compression: zlib
24 24 compression-level: default
25 25 $ hg debugbuilddag .+5000 --new-file --config "storage.revlog.nodemap.mode=warn"
26 26 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
27 27 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
28 28 $ hg debugnodemap --metadata
29 29 uid: ???????????????? (glob)
30 30 tip-rev: 5000
31 31 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
32 32 data-length: 121088
33 33 data-unused: 0
34 34 data-unused: 0.000%
35 35 $ f --size .hg/store/00changelog.n
36 36 .hg/store/00changelog.n: size=70
37 37
38 38 Simple lookup works
39 39
40 40 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
41 41 $ hg log -r "$ANYNODE" --template '{rev}\n'
42 42 5000
43 43
44 44
45 45 #if rust
46 46
47 47 $ f --sha256 .hg/store/00changelog-*.nd
48 48 .hg/store/00changelog-????????????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
49 49
50 50 $ f --sha256 .hg/store/00manifest-*.nd
51 51 .hg/store/00manifest-????????????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
52 52 $ hg debugnodemap --dump-new | f --sha256 --size
53 53 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
54 54 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
55 55 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
56 56 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
57 57 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
58 58 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
59 59 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
60 60 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
61 61 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
62 62 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
63 63 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
64 64 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
65 65 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
66 66 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
67 67 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
68 68 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
69 69 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
70 70 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
71 71 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
72 72
73 73
74 74 #else
75 75
76 76 $ f --sha256 .hg/store/00changelog-*.nd
77 77 .hg/store/00changelog-????????????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
78 78 $ hg debugnodemap --dump-new | f --sha256 --size
79 79 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
80 80 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
81 81 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
82 82 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
83 83 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
84 84 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
85 85 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
86 86 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
87 87 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
88 88 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
89 89 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
90 90 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
91 91 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
92 92 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
93 93 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
94 94 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
95 95 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
96 96 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
97 97 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
98 98
99 99 #endif
100 100
101 101 $ hg debugnodemap --check
102 102 revision in index: 5001
103 103 revision in nodemap: 5001
104 104
105 105 add a new commit
106 106
107 107 $ hg up
108 108 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109 $ echo foo > foo
110 110 $ hg add foo
111 111
112 112 #if no-pure no-rust
113 113
114 114 $ hg ci -m 'foo' --config "storage.revlog.nodemap.mode=strict"
115 115 transaction abort!
116 116 rollback completed
117 117 abort: persistent nodemap in strict mode without efficient method
118 118 [255]
119 119
120 120 #endif
121 121
122 122 $ hg ci -m 'foo'
123 123
124 124 #if no-pure no-rust
125 125 $ hg debugnodemap --metadata
126 126 uid: ???????????????? (glob)
127 127 tip-rev: 5001
128 128 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
129 129 data-length: 121088
130 130 data-unused: 0
131 131 data-unused: 0.000%
132 132 #else
133 133 $ hg debugnodemap --metadata
134 134 uid: ???????????????? (glob)
135 135 tip-rev: 5001
136 136 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
137 137 data-length: 121344
138 138 data-unused: 256
139 139 data-unused: 0.211%
140 140 #endif
141 141
142 142 $ f --size .hg/store/00changelog.n
143 143 .hg/store/00changelog.n: size=70
144 144
145 145 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
146 146
147 147 #if pure
148 148 $ f --sha256 .hg/store/00changelog-*.nd --size
149 149 .hg/store/00changelog-????????????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
150 150 #endif
151 151
152 152 #if rust
153 153 $ f --sha256 .hg/store/00changelog-*.nd --size
154 154 .hg/store/00changelog-????????????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
155 155 #endif
156 156
157 157 #if no-pure no-rust
158 158 $ f --sha256 .hg/store/00changelog-*.nd --size
159 159 .hg/store/00changelog-????????????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
160 160 #endif
161 161
162 162 $ hg debugnodemap --check
163 163 revision in index: 5002
164 164 revision in nodemap: 5002
165 165
166 166 Test code path without mmap
167 167 ---------------------------
168 168
169 169 $ echo bar > bar
170 170 $ hg add bar
171 171 $ hg ci -m 'bar' --config storage.revlog.nodemap.mmap=no
172 172
173 173 $ hg debugnodemap --check --config storage.revlog.nodemap.mmap=yes
174 174 revision in index: 5003
175 175 revision in nodemap: 5003
176 176 $ hg debugnodemap --check --config storage.revlog.nodemap.mmap=no
177 177 revision in index: 5003
178 178 revision in nodemap: 5003
179 179
180 180
181 181 #if pure
182 182 $ hg debugnodemap --metadata
183 183 uid: ???????????????? (glob)
184 184 tip-rev: 5002
185 185 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
186 186 data-length: 121600
187 187 data-unused: 512
188 188 data-unused: 0.421%
189 189 $ f --sha256 .hg/store/00changelog-*.nd --size
190 190 .hg/store/00changelog-????????????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
191 191 #endif
192 192 #if rust
193 193 $ hg debugnodemap --metadata
194 194 uid: ???????????????? (glob)
195 195 tip-rev: 5002
196 196 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
197 197 data-length: 121600
198 198 data-unused: 512
199 199 data-unused: 0.421%
200 200 $ f --sha256 .hg/store/00changelog-*.nd --size
201 201 .hg/store/00changelog-????????????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
202 202 #endif
203 203 #if no-pure no-rust
204 204 $ hg debugnodemap --metadata
205 205 uid: ???????????????? (glob)
206 206 tip-rev: 5002
207 207 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
208 208 data-length: 121088
209 209 data-unused: 0
210 210 data-unused: 0.000%
211 211 $ f --sha256 .hg/store/00changelog-*.nd --size
212 212 .hg/store/00changelog-????????????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
213 213 #endif
214 214
215 215 Test force warming the cache
216 216
217 217 $ rm .hg/store/00changelog.n
218 218 $ hg debugnodemap --metadata
219 219 $ hg debugupdatecache
220 220 #if pure
221 221 $ hg debugnodemap --metadata
222 222 uid: ???????????????? (glob)
223 223 tip-rev: 5002
224 224 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
225 225 data-length: 121088
226 226 data-unused: 0
227 227 data-unused: 0.000%
228 228 #else
229 229 $ hg debugnodemap --metadata
230 230 uid: ???????????????? (glob)
231 231 tip-rev: 5002
232 232 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
233 233 data-length: 121088
234 234 data-unused: 0
235 235 data-unused: 0.000%
236 236 #endif
237 237
238 238 Check out of sync nodemap
239 239 =========================
240 240
241 241 First copy old data on the side.
242 242
243 243 $ mkdir ../tmp-copies
244 244 $ cp .hg/store/00changelog-????????????????.nd .hg/store/00changelog.n ../tmp-copies
245 245
246 246 Nodemap lagging behind
247 247 ----------------------
248 248
249 249 make a new commit
250 250
251 251 $ echo bar2 > bar
252 252 $ hg ci -m 'bar2'
253 253 $ NODE=`hg log -r tip -T '{node}\n'`
254 254 $ hg log -r "$NODE" -T '{rev}\n'
255 255 5003
256 256
257 257 If the nodemap is lagging behind, it can catch up fine
258 258
259 259 $ hg debugnodemap --metadata
260 260 uid: ???????????????? (glob)
261 261 tip-rev: 5003
262 262 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
263 263 data-length: 121344 (pure !)
264 264 data-length: 121344 (rust !)
265 265 data-length: 121152 (no-rust no-pure !)
266 266 data-unused: 192 (pure !)
267 267 data-unused: 192 (rust !)
268 268 data-unused: 0 (no-rust no-pure !)
269 269 data-unused: 0.158% (pure !)
270 270 data-unused: 0.158% (rust !)
271 271 data-unused: 0.000% (no-rust no-pure !)
272 272 $ cp -f ../tmp-copies/* .hg/store/
273 273 $ hg debugnodemap --metadata
274 274 uid: ???????????????? (glob)
275 275 tip-rev: 5002
276 276 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
277 277 data-length: 121088
278 278 data-unused: 0
279 279 data-unused: 0.000%
280 280 $ hg log -r "$NODE" -T '{rev}\n'
281 281 5003
282 282
283 283 changelog altered
284 284 -----------------
285 285
286 286 If the nodemap is not gated behind a requirements, an unaware client can alter
287 287 the repository so the revlog used to generate the nodemap is not longer
288 288 compatible with the persistent nodemap. We need to detect that.
289 289
290 290 $ hg up "$NODE~5"
291 291 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
292 292 $ echo bar > babar
293 293 $ hg add babar
294 294 $ hg ci -m 'babar'
295 295 created new head
296 296 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
297 297 $ hg log -r "$OTHERNODE" -T '{rev}\n'
298 298 5004
299 299
300 300 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
301 301
302 302 the nodemap should detect the changelog have been tampered with and recover.
303 303
304 304 $ hg debugnodemap --metadata
305 305 uid: ???????????????? (glob)
306 306 tip-rev: 5002
307 307 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
308 308 data-length: 121536 (pure !)
309 309 data-length: 121088 (rust !)
310 310 data-length: 121088 (no-pure no-rust !)
311 311 data-unused: 448 (pure !)
312 312 data-unused: 0 (rust !)
313 313 data-unused: 0 (no-pure no-rust !)
314 314 data-unused: 0.000% (rust !)
315 315 data-unused: 0.369% (pure !)
316 316 data-unused: 0.000% (no-pure no-rust !)
317 317
318 318 $ cp -f ../tmp-copies/* .hg/store/
319 319 $ hg debugnodemap --metadata
320 320 uid: ???????????????? (glob)
321 321 tip-rev: 5002
322 322 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
323 323 data-length: 121088
324 324 data-unused: 0
325 325 data-unused: 0.000%
326 326 $ hg log -r "$OTHERNODE" -T '{rev}\n'
327 327 5002
328 328
329 329 Check transaction related property
330 330 ==================================
331 331
332 332 An up to date nodemap should be available to shell hooks,
333 333
334 334 $ echo dsljfl > a
335 335 $ hg add a
336 336 $ hg ci -m a
337 337 $ hg debugnodemap --metadata
338 338 uid: ???????????????? (glob)
339 339 tip-rev: 5003
340 340 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
341 341 data-length: 121088
342 342 data-unused: 0
343 343 data-unused: 0.000%
344 344 $ echo babar2 > babar
345 345 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
346 346 uid: ???????????????? (glob)
347 347 tip-rev: 5004
348 348 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
349 349 data-length: 121280 (pure !)
350 350 data-length: 121280 (rust !)
351 351 data-length: 121088 (no-pure no-rust !)
352 352 data-unused: 192 (pure !)
353 353 data-unused: 192 (rust !)
354 354 data-unused: 0 (no-pure no-rust !)
355 355 data-unused: 0.158% (pure !)
356 356 data-unused: 0.158% (rust !)
357 357 data-unused: 0.000% (no-pure no-rust !)
358 358 $ hg debugnodemap --metadata
359 359 uid: ???????????????? (glob)
360 360 tip-rev: 5004
361 361 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
362 362 data-length: 121280 (pure !)
363 363 data-length: 121280 (rust !)
364 364 data-length: 121088 (no-pure no-rust !)
365 365 data-unused: 192 (pure !)
366 366 data-unused: 192 (rust !)
367 367 data-unused: 0 (no-pure no-rust !)
368 368 data-unused: 0.158% (pure !)
369 369 data-unused: 0.158% (rust !)
370 370 data-unused: 0.000% (no-pure no-rust !)
371 371
372 372 Another process does not see the pending nodemap content during run.
373 373
374 374 $ PATH=$RUNTESTDIR/testlib/:$PATH
375 375 $ echo qpoasp > a
376 376 $ hg ci -m a2 \
377 377 > --config "hooks.pretxnclose=wait-on-file 20 sync-repo-read sync-txn-pending" \
378 378 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
379 379
380 380 (read the repository while the commit transaction is pending)
381 381
382 382 $ wait-on-file 20 sync-txn-pending && \
383 383 > hg debugnodemap --metadata && \
384 384 > wait-on-file 20 sync-txn-close sync-repo-read
385 385 uid: ???????????????? (glob)
386 386 tip-rev: 5004
387 387 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
388 388 data-length: 121280 (pure !)
389 389 data-length: 121280 (rust !)
390 390 data-length: 121088 (no-pure no-rust !)
391 391 data-unused: 192 (pure !)
392 392 data-unused: 192 (rust !)
393 393 data-unused: 0 (no-pure no-rust !)
394 394 data-unused: 0.158% (pure !)
395 395 data-unused: 0.158% (rust !)
396 396 data-unused: 0.000% (no-pure no-rust !)
397 397 $ hg debugnodemap --metadata
398 398 uid: ???????????????? (glob)
399 399 tip-rev: 5005
400 400 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
401 401 data-length: 121536 (pure !)
402 402 data-length: 121536 (rust !)
403 403 data-length: 121088 (no-pure no-rust !)
404 404 data-unused: 448 (pure !)
405 405 data-unused: 448 (rust !)
406 406 data-unused: 0 (no-pure no-rust !)
407 407 data-unused: 0.369% (pure !)
408 408 data-unused: 0.369% (rust !)
409 409 data-unused: 0.000% (no-pure no-rust !)
410 410
411 411 $ cat output.txt
412 412
413 413 Check that a failing transaction will properly revert the data
414 414
415 415 $ echo plakfe > a
416 416 $ f --size --sha256 .hg/store/00changelog-*.nd
417 417 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
418 418 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
419 419 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
420 420 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
421 421 transaction abort!
422 422 rollback completed
423 423 abort: This is a late abort
424 424 [255]
425 425 $ hg debugnodemap --metadata
426 426 uid: ???????????????? (glob)
427 427 tip-rev: 5005
428 428 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
429 429 data-length: 121536 (pure !)
430 430 data-length: 121536 (rust !)
431 431 data-length: 121088 (no-pure no-rust !)
432 432 data-unused: 448 (pure !)
433 433 data-unused: 448 (rust !)
434 434 data-unused: 0 (no-pure no-rust !)
435 435 data-unused: 0.369% (pure !)
436 436 data-unused: 0.369% (rust !)
437 437 data-unused: 0.000% (no-pure no-rust !)
438 438 $ f --size --sha256 .hg/store/00changelog-*.nd
439 439 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
440 440 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
441 441 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
442
443 Test upgrade / downgrade
444 ========================
445
446 downgrading
447
448 $ cat << EOF >> .hg/hgrc
449 > [format]
450 > use-persistent-nodemap=no
451 > EOF
452 $ hg debugformat -v
453 format-variant repo config default
454 fncache: yes yes yes
455 dotencode: yes yes yes
456 generaldelta: yes yes yes
457 sparserevlog: yes yes yes
458 sidedata: no no no
459 persistent-nodemap: yes no no
460 copies-sdc: no no no
461 plain-cl-delta: yes yes yes
462 compression: zlib zlib zlib
463 compression-level: default default default
464 $ hg debugupgraderepo --run --no-backup --quiet
465 upgrade will perform the following actions:
466
467 requirements
468 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store
469 removed: persistent-nodemap
470
471 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
472 [1]
473 $ hg debugnodemap --metadata
474
475
476 upgrading
477
478 $ cat << EOF >> .hg/hgrc
479 > [format]
480 > use-persistent-nodemap=yes
481 > EOF
482 $ hg debugformat -v
483 format-variant repo config default
484 fncache: yes yes yes
485 dotencode: yes yes yes
486 generaldelta: yes yes yes
487 sparserevlog: yes yes yes
488 sidedata: no no no
489 persistent-nodemap: no yes no
490 copies-sdc: no no no
491 plain-cl-delta: yes yes yes
492 compression: zlib zlib zlib
493 compression-level: default default default
494 $ hg debugupgraderepo --run --no-backup --quiet
495 upgrade will perform the following actions:
496
497 requirements
498 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store
499 added: persistent-nodemap
500
501 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
502 00changelog-*.nd (glob)
503 00changelog.n
504 00manifest-*.nd (glob)
505 00manifest.n
506
507 $ hg debugnodemap --metadata
508 uid: * (glob)
509 tip-rev: 5005
510 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
511 data-length: 121088
512 data-unused: 0
513 data-unused: 0.000%
514
515 Running unrelated upgrade
516
517 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
518 upgrade will perform the following actions:
519
520 requirements
521 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store
522
523 optimisations: re-delta-all
524
525 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
526 00changelog-*.nd (glob)
527 00changelog.n
528 00manifest-*.nd (glob)
529 00manifest.n
530
531 $ hg debugnodemap --metadata
532 uid: * (glob)
533 tip-rev: 5005
534 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
535 data-length: 121088
536 data-unused: 0
537 data-unused: 0.000%
General Comments 0
You need to be logged in to leave comments. Login now