mercurial/repair.py
changeset 30777 7de7afd8bdd9
parent 30776 3997edc4a86d
child 30779 38aa1ca97b6a
equal deleted inserted replaced
30776:3997edc4a86d 30777:7de7afd8bdd9
     8 
     8 
     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 
    14 
    14 from .i18n import _
    15 from .i18n import _
    15 from .node import short
    16 from .node import short
    16 from . import (
    17 from . import (
    17     bundle2,
    18     bundle2,
    18     changegroup,
    19     changegroup,
    19     error,
    20     error,
    20     exchange,
    21     exchange,
    21     obsolete,
    22     obsolete,
       
    23     scmutil,
    22     util,
    24     util,
    23 )
    25 )
    24 
    26 
    25 def _bundle(repo, bases, heads, node, suffix, compress=True):
    27 def _bundle(repo, bases, heads, node, suffix, compress=True):
    26     """create a bundle with the specified revisions as a backup"""
    28     """create a bundle with the specified revisions as a backup"""
   635     # FUTURE consider adding some optimizations here for certain transitions.
   637     # FUTURE consider adding some optimizations here for certain transitions.
   636     # e.g. adding generaldelta could schedule parent redeltas.
   638     # e.g. adding generaldelta could schedule parent redeltas.
   637 
   639 
   638     return newactions
   640     return newactions
   639 
   641 
       
   642 def _upgraderepo(ui, srcrepo, dstrepo, requirements, actions):
       
   643     """Do the low-level work of upgrading a repository.
       
   644 
       
   645     The upgrade is effectively performed as a copy between a source
       
   646     repository and a temporary destination repository.
       
   647 
       
   648     The source repository is unmodified for as long as possible so the
       
   649     upgrade can abort at any time without causing loss of service for
       
   650     readers and without corrupting the source repository.
       
   651     """
       
   652     assert srcrepo.currentwlock()
       
   653     assert dstrepo.currentwlock()
       
   654 
       
   655     # TODO copy store
       
   656 
       
   657     backuppath = tempfile.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path)
       
   658     backupvfs = scmutil.vfs(backuppath)
       
   659 
       
   660     # Make a backup of requires file first, as it is the first to be modified.
       
   661     util.copyfile(srcrepo.join('requires'), backupvfs.join('requires'))
       
   662 
       
   663     # We install an arbitrary requirement that clients must not support
       
   664     # as a mechanism to lock out new clients during the data swap. This is
       
   665     # better than allowing a client to continue while the repository is in
       
   666     # an inconsistent state.
       
   667     ui.write(_('marking source repository as being upgraded; clients will be '
       
   668                'unable to read from repository\n'))
       
   669     scmutil.writerequires(srcrepo.vfs,
       
   670                           srcrepo.requirements | set(['upgradeinprogress']))
       
   671 
       
   672     ui.write(_('starting in-place swap of repository data\n'))
       
   673     ui.write(_('replaced files will be backed up at %s\n') %
       
   674              backuppath)
       
   675 
       
   676     # TODO do the store swap here.
       
   677 
       
   678     # We first write the requirements file. Any new requirements will lock
       
   679     # out legacy clients.
       
   680     ui.write(_('finalizing requirements file and making repository readable '
       
   681                'again\n'))
       
   682     scmutil.writerequires(srcrepo.vfs, requirements)
       
   683 
       
   684     return backuppath
       
   685 
   640 def upgraderepo(ui, repo, run=False, optimize=None):
   686 def upgraderepo(ui, repo, run=False, optimize=None):
   641     """Upgrade a repository in place."""
   687     """Upgrade a repository in place."""
   642     # Avoid cycle: cmdutil -> repair -> localrepo -> cmdutil
   688     # Avoid cycle: cmdutil -> repair -> localrepo -> cmdutil
   643     from . import localrepo
   689     from . import localrepo
   644 
   690 
   769         if unusedoptimize:
   815         if unusedoptimize:
   770             ui.write(_('additional optimizations are available by specifying '
   816             ui.write(_('additional optimizations are available by specifying '
   771                      '"--optimize <name>":\n\n'))
   817                      '"--optimize <name>":\n\n'))
   772             for i in unusedoptimize:
   818             for i in unusedoptimize:
   773                 ui.write(_('%s\n   %s\n\n') % (i.name, i.description))
   819                 ui.write(_('%s\n   %s\n\n') % (i.name, i.description))
       
   820         return
       
   821 
       
   822     # Else we're in the run=true case.
       
   823     ui.write(_('upgrade will perform the following actions:\n\n'))
       
   824     printrequirements()
       
   825     printupgradeactions()
       
   826 
       
   827     ui.write(_('beginning upgrade...\n'))
       
   828     with repo.wlock():
       
   829         with repo.lock():
       
   830             ui.write(_('repository locked and read-only\n'))
       
   831             # Our strategy for upgrading the repository is to create a new,
       
   832             # temporary repository, write data to it, then do a swap of the
       
   833             # data. There are less heavyweight ways to do this, but it is easier
       
   834             # to create a new repo object than to instantiate all the components
       
   835             # (like the store) separately.
       
   836             tmppath = tempfile.mkdtemp(prefix='upgrade.', dir=repo.path)
       
   837             backuppath = None
       
   838             try:
       
   839                 ui.write(_('creating temporary repository to stage migrated '
       
   840                            'data: %s\n') % tmppath)
       
   841                 dstrepo = localrepo.localrepository(repo.baseui,
       
   842                                                     path=tmppath,
       
   843                                                     create=True)
       
   844 
       
   845                 with dstrepo.wlock():
       
   846                     with dstrepo.lock():
       
   847                         backuppath = _upgraderepo(ui, repo, dstrepo, newreqs,
       
   848                                                   actions)
       
   849 
       
   850             finally:
       
   851                 ui.write(_('removing temporary repository %s\n') % tmppath)
       
   852                 repo.vfs.rmtree(tmppath, forcibly=True)
       
   853 
       
   854                 if backuppath:
       
   855                     ui.warn(_('copy of old repository backed up at %s\n') %
       
   856                             backuppath)
       
   857                     ui.warn(_('the old repository will not be deleted; remove '
       
   858                               'it to free up disk space once the upgraded '
       
   859                               'repository is verified\n'))