[PATCH] [RFC] rollback -n

Brendan Cully brendan at kublai.com
Mon Mar 31 19:04:46 CDT 2008


Here's a hacky version of a -n flag for rollback, which prints the
revision to which rollback would revert the repository without
actually reverting it.

I wouldn't call it finished, and I'm not particularly happy about the
way this patch overrides the changelog opener. Nor have I tested it
with revlogv0. This patch is really a request for comments.
-------------- next part --------------
# HG changeset patch
# User Brendan Cully <brendan at kublai.com>
# Date 1207008092 25200
# Node ID ed1a5aa8e1721d9b977ad1694763dfff706ae1a2
# Parent  626a8e39684656e356c68fd178b255f6b9cb0ab2
Implement dry run for rollback

diff -r 626a8e396846 -r ed1a5aa8e172 mercurial/commands.py
--- a/mercurial/commands.py	Mon Mar 31 18:49:15 2008 +0200
+++ b/mercurial/commands.py	Mon Mar 31 17:01:32 2008 -0700
@@ -2447,7 +2447,7 @@
     finally:
         del wlock
 
-def rollback(ui, repo):
+def rollback(ui, repo, **opts):
     """roll back the last transaction
 
     This command should be used with care. There is only one level of
@@ -2473,7 +2473,15 @@
     repository; for example an in-progress pull from the repository
     may fail if a rollback is performed.
     """
-    repo.rollback()
+    rbtip = repo.rollback(dryrun=opts.get('dry_run'))
+    if not rbtip:
+        return 1
+
+    rev = repo.changelog.rev(rbtip)
+    if opts.get('dry_run'):
+        ui.status('would roll back to revision %d\n' % rev)
+    else:
+        ui.note('rolled back to revision %d\n' % rev)
 
 def root(ui, repo):
     """print the root (top) of the current working dir
@@ -3196,7 +3204,7 @@
           ('', 'no-backup', None, _('do not save backup copies of files')),
          ] + walkopts + dryrunopts,
          _('hg revert [OPTION]... [-r REV] [NAME]...')),
-    "rollback": (rollback, [], _('hg rollback')),
+    "rollback": (rollback, dryrunopts, _('hg rollback')),
     "root": (root, [], _('hg root')),
     "^serve":
         (serve,
diff -r 626a8e396846 -r ed1a5aa8e172 mercurial/localrepo.py
--- a/mercurial/localrepo.py	Mon Mar 31 18:49:15 2008 +0200
+++ b/mercurial/localrepo.py	Mon Mar 31 17:01:32 2008 -0700
@@ -608,12 +608,24 @@
         finally:
             del l
 
-    def rollback(self):
+    def rollback(self, dryrun=False):
+        def szopener(opener, sz):
+            def o(*args, **kwargs):
+                kwargs['size'] = sz
+                return opener(*args, **kwargs)
+            return o
+
         wlock = lock = None
         try:
             wlock = self.wlock()
             lock = self.lock()
             if os.path.exists(self.sjoin("undo")):
+                if dryrun:
+                    files = transaction.parse(self.sjoin("undo"))
+                    clo = files['00changelog.i']
+                    opener = szopener(self.sopener, clo)
+                    cl = changelog.changelog(opener)
+                    return cl.tip()
                 self.ui.status(_("rolling back last transaction\n"))
                 transaction.rollback(self.sopener, self.sjoin("undo"))
                 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
@@ -626,6 +638,7 @@
                                  % util.tolocal(self.dirstate.branch()))
                 self.invalidate()
                 self.dirstate.invalidate()
+                return self.changelog.tip()
             else:
                 self.ui.warn(_("no rollback information available\n"))
         finally:
diff -r 626a8e396846 -r ed1a5aa8e172 mercurial/transaction.py
--- a/mercurial/transaction.py	Mon Mar 31 18:49:15 2008 +0200
+++ b/mercurial/transaction.py	Mon Mar 31 17:01:32 2008 -0700
@@ -92,13 +92,16 @@
 
         self.report(_("rollback completed\n"))
 
-def rollback(opener, file):
+def parse(file):
     files = {}
     for l in open(file).readlines():
         f, o = l.split('\0')
-        files[f] = o
+        files[f] = int(o)
+    return files
+
+def rollback(opener, file):
+    files = parse(file)
     for f in files:
         o = files[f]
-        opener(f, "a").truncate(int(o))
+        opener(f, "a").truncate(o)
     os.unlink(file)
-
diff -r 626a8e396846 -r ed1a5aa8e172 mercurial/util.py
--- a/mercurial/util.py	Mon Mar 31 18:49:15 2008 +0200
+++ b/mercurial/util.py	Mon Mar 31 17:01:32 2008 -0700
@@ -751,9 +751,22 @@
 def fstat(fp):
     '''stat file object that may not have fileno method.'''
     try:
-        return os.fstat(fp.fileno())
+        sb = os.fstat(fp.fileno())
     except AttributeError:
-        return os.stat(fp.name)
+        sb = os.stat(fp.name)
+
+    if hasattr(fp, 'size'):
+        class sstat(object):
+            def __init__(self, sb, sz):
+                self._obj = sb
+                self.st_size = sz
+            
+            def __getattr__(self, name):
+                return getattr(self._obj, name)
+
+        return sstat(sb, fp.size)
+
+    return sb
 
 posixfile = file
 
@@ -1394,6 +1407,31 @@
     makedirs(parent, mode)
     makedirs(name, mode)
 
+class sizedfile(object):
+    "Wrap a file object to limit its apparent size"
+    def __init__(self, fp, size):
+        self._fp = fp
+        self.size = size
+
+    def __getattr__(self, name):
+        return getattr(self._fp, name)
+
+    def read(self, sz=None):
+        if sz is None:
+            sz = self.size
+        maxsz = self.size - self.tell()
+        if sz > maxsz:
+            sz = maxsz
+
+        return self._fp.read(sz)
+
+    def seek(self, offset, whence=0):
+        if whence == 2:
+            offset = offset + self.size
+            whence = 0
+
+        return self._fp.seek(offset, whence)
+
 class opener(object):
     """Open files relative to a base directory
 
@@ -1419,7 +1457,7 @@
             return
         os.chmod(name, self.createmode & 0666)
 
-    def __call__(self, path, mode="r", text=False, atomictemp=False):
+    def __call__(self, path, mode="r", text=False, atomictemp=False, size=None):
         self.audit_path(path)
         f = os.path.join(self.base, path)
 
@@ -1442,6 +1480,8 @@
         fp = posixfile(f, mode)
         if nlink == 0:
             self._fixfilemode(f)
+        if size is not None:
+            fp = sizedfile(fp, size)
         return fp
 
     def symlink(self, src, dst):


More information about the Mercurial-devel mailing list