mercurial/repair.py
changeset 30779 38aa1ca97b6a
parent 30777 7de7afd8bdd9
child 30780 2603d04889e1
equal deleted inserted replaced
30778:1c7368d1a25f 30779:38aa1ca97b6a
     9 from __future__ import absolute_import
     9 from __future__ import absolute_import
    10 
    10 
    11 import errno
    11 import errno
    12 import hashlib
    12 import hashlib
    13 import tempfile
    13 import tempfile
       
    14 import time
    14 
    15 
    15 from .i18n import _
    16 from .i18n import _
    16 from .node import short
    17 from .node import short
    17 from . import (
    18 from . import (
    18     bundle2,
    19     bundle2,
    19     changegroup,
    20     changegroup,
       
    21     changelog,
    20     error,
    22     error,
    21     exchange,
    23     exchange,
       
    24     manifest,
    22     obsolete,
    25     obsolete,
       
    26     revlog,
    23     scmutil,
    27     scmutil,
    24     util,
    28     util,
    25 )
    29 )
    26 
    30 
    27 def _bundle(repo, bases, heads, node, suffix, compress=True):
    31 def _bundle(repo, bases, heads, node, suffix, compress=True):
   637     # FUTURE consider adding some optimizations here for certain transitions.
   641     # FUTURE consider adding some optimizations here for certain transitions.
   638     # e.g. adding generaldelta could schedule parent redeltas.
   642     # e.g. adding generaldelta could schedule parent redeltas.
   639 
   643 
   640     return newactions
   644     return newactions
   641 
   645 
       
   646 def _revlogfrompath(repo, path):
       
   647     """Obtain a revlog from a repo path.
       
   648 
       
   649     An instance of the appropriate class is returned.
       
   650     """
       
   651     if path == '00changelog.i':
       
   652         return changelog.changelog(repo.svfs)
       
   653     elif path.endswith('00manifest.i'):
       
   654         mandir = path[:-len('00manifest.i')]
       
   655         return manifest.manifestrevlog(repo.svfs, dir=mandir)
       
   656     else:
       
   657         # Filelogs don't do anything special with settings. So we can use a
       
   658         # vanilla revlog.
       
   659         return revlog.revlog(repo.svfs, path)
       
   660 
       
   661 def _copyrevlogs(ui, srcrepo, dstrepo, tr, deltareuse, aggressivemergedeltas):
       
   662     """Copy revlogs between 2 repos."""
       
   663     revcount = 0
       
   664     srcsize = 0
       
   665     srcrawsize = 0
       
   666     dstsize = 0
       
   667     fcount = 0
       
   668     frevcount = 0
       
   669     fsrcsize = 0
       
   670     frawsize = 0
       
   671     fdstsize = 0
       
   672     mcount = 0
       
   673     mrevcount = 0
       
   674     msrcsize = 0
       
   675     mrawsize = 0
       
   676     mdstsize = 0
       
   677     crevcount = 0
       
   678     csrcsize = 0
       
   679     crawsize = 0
       
   680     cdstsize = 0
       
   681 
       
   682     # Perform a pass to collect metadata. This validates we can open all
       
   683     # source files and allows a unified progress bar to be displayed.
       
   684     for unencoded, encoded, size in srcrepo.store.walk():
       
   685         if unencoded.endswith('.d'):
       
   686             continue
       
   687 
       
   688         rl = _revlogfrompath(srcrepo, unencoded)
       
   689         revcount += len(rl)
       
   690 
       
   691         datasize = 0
       
   692         rawsize = 0
       
   693         idx = rl.index
       
   694         for rev in rl:
       
   695             e = idx[rev]
       
   696             datasize += e[1]
       
   697             rawsize += e[2]
       
   698 
       
   699         srcsize += datasize
       
   700         srcrawsize += rawsize
       
   701 
       
   702         # This is for the separate progress bars.
       
   703         if isinstance(rl, changelog.changelog):
       
   704             crevcount += len(rl)
       
   705             csrcsize += datasize
       
   706             crawsize += rawsize
       
   707         elif isinstance(rl, manifest.manifestrevlog):
       
   708             mcount += 1
       
   709             mrevcount += len(rl)
       
   710             msrcsize += datasize
       
   711             mrawsize += rawsize
       
   712         elif isinstance(rl, revlog.revlog):
       
   713             fcount += 1
       
   714             frevcount += len(rl)
       
   715             fsrcsize += datasize
       
   716             frawsize += rawsize
       
   717 
       
   718     if not revcount:
       
   719         return
       
   720 
       
   721     ui.write(_('migrating %d total revisions (%d in filelogs, %d in manifests, '
       
   722                '%d in changelog)\n') %
       
   723              (revcount, frevcount, mrevcount, crevcount))
       
   724     ui.write(_('migrating %s in store; %s tracked data\n') % (
       
   725              (util.bytecount(srcsize), util.bytecount(srcrawsize))))
       
   726 
       
   727     # Used to keep track of progress.
       
   728     progress = []
       
   729     def oncopiedrevision(rl, rev, node):
       
   730         progress[1] += 1
       
   731         srcrepo.ui.progress(progress[0], progress[1], total=progress[2])
       
   732 
       
   733     # Do the actual copying.
       
   734     # FUTURE this operation can be farmed off to worker processes.
       
   735     seen = set()
       
   736     for unencoded, encoded, size in srcrepo.store.walk():
       
   737         if unencoded.endswith('.d'):
       
   738             continue
       
   739 
       
   740         oldrl = _revlogfrompath(srcrepo, unencoded)
       
   741         newrl = _revlogfrompath(dstrepo, unencoded)
       
   742 
       
   743         if isinstance(oldrl, changelog.changelog) and 'c' not in seen:
       
   744             ui.write(_('finished migrating %d manifest revisions across %d '
       
   745                        'manifests; change in size: %s\n') %
       
   746                      (mrevcount, mcount, util.bytecount(mdstsize - msrcsize)))
       
   747 
       
   748             ui.write(_('migrating changelog containing %d revisions '
       
   749                        '(%s in store; %s tracked data)\n') %
       
   750                      (crevcount, util.bytecount(csrcsize),
       
   751                       util.bytecount(crawsize)))
       
   752             seen.add('c')
       
   753             progress[:] = [_('changelog revisions'), 0, crevcount]
       
   754         elif isinstance(oldrl, manifest.manifestrevlog) and 'm' not in seen:
       
   755             ui.write(_('finished migrating %d filelog revisions across %d '
       
   756                        'filelogs; change in size: %s\n') %
       
   757                      (frevcount, fcount, util.bytecount(fdstsize - fsrcsize)))
       
   758 
       
   759             ui.write(_('migrating %d manifests containing %d revisions '
       
   760                        '(%s in store; %s tracked data)\n') %
       
   761                      (mcount, mrevcount, util.bytecount(msrcsize),
       
   762                       util.bytecount(mrawsize)))
       
   763             seen.add('m')
       
   764             progress[:] = [_('manifest revisions'), 0, mrevcount]
       
   765         elif 'f' not in seen:
       
   766             ui.write(_('migrating %d filelogs containing %d revisions '
       
   767                        '(%s in store; %s tracked data)\n') %
       
   768                      (fcount, frevcount, util.bytecount(fsrcsize),
       
   769                       util.bytecount(frawsize)))
       
   770             seen.add('f')
       
   771             progress[:] = [_('file revisions'), 0, frevcount]
       
   772 
       
   773         ui.progress(progress[0], progress[1], total=progress[2])
       
   774 
       
   775         ui.note(_('cloning %d revisions from %s\n') % (len(oldrl), unencoded))
       
   776         oldrl.clone(tr, newrl, addrevisioncb=oncopiedrevision,
       
   777                     deltareuse=deltareuse,
       
   778                     aggressivemergedeltas=aggressivemergedeltas)
       
   779 
       
   780         datasize = 0
       
   781         idx = newrl.index
       
   782         for rev in newrl:
       
   783             datasize += idx[rev][1]
       
   784 
       
   785         dstsize += datasize
       
   786 
       
   787         if isinstance(newrl, changelog.changelog):
       
   788             cdstsize += datasize
       
   789         elif isinstance(newrl, manifest.manifestrevlog):
       
   790             mdstsize += datasize
       
   791         else:
       
   792             fdstsize += datasize
       
   793 
       
   794     ui.progress(progress[0], None)
       
   795 
       
   796     ui.write(_('finished migrating %d changelog revisions; change in size: '
       
   797                '%s\n') % (crevcount, util.bytecount(cdstsize - csrcsize)))
       
   798 
       
   799     ui.write(_('finished migrating %d total revisions; total change in store '
       
   800                'size: %s\n') % (revcount, util.bytecount(dstsize - srcsize)))
       
   801 
   642 def _upgraderepo(ui, srcrepo, dstrepo, requirements, actions):
   802 def _upgraderepo(ui, srcrepo, dstrepo, requirements, actions):
   643     """Do the low-level work of upgrading a repository.
   803     """Do the low-level work of upgrading a repository.
   644 
   804 
   645     The upgrade is effectively performed as a copy between a source
   805     The upgrade is effectively performed as a copy between a source
   646     repository and a temporary destination repository.
   806     repository and a temporary destination repository.
   650     readers and without corrupting the source repository.
   810     readers and without corrupting the source repository.
   651     """
   811     """
   652     assert srcrepo.currentwlock()
   812     assert srcrepo.currentwlock()
   653     assert dstrepo.currentwlock()
   813     assert dstrepo.currentwlock()
   654 
   814 
   655     # TODO copy store
   815     ui.write(_('(it is safe to interrupt this process any time before '
       
   816                'data migration completes)\n'))
       
   817 
       
   818     if 'redeltaall' in actions:
       
   819         deltareuse = revlog.revlog.DELTAREUSENEVER
       
   820     elif 'redeltaparent' in actions:
       
   821         deltareuse = revlog.revlog.DELTAREUSESAMEREVS
       
   822     elif 'redeltamultibase' in actions:
       
   823         deltareuse = revlog.revlog.DELTAREUSESAMEREVS
       
   824     else:
       
   825         deltareuse = revlog.revlog.DELTAREUSEALWAYS
       
   826 
       
   827     with dstrepo.transaction('upgrade') as tr:
       
   828         _copyrevlogs(ui, srcrepo, dstrepo, tr, deltareuse,
       
   829                      'redeltamultibase' in actions)
       
   830 
       
   831     # TODO copy non-revlog store files
       
   832 
       
   833     ui.write(_('data fully migrated to temporary repository\n'))
   656 
   834 
   657     backuppath = tempfile.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path)
   835     backuppath = tempfile.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path)
   658     backupvfs = scmutil.vfs(backuppath)
   836     backupvfs = scmutil.vfs(backuppath)
   659 
   837 
   660     # Make a backup of requires file first, as it is the first to be modified.
   838     # Make a backup of requires file first, as it is the first to be modified.
   671 
   849 
   672     ui.write(_('starting in-place swap of repository data\n'))
   850     ui.write(_('starting in-place swap of repository data\n'))
   673     ui.write(_('replaced files will be backed up at %s\n') %
   851     ui.write(_('replaced files will be backed up at %s\n') %
   674              backuppath)
   852              backuppath)
   675 
   853 
   676     # TODO do the store swap here.
   854     # Now swap in the new store directory. Doing it as a rename should make
       
   855     # the operation nearly instantaneous and atomic (at least in well-behaved
       
   856     # environments).
       
   857     ui.write(_('replacing store...\n'))
       
   858     tstart = time.time()
       
   859     util.rename(srcrepo.spath, backupvfs.join('store'))
       
   860     util.rename(dstrepo.spath, srcrepo.spath)
       
   861     elapsed = time.time() - tstart
       
   862     ui.write(_('store replacement complete; repository was inconsistent for '
       
   863                '%0.1fs\n') % elapsed)
   677 
   864 
   678     # We first write the requirements file. Any new requirements will lock
   865     # We first write the requirements file. Any new requirements will lock
   679     # out legacy clients.
   866     # out legacy clients.
   680     ui.write(_('finalizing requirements file and making repository readable '
   867     ui.write(_('finalizing requirements file and making repository readable '
   681                'again\n'))
   868                'again\n'))