##// END OF EJS Templates
upgrade: use StoreEntry object in upgrade...
marmoute -
r51383:0935b9db default
parent child Browse files
Show More
@@ -1,679 +1,679
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
9 9 import stat
10 10
11 11 from ..i18n import _
12 12 from ..pycompat import getattr
13 13 from .. import (
14 14 changelog,
15 15 error,
16 16 filelog,
17 17 manifest,
18 18 metadata,
19 19 pycompat,
20 20 requirements,
21 21 scmutil,
22 22 store,
23 23 util,
24 24 vfs as vfsmod,
25 25 )
26 26 from ..revlogutils import (
27 27 constants as revlogconst,
28 28 flagutil,
29 29 nodemap,
30 30 sidedata as sidedatamod,
31 31 )
32 32 from . import actions as upgrade_actions
33 33
34 34
35 35 def get_sidedata_helpers(srcrepo, dstrepo):
36 36 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
37 37 sequential = pycompat.iswindows or not use_w
38 38 if not sequential:
39 39 srcrepo.register_sidedata_computer(
40 40 revlogconst.KIND_CHANGELOG,
41 41 sidedatamod.SD_FILES,
42 42 (sidedatamod.SD_FILES,),
43 43 metadata._get_worker_sidedata_adder(srcrepo, dstrepo),
44 44 flagutil.REVIDX_HASCOPIESINFO,
45 45 replace=True,
46 46 )
47 47 return sidedatamod.get_sidedata_helpers(srcrepo, dstrepo._wanted_sidedata)
48 48
49 49
50 def _revlogfrompath(repo, rl_type, path):
51 """Obtain a revlog from a repo path.
50 def _revlog_from_store_entry(repo, entry):
51 """Obtain a revlog from a repo store entry.
52 52
53 53 An instance of the appropriate class is returned.
54 54 """
55 rl_type = entry.revlog_type
56 path = entry.unencoded_path
55 57 if rl_type & store.FILEFLAGS_CHANGELOG:
56 58 return changelog.changelog(repo.svfs)
57 59 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
58 60 mandir = b''
59 61 if b'/' in path:
60 62 mandir = path.rsplit(b'/', 1)[0]
61 63 return manifest.manifestrevlog(
62 64 repo.nodeconstants, repo.svfs, tree=mandir
63 65 )
64 66 else:
65 67 # drop the extension and the `data/` prefix
66 68 path_part = path.rsplit(b'.', 1)[0].split(b'/', 1)
67 69 if len(path_part) < 2:
68 70 msg = _(b'cannot recognize revlog from filename: %s')
69 71 msg %= path
70 72 raise error.Abort(msg)
71 73 path = path_part[1]
72 74 return filelog.filelog(repo.svfs, path)
73 75
74 76
75 def _copyrevlog(tr, destrepo, oldrl, rl_type, unencodedname):
77 def _copyrevlog(tr, destrepo, oldrl, entry):
76 78 """copy all relevant files for `oldrl` into `destrepo` store
77 79
78 80 Files are copied "as is" without any transformation. The copy is performed
79 81 without extra checks. Callers are responsible for making sure the copied
80 82 content is compatible with format of the destination repository.
81 83 """
82 84 oldrl = getattr(oldrl, '_revlog', oldrl)
83 newrl = _revlogfrompath(destrepo, rl_type, unencodedname)
85 newrl = _revlog_from_store_entry(destrepo, entry)
84 86 newrl = getattr(newrl, '_revlog', newrl)
85 87
86 88 oldvfs = oldrl.opener
87 89 newvfs = newrl.opener
88 90 oldindex = oldvfs.join(oldrl._indexfile)
89 91 newindex = newvfs.join(newrl._indexfile)
90 92 olddata = oldvfs.join(oldrl._datafile)
91 93 newdata = newvfs.join(newrl._datafile)
92 94
93 95 with newvfs(newrl._indexfile, b'w'):
94 96 pass # create all the directories
95 97
96 98 util.copyfile(oldindex, newindex)
97 99 copydata = oldrl.opener.exists(oldrl._datafile)
98 100 if copydata:
99 101 util.copyfile(olddata, newdata)
100 102
101 if rl_type & store.FILEFLAGS_FILELOG:
103 if entry.revlog_type & store.FILEFLAGS_FILELOG:
104 unencodedname = entry.unencoded_path
102 105 destrepo.svfs.fncache.add(unencodedname)
103 106 if copydata:
104 107 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
105 108
106 109
107 110 UPGRADE_CHANGELOG = b"changelog"
108 111 UPGRADE_MANIFEST = b"manifest"
109 112 UPGRADE_FILELOGS = b"all-filelogs"
110 113
111 114 UPGRADE_ALL_REVLOGS = frozenset(
112 115 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOGS]
113 116 )
114 117
115 118
116 119 def matchrevlog(revlogfilter, rl_type):
117 120 """check if a revlog is selected for cloning.
118 121
119 122 In other words, are there any updates which need to be done on revlog
120 123 or it can be blindly copied.
121 124
122 125 The store entry is checked against the passed filter"""
123 126 if rl_type & store.FILEFLAGS_CHANGELOG:
124 127 return UPGRADE_CHANGELOG in revlogfilter
125 128 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
126 129 return UPGRADE_MANIFEST in revlogfilter
127 130 assert rl_type & store.FILEFLAGS_FILELOG
128 131 return UPGRADE_FILELOGS in revlogfilter
129 132
130 133
131 134 def _perform_clone(
132 135 ui,
133 136 dstrepo,
134 137 tr,
135 138 old_revlog,
136 rl_type,
137 unencoded,
139 entry,
138 140 upgrade_op,
139 141 sidedata_helpers,
140 142 oncopiedrevision,
141 143 ):
142 144 """returns the new revlog object created"""
143 145 newrl = None
144 if matchrevlog(upgrade_op.revlogs_to_process, rl_type):
146 revlog_path = entry.unencoded_path
147 if matchrevlog(upgrade_op.revlogs_to_process, entry.revlog_type):
145 148 ui.note(
146 _(b'cloning %d revisions from %s\n') % (len(old_revlog), unencoded)
149 _(b'cloning %d revisions from %s\n')
150 % (len(old_revlog), revlog_path)
147 151 )
148 newrl = _revlogfrompath(dstrepo, rl_type, unencoded)
152 newrl = _revlog_from_store_entry(dstrepo, entry)
149 153 old_revlog.clone(
150 154 tr,
151 155 newrl,
152 156 addrevisioncb=oncopiedrevision,
153 157 deltareuse=upgrade_op.delta_reuse_mode,
154 158 forcedeltabothparents=upgrade_op.force_re_delta_both_parents,
155 159 sidedata_helpers=sidedata_helpers,
156 160 )
157 161 else:
158 162 msg = _(b'blindly copying %s containing %i revisions\n')
159 ui.note(msg % (unencoded, len(old_revlog)))
160 _copyrevlog(tr, dstrepo, old_revlog, rl_type, unencoded)
163 ui.note(msg % (revlog_path, len(old_revlog)))
164 _copyrevlog(tr, dstrepo, old_revlog, entry)
161 165
162 newrl = _revlogfrompath(dstrepo, rl_type, unencoded)
166 newrl = _revlog_from_store_entry(dstrepo, entry)
163 167 return newrl
164 168
165 169
166 170 def _clonerevlogs(
167 171 ui,
168 172 srcrepo,
169 173 dstrepo,
170 174 tr,
171 175 upgrade_op,
172 176 ):
173 177 """Copy revlogs between 2 repos."""
174 178 revcount = 0
175 179 srcsize = 0
176 180 srcrawsize = 0
177 181 dstsize = 0
178 182 fcount = 0
179 183 frevcount = 0
180 184 fsrcsize = 0
181 185 frawsize = 0
182 186 fdstsize = 0
183 187 mcount = 0
184 188 mrevcount = 0
185 189 msrcsize = 0
186 190 mrawsize = 0
187 191 mdstsize = 0
188 192 crevcount = 0
189 193 csrcsize = 0
190 194 crawsize = 0
191 195 cdstsize = 0
192 196
193 197 alldatafiles = list(srcrepo.store.walk())
194 198 # mapping of data files which needs to be cloned
195 199 # key is unencoded filename
196 200 # value is revlog_object_from_srcrepo
197 201 manifests = {}
198 202 changelogs = {}
199 203 filelogs = {}
200 204
201 205 # Perform a pass to collect metadata. This validates we can open all
202 206 # source files and allows a unified progress bar to be displayed.
203 207 for entry in alldatafiles:
204 208 if not (entry.is_revlog and entry.is_revlog_main):
205 209 continue
206 unencoded = entry.unencoded_path
207 210
208 rl = _revlogfrompath(srcrepo, entry.revlog_type, unencoded)
211 rl = _revlog_from_store_entry(srcrepo, entry)
209 212
210 213 info = rl.storageinfo(
211 214 exclusivefiles=True,
212 215 revisionscount=True,
213 216 trackedsize=True,
214 217 storedsize=True,
215 218 )
216 219
217 220 revcount += info[b'revisionscount'] or 0
218 221 datasize = info[b'storedsize'] or 0
219 222 rawsize = info[b'trackedsize'] or 0
220 223
221 224 srcsize += datasize
222 225 srcrawsize += rawsize
223 226
224 227 # This is for the separate progress bars.
225 228 if entry.revlog_type & store.FILEFLAGS_CHANGELOG:
226 changelogs[unencoded] = entry.revlog_type
229 changelogs[entry.target_id] = entry
227 230 crevcount += len(rl)
228 231 csrcsize += datasize
229 232 crawsize += rawsize
230 233 elif entry.revlog_type & store.FILEFLAGS_MANIFESTLOG:
231 manifests[unencoded] = entry.revlog_type
234 manifests[entry.target_id] = entry
232 235 mcount += 1
233 236 mrevcount += len(rl)
234 237 msrcsize += datasize
235 238 mrawsize += rawsize
236 239 elif entry.revlog_type & store.FILEFLAGS_FILELOG:
237 filelogs[unencoded] = entry.revlog_type
240 filelogs[entry.target_id] = entry
238 241 fcount += 1
239 242 frevcount += len(rl)
240 243 fsrcsize += datasize
241 244 frawsize += rawsize
242 245 else:
243 246 error.ProgrammingError(b'unknown revlog type')
244 247
245 248 if not revcount:
246 249 return
247 250
248 251 ui.status(
249 252 _(
250 253 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
251 254 b'%d in changelog)\n'
252 255 )
253 256 % (revcount, frevcount, mrevcount, crevcount)
254 257 )
255 258 ui.status(
256 259 _(b'migrating %s in store; %s tracked data\n')
257 260 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
258 261 )
259 262
260 263 # Used to keep track of progress.
261 264 progress = None
262 265
263 266 def oncopiedrevision(rl, rev, node):
264 267 progress.increment()
265 268
266 269 sidedata_helpers = get_sidedata_helpers(srcrepo, dstrepo)
267 270
268 271 # Migrating filelogs
269 272 ui.status(
270 273 _(
271 274 b'migrating %d filelogs containing %d revisions '
272 275 b'(%s in store; %s tracked data)\n'
273 276 )
274 277 % (
275 278 fcount,
276 279 frevcount,
277 280 util.bytecount(fsrcsize),
278 281 util.bytecount(frawsize),
279 282 )
280 283 )
281 284 progress = srcrepo.ui.makeprogress(_(b'file revisions'), total=frevcount)
282 for unencoded, rl_type in sorted(filelogs.items()):
283 oldrl = _revlogfrompath(srcrepo, rl_type, unencoded)
285 for target_id, entry in sorted(filelogs.items()):
286 oldrl = _revlog_from_store_entry(srcrepo, entry)
284 287
285 288 newrl = _perform_clone(
286 289 ui,
287 290 dstrepo,
288 291 tr,
289 292 oldrl,
290 rl_type,
291 unencoded,
293 entry,
292 294 upgrade_op,
293 295 sidedata_helpers,
294 296 oncopiedrevision,
295 297 )
296 298 info = newrl.storageinfo(storedsize=True)
297 299 fdstsize += info[b'storedsize'] or 0
298 300 ui.status(
299 301 _(
300 302 b'finished migrating %d filelog revisions across %d '
301 303 b'filelogs; change in size: %s\n'
302 304 )
303 305 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
304 306 )
305 307
306 308 # Migrating manifests
307 309 ui.status(
308 310 _(
309 311 b'migrating %d manifests containing %d revisions '
310 312 b'(%s in store; %s tracked data)\n'
311 313 )
312 314 % (
313 315 mcount,
314 316 mrevcount,
315 317 util.bytecount(msrcsize),
316 318 util.bytecount(mrawsize),
317 319 )
318 320 )
319 321 if progress:
320 322 progress.complete()
321 323 progress = srcrepo.ui.makeprogress(
322 324 _(b'manifest revisions'), total=mrevcount
323 325 )
324 for unencoded, rl_type in sorted(manifests.items()):
325 oldrl = _revlogfrompath(srcrepo, rl_type, unencoded)
326 for target_id, entry in sorted(manifests.items()):
327 oldrl = _revlog_from_store_entry(srcrepo, entry)
326 328 newrl = _perform_clone(
327 329 ui,
328 330 dstrepo,
329 331 tr,
330 332 oldrl,
331 rl_type,
332 unencoded,
333 entry,
333 334 upgrade_op,
334 335 sidedata_helpers,
335 336 oncopiedrevision,
336 337 )
337 338 info = newrl.storageinfo(storedsize=True)
338 339 mdstsize += info[b'storedsize'] or 0
339 340 ui.status(
340 341 _(
341 342 b'finished migrating %d manifest revisions across %d '
342 343 b'manifests; change in size: %s\n'
343 344 )
344 345 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
345 346 )
346 347
347 348 # Migrating changelog
348 349 ui.status(
349 350 _(
350 351 b'migrating changelog containing %d revisions '
351 352 b'(%s in store; %s tracked data)\n'
352 353 )
353 354 % (
354 355 crevcount,
355 356 util.bytecount(csrcsize),
356 357 util.bytecount(crawsize),
357 358 )
358 359 )
359 360 if progress:
360 361 progress.complete()
361 362 progress = srcrepo.ui.makeprogress(
362 363 _(b'changelog revisions'), total=crevcount
363 364 )
364 for unencoded, rl_type in sorted(changelogs.items()):
365 oldrl = _revlogfrompath(srcrepo, rl_type, unencoded)
365 for target_id, entry in sorted(changelogs.items()):
366 oldrl = _revlog_from_store_entry(srcrepo, entry)
366 367 newrl = _perform_clone(
367 368 ui,
368 369 dstrepo,
369 370 tr,
370 371 oldrl,
371 rl_type,
372 unencoded,
372 entry,
373 373 upgrade_op,
374 374 sidedata_helpers,
375 375 oncopiedrevision,
376 376 )
377 377 info = newrl.storageinfo(storedsize=True)
378 378 cdstsize += info[b'storedsize'] or 0
379 379 progress.complete()
380 380 ui.status(
381 381 _(
382 382 b'finished migrating %d changelog revisions; change in size: '
383 383 b'%s\n'
384 384 )
385 385 % (crevcount, util.bytecount(cdstsize - csrcsize))
386 386 )
387 387
388 388 dstsize = fdstsize + mdstsize + cdstsize
389 389 ui.status(
390 390 _(
391 391 b'finished migrating %d total revisions; total change in store '
392 392 b'size: %s\n'
393 393 )
394 394 % (revcount, util.bytecount(dstsize - srcsize))
395 395 )
396 396
397 397
398 398 def _files_to_copy_post_revlog_clone(srcrepo):
399 399 """yields files which should be copied to destination after revlogs
400 400 are cloned"""
401 401 for path, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
402 402 # don't copy revlogs as they are already cloned
403 403 if store.revlog_type(path) is not None:
404 404 continue
405 405 # Skip transaction related files.
406 406 if path.startswith(b'undo'):
407 407 continue
408 408 # Only copy regular files.
409 409 if kind != stat.S_IFREG:
410 410 continue
411 411 # Skip other skipped files.
412 412 if path in (b'lock', b'fncache'):
413 413 continue
414 414 # TODO: should we skip cache too?
415 415
416 416 yield path
417 417
418 418
419 419 def _replacestores(currentrepo, upgradedrepo, backupvfs, upgrade_op):
420 420 """Replace the stores after current repository is upgraded
421 421
422 422 Creates a backup of current repository store at backup path
423 423 Replaces upgraded store files in current repo from upgraded one
424 424
425 425 Arguments:
426 426 currentrepo: repo object of current repository
427 427 upgradedrepo: repo object of the upgraded data
428 428 backupvfs: vfs object for the backup path
429 429 upgrade_op: upgrade operation object
430 430 to be used to decide what all is upgraded
431 431 """
432 432 # TODO: don't blindly rename everything in store
433 433 # There can be upgrades where store is not touched at all
434 434 if upgrade_op.backup_store:
435 435 util.rename(currentrepo.spath, backupvfs.join(b'store'))
436 436 else:
437 437 currentrepo.vfs.rmtree(b'store', forcibly=True)
438 438 util.rename(upgradedrepo.spath, currentrepo.spath)
439 439
440 440
441 441 def finishdatamigration(ui, srcrepo, dstrepo, requirements):
442 442 """Hook point for extensions to perform additional actions during upgrade.
443 443
444 444 This function is called after revlogs and store files have been copied but
445 445 before the new store is swapped into the original location.
446 446 """
447 447
448 448
449 449 def upgrade(ui, srcrepo, dstrepo, upgrade_op):
450 450 """Do the low-level work of upgrading a repository.
451 451
452 452 The upgrade is effectively performed as a copy between a source
453 453 repository and a temporary destination repository.
454 454
455 455 The source repository is unmodified for as long as possible so the
456 456 upgrade can abort at any time without causing loss of service for
457 457 readers and without corrupting the source repository.
458 458 """
459 459 assert srcrepo.currentwlock()
460 460 assert dstrepo.currentwlock()
461 461 backuppath = None
462 462 backupvfs = None
463 463
464 464 ui.status(
465 465 _(
466 466 b'(it is safe to interrupt this process any time before '
467 467 b'data migration completes)\n'
468 468 )
469 469 )
470 470
471 471 if upgrade_actions.dirstatev2 in upgrade_op.upgrade_actions:
472 472 ui.status(_(b'upgrading to dirstate-v2 from v1\n'))
473 473 upgrade_dirstate(ui, srcrepo, upgrade_op, b'v1', b'v2')
474 474 upgrade_op.upgrade_actions.remove(upgrade_actions.dirstatev2)
475 475
476 476 if upgrade_actions.dirstatev2 in upgrade_op.removed_actions:
477 477 ui.status(_(b'downgrading from dirstate-v2 to v1\n'))
478 478 upgrade_dirstate(ui, srcrepo, upgrade_op, b'v2', b'v1')
479 479 upgrade_op.removed_actions.remove(upgrade_actions.dirstatev2)
480 480
481 481 if upgrade_actions.dirstatetrackedkey in upgrade_op.upgrade_actions:
482 482 ui.status(_(b'create dirstate-tracked-hint file\n'))
483 483 upgrade_tracked_hint(ui, srcrepo, upgrade_op, add=True)
484 484 upgrade_op.upgrade_actions.remove(upgrade_actions.dirstatetrackedkey)
485 485 elif upgrade_actions.dirstatetrackedkey in upgrade_op.removed_actions:
486 486 ui.status(_(b'remove dirstate-tracked-hint file\n'))
487 487 upgrade_tracked_hint(ui, srcrepo, upgrade_op, add=False)
488 488 upgrade_op.removed_actions.remove(upgrade_actions.dirstatetrackedkey)
489 489
490 490 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
491 491 return
492 492
493 493 if upgrade_op.requirements_only:
494 494 ui.status(_(b'upgrading repository requirements\n'))
495 495 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
496 496 # if there is only one action and that is persistent nodemap upgrade
497 497 # directly write the nodemap file and update requirements instead of going
498 498 # through the whole cloning process
499 499 elif (
500 500 len(upgrade_op.upgrade_actions) == 1
501 501 and b'persistent-nodemap' in upgrade_op.upgrade_actions_names
502 502 and not upgrade_op.removed_actions
503 503 ):
504 504 ui.status(
505 505 _(b'upgrading repository to use persistent nodemap feature\n')
506 506 )
507 507 with srcrepo.transaction(b'upgrade') as tr:
508 508 unfi = srcrepo.unfiltered()
509 509 cl = unfi.changelog
510 510 nodemap.persist_nodemap(tr, cl, force=True)
511 511 # we want to directly operate on the underlying revlog to force
512 512 # create a nodemap file. This is fine since this is upgrade code
513 513 # and it heavily relies on repository being revlog based
514 514 # hence accessing private attributes can be justified
515 515 nodemap.persist_nodemap(
516 516 tr, unfi.manifestlog._rootstore._revlog, force=True
517 517 )
518 518 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
519 519 elif (
520 520 len(upgrade_op.removed_actions) == 1
521 521 and [
522 522 x
523 523 for x in upgrade_op.removed_actions
524 524 if x.name == b'persistent-nodemap'
525 525 ]
526 526 and not upgrade_op.upgrade_actions
527 527 ):
528 528 ui.status(
529 529 _(b'downgrading repository to not use persistent nodemap feature\n')
530 530 )
531 531 with srcrepo.transaction(b'upgrade') as tr:
532 532 unfi = srcrepo.unfiltered()
533 533 cl = unfi.changelog
534 534 nodemap.delete_nodemap(tr, srcrepo, cl)
535 535 # check comment 20 lines above for accessing private attributes
536 536 nodemap.delete_nodemap(
537 537 tr, srcrepo, unfi.manifestlog._rootstore._revlog
538 538 )
539 539 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
540 540 else:
541 541 with dstrepo.transaction(b'upgrade') as tr:
542 542 _clonerevlogs(
543 543 ui,
544 544 srcrepo,
545 545 dstrepo,
546 546 tr,
547 547 upgrade_op,
548 548 )
549 549
550 550 # Now copy other files in the store directory.
551 551 for p in _files_to_copy_post_revlog_clone(srcrepo):
552 552 srcrepo.ui.status(_(b'copying %s\n') % p)
553 553 src = srcrepo.store.rawvfs.join(p)
554 554 dst = dstrepo.store.rawvfs.join(p)
555 555 util.copyfile(src, dst, copystat=True)
556 556
557 557 finishdatamigration(ui, srcrepo, dstrepo, requirements)
558 558
559 559 ui.status(_(b'data fully upgraded in a temporary repository\n'))
560 560
561 561 if upgrade_op.backup_store:
562 562 backuppath = pycompat.mkdtemp(
563 563 prefix=b'upgradebackup.', dir=srcrepo.path
564 564 )
565 565 backupvfs = vfsmod.vfs(backuppath)
566 566
567 567 # Make a backup of requires file first, as it is the first to be modified.
568 568 util.copyfile(
569 569 srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
570 570 )
571 571
572 572 # We install an arbitrary requirement that clients must not support
573 573 # as a mechanism to lock out new clients during the data swap. This is
574 574 # better than allowing a client to continue while the repository is in
575 575 # an inconsistent state.
576 576 ui.status(
577 577 _(
578 578 b'marking source repository as being upgraded; clients will be '
579 579 b'unable to read from repository\n'
580 580 )
581 581 )
582 582 scmutil.writereporequirements(
583 583 srcrepo, srcrepo.requirements | {b'upgradeinprogress'}
584 584 )
585 585
586 586 ui.status(_(b'starting in-place swap of repository data\n'))
587 587 if upgrade_op.backup_store:
588 588 ui.status(
589 589 _(b'replaced files will be backed up at %s\n') % backuppath
590 590 )
591 591
592 592 # Now swap in the new store directory. Doing it as a rename should make
593 593 # the operation nearly instantaneous and atomic (at least in well-behaved
594 594 # environments).
595 595 ui.status(_(b'replacing store...\n'))
596 596 tstart = util.timer()
597 597 _replacestores(srcrepo, dstrepo, backupvfs, upgrade_op)
598 598 elapsed = util.timer() - tstart
599 599 ui.status(
600 600 _(
601 601 b'store replacement complete; repository was inconsistent for '
602 602 b'%0.1fs\n'
603 603 )
604 604 % elapsed
605 605 )
606 606
607 607 # We first write the requirements file. Any new requirements will lock
608 608 # out legacy clients.
609 609 ui.status(
610 610 _(
611 611 b'finalizing requirements file and making repository readable '
612 612 b'again\n'
613 613 )
614 614 )
615 615 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
616 616
617 617 if upgrade_op.backup_store:
618 618 # The lock file from the old store won't be removed because nothing has a
619 619 # reference to its new location. So clean it up manually. Alternatively, we
620 620 # could update srcrepo.svfs and other variables to point to the new
621 621 # location. This is simpler.
622 622 assert backupvfs is not None # help pytype
623 623 backupvfs.unlink(b'store/lock')
624 624
625 625 return backuppath
626 626
627 627
628 628 def upgrade_dirstate(ui, srcrepo, upgrade_op, old, new):
629 629 if upgrade_op.backup_store:
630 630 backuppath = pycompat.mkdtemp(
631 631 prefix=b'upgradebackup.', dir=srcrepo.path
632 632 )
633 633 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
634 634 backupvfs = vfsmod.vfs(backuppath)
635 635 util.copyfile(
636 636 srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
637 637 )
638 638 try:
639 639 util.copyfile(
640 640 srcrepo.vfs.join(b'dirstate'), backupvfs.join(b'dirstate')
641 641 )
642 642 except FileNotFoundError:
643 643 # The dirstate does not exist on an empty repo or a repo with no
644 644 # revision checked out
645 645 pass
646 646
647 647 assert srcrepo.dirstate._use_dirstate_v2 == (old == b'v2')
648 648 use_v2 = new == b'v2'
649 649 if use_v2:
650 650 # Write the requirements *before* upgrading
651 651 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
652 652
653 653 srcrepo.dirstate._map.preload()
654 654 srcrepo.dirstate._use_dirstate_v2 = use_v2
655 655 srcrepo.dirstate._map._use_dirstate_v2 = use_v2
656 656 srcrepo.dirstate._dirty = True
657 657 try:
658 658 srcrepo.vfs.unlink(b'dirstate')
659 659 except FileNotFoundError:
660 660 # The dirstate does not exist on an empty repo or a repo with no
661 661 # revision checked out
662 662 pass
663 663
664 664 srcrepo.dirstate.write(None)
665 665 if not use_v2:
666 666 # Remove the v2 requirement *after* downgrading
667 667 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
668 668
669 669
670 670 def upgrade_tracked_hint(ui, srcrepo, upgrade_op, add):
671 671 if add:
672 672 srcrepo.dirstate._use_tracked_hint = True
673 673 srcrepo.dirstate._dirty = True
674 674 srcrepo.dirstate._dirty_tracked_set = True
675 675 srcrepo.dirstate.write(None)
676 676 if not add:
677 677 srcrepo.dirstate.delete_tracked_hint()
678 678
679 679 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
General Comments 0
You need to be logged in to leave comments. Login now