shelve: only keep the latest N shelve backups
authorColin Chan <colinchan@fb.com>
Wed, 01 Jul 2015 13:14:03 -0700
changeset 25713 2ca116614cfc
parent 25712 8a6264a2ee60
child 25716 d50677c3bf44
shelve: only keep the latest N shelve backups This will keep the backup directory from growing indefinitely. The number of backups to keep can be set using the shelve.maxbackups config option (defaults to 10 backups).
hgext/shelve.py
tests/test-shelve.t
--- a/hgext/shelve.py	Wed Jul 01 13:13:02 2015 -0700
+++ b/hgext/shelve.py	Wed Jul 01 13:14:03 2015 -0700
@@ -40,6 +40,8 @@
 # leave the attribute unspecified.
 testedwith = 'internal'
 
+backupdir = 'shelve-backup'
+
 class shelvedfile(object):
     """Helper for the file storing a single shelve
 
@@ -49,7 +51,7 @@
         self.repo = repo
         self.name = name
         self.vfs = scmutil.vfs(repo.join('shelved'))
-        self.backupvfs = scmutil.vfs(repo.join('shelve-backup'))
+        self.backupvfs = scmutil.vfs(repo.join(backupdir))
         self.ui = self.repo.ui
         if filetype:
             self.fname = name + '.' + filetype
@@ -156,6 +158,20 @@
     def clear(cls, repo):
         util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
 
+def cleanupoldbackups(repo):
+    vfs = scmutil.vfs(repo.join(backupdir))
+    maxbackups = repo.ui.configint('shelve', 'maxbackups', 10)
+    hgfiles = [f for f in vfs.listdir() if f.endswith('.hg')]
+    hgfiles = sorted([(vfs.stat(f).st_mtime, f) for f in hgfiles])
+    for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
+        base = f[:-3]
+        for ext in 'hg patch'.split():
+            try:
+                vfs.unlink(base + '.' + ext)
+            except OSError as err:
+                if err.errno != errno.ENOENT:
+                    raise
+
 def createcmd(ui, repo, pats, opts):
     """subcommand that creates a new shelve"""
 
@@ -298,6 +314,7 @@
             suffix = name.rsplit('.', 1)[-1]
             if suffix in ('hg', 'patch'):
                 shelvedfile(repo, name).movetobackup()
+            cleanupoldbackups(repo)
     finally:
         lockmod.release(wlock)
 
@@ -310,6 +327,7 @@
         for name in pats:
             for suffix in 'hg patch'.split():
                 shelvedfile(repo, name, suffix).movetobackup()
+        cleanupoldbackups(repo)
     except OSError as err:
         if err.errno != errno.ENOENT:
             raise
@@ -459,6 +477,7 @@
     if not opts['keep']:
         for filetype in 'hg patch'.split():
             shelvedfile(repo, name, filetype).movetobackup()
+        cleanupoldbackups(repo)
 
 def unshelvecontinue(ui, repo, state, opts):
     """subcommand to continue an in-progress unshelve"""
@@ -534,6 +553,11 @@
     (Alternatively, you can use ``--abort`` to abandon an unshelve
     that causes a conflict. This reverts the unshelved changes, and
     leaves the bundle in place.)
+
+    After a successful unshelve, the shelved changes are stored in a
+    backup directory. Only the N most recent backups are kept. N
+    defaults to 10 but can be overridden using the shelve.maxbackups
+    configuration option.
     """
     abortf = opts['abort']
     continuef = opts['continue']
--- a/tests/test-shelve.t	Wed Jul 01 13:13:02 2015 -0700
+++ b/tests/test-shelve.t	Wed Jul 01 13:14:03 2015 -0700
@@ -5,6 +5,8 @@
   > [defaults]
   > diff = --nodates --git
   > qnew = --date '0 0'
+  > [shelve]
+  > maxbackups = 2
   > EOF
 
   $ hg init repo
@@ -248,6 +250,14 @@
     c
   R b/b
 
+ensure old shelve backups are being deleted automatically
+
+  $ ls .hg/shelve-backup/
+  default-01.hg
+  default-01.patch
+  wibble.hg
+  wibble.patch
+
 cause unshelving to result in a merge with 'a' conflicting
 
   $ hg shelve -q