hgext/shelve.py
changeset 25713 2ca116614cfc
parent 25712 8a6264a2ee60
child 25774 4f8c20fe66f0
equal deleted inserted replaced
25712:8a6264a2ee60 25713:2ca116614cfc
    38 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    38 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    39 # be specifying the version(s) of Mercurial they are tested with, or
    39 # be specifying the version(s) of Mercurial they are tested with, or
    40 # leave the attribute unspecified.
    40 # leave the attribute unspecified.
    41 testedwith = 'internal'
    41 testedwith = 'internal'
    42 
    42 
       
    43 backupdir = 'shelve-backup'
       
    44 
    43 class shelvedfile(object):
    45 class shelvedfile(object):
    44     """Helper for the file storing a single shelve
    46     """Helper for the file storing a single shelve
    45 
    47 
    46     Handles common functions on shelve files (.hg/.patch) using
    48     Handles common functions on shelve files (.hg/.patch) using
    47     the vfs layer"""
    49     the vfs layer"""
    48     def __init__(self, repo, name, filetype=None):
    50     def __init__(self, repo, name, filetype=None):
    49         self.repo = repo
    51         self.repo = repo
    50         self.name = name
    52         self.name = name
    51         self.vfs = scmutil.vfs(repo.join('shelved'))
    53         self.vfs = scmutil.vfs(repo.join('shelved'))
    52         self.backupvfs = scmutil.vfs(repo.join('shelve-backup'))
    54         self.backupvfs = scmutil.vfs(repo.join(backupdir))
    53         self.ui = self.repo.ui
    55         self.ui = self.repo.ui
    54         if filetype:
    56         if filetype:
    55             self.fname = name + '.' + filetype
    57             self.fname = name + '.' + filetype
    56         else:
    58         else:
    57             self.fname = name
    59             self.fname = name
   153         fp.close()
   155         fp.close()
   154 
   156 
   155     @classmethod
   157     @classmethod
   156     def clear(cls, repo):
   158     def clear(cls, repo):
   157         util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
   159         util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
       
   160 
       
   161 def cleanupoldbackups(repo):
       
   162     vfs = scmutil.vfs(repo.join(backupdir))
       
   163     maxbackups = repo.ui.configint('shelve', 'maxbackups', 10)
       
   164     hgfiles = [f for f in vfs.listdir() if f.endswith('.hg')]
       
   165     hgfiles = sorted([(vfs.stat(f).st_mtime, f) for f in hgfiles])
       
   166     for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
       
   167         base = f[:-3]
       
   168         for ext in 'hg patch'.split():
       
   169             try:
       
   170                 vfs.unlink(base + '.' + ext)
       
   171             except OSError as err:
       
   172                 if err.errno != errno.ENOENT:
       
   173                     raise
   158 
   174 
   159 def createcmd(ui, repo, pats, opts):
   175 def createcmd(ui, repo, pats, opts):
   160     """subcommand that creates a new shelve"""
   176     """subcommand that creates a new shelve"""
   161 
   177 
   162     def publicancestors(ctx):
   178     def publicancestors(ctx):
   296         wlock = repo.wlock()
   312         wlock = repo.wlock()
   297         for (name, _type) in repo.vfs.readdir('shelved'):
   313         for (name, _type) in repo.vfs.readdir('shelved'):
   298             suffix = name.rsplit('.', 1)[-1]
   314             suffix = name.rsplit('.', 1)[-1]
   299             if suffix in ('hg', 'patch'):
   315             if suffix in ('hg', 'patch'):
   300                 shelvedfile(repo, name).movetobackup()
   316                 shelvedfile(repo, name).movetobackup()
       
   317             cleanupoldbackups(repo)
   301     finally:
   318     finally:
   302         lockmod.release(wlock)
   319         lockmod.release(wlock)
   303 
   320 
   304 def deletecmd(ui, repo, pats):
   321 def deletecmd(ui, repo, pats):
   305     """subcommand that deletes a specific shelve"""
   322     """subcommand that deletes a specific shelve"""
   308     wlock = repo.wlock()
   325     wlock = repo.wlock()
   309     try:
   326     try:
   310         for name in pats:
   327         for name in pats:
   311             for suffix in 'hg patch'.split():
   328             for suffix in 'hg patch'.split():
   312                 shelvedfile(repo, name, suffix).movetobackup()
   329                 shelvedfile(repo, name, suffix).movetobackup()
       
   330         cleanupoldbackups(repo)
   313     except OSError as err:
   331     except OSError as err:
   314         if err.errno != errno.ENOENT:
   332         if err.errno != errno.ENOENT:
   315             raise
   333             raise
   316         raise util.Abort(_("shelved change '%s' not found") % name)
   334         raise util.Abort(_("shelved change '%s' not found") % name)
   317     finally:
   335     finally:
   457 def unshelvecleanup(ui, repo, name, opts):
   475 def unshelvecleanup(ui, repo, name, opts):
   458     """remove related files after an unshelve"""
   476     """remove related files after an unshelve"""
   459     if not opts['keep']:
   477     if not opts['keep']:
   460         for filetype in 'hg patch'.split():
   478         for filetype in 'hg patch'.split():
   461             shelvedfile(repo, name, filetype).movetobackup()
   479             shelvedfile(repo, name, filetype).movetobackup()
       
   480         cleanupoldbackups(repo)
   462 
   481 
   463 def unshelvecontinue(ui, repo, state, opts):
   482 def unshelvecontinue(ui, repo, state, opts):
   464     """subcommand to continue an in-progress unshelve"""
   483     """subcommand to continue an in-progress unshelve"""
   465     # We're finishing off a merge. First parent is our original
   484     # We're finishing off a merge. First parent is our original
   466     # parent, second is the temporary "fake" commit we're unshelving.
   485     # parent, second is the temporary "fake" commit we're unshelving.
   532     will not be moved until you successfully complete the unshelve.)
   551     will not be moved until you successfully complete the unshelve.)
   533 
   552 
   534     (Alternatively, you can use ``--abort`` to abandon an unshelve
   553     (Alternatively, you can use ``--abort`` to abandon an unshelve
   535     that causes a conflict. This reverts the unshelved changes, and
   554     that causes a conflict. This reverts the unshelved changes, and
   536     leaves the bundle in place.)
   555     leaves the bundle in place.)
       
   556 
       
   557     After a successful unshelve, the shelved changes are stored in a
       
   558     backup directory. Only the N most recent backups are kept. N
       
   559     defaults to 10 but can be overridden using the shelve.maxbackups
       
   560     configuration option.
   537     """
   561     """
   538     abortf = opts['abort']
   562     abortf = opts['abort']
   539     continuef = opts['continue']
   563     continuef = opts['continue']
   540     if not abortf and not continuef:
   564     if not abortf and not continuef:
   541         cmdutil.checkunfinished(repo)
   565         cmdutil.checkunfinished(repo)