[PATCH 1 of 1] mq: add FREEZE functionality to protect patches from careless modification

FUJIWARA Katsunori fujiwara at ascade.co.jp
Sun Mar 15 06:32:22 CDT 2009


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1237116262 -32400
# Node ID 0a77551036fd520b86d9dd79b0298a563c671d03
# Parent  a536b9a43227db025d0991c689ed844fe34e8279
mq: add FREEZE functionality to protect patches from careless modification

FROZEN patch prevents qrefresh, qdelete, qfold and qfinish from
modifing its content.

diff -r a536b9a43227 -r 0a77551036fd hgext/mq.py
--- a/hgext/mq.py	Tue Mar 10 23:41:37 2009 +0100
+++ b/hgext/mq.py	Sun Mar 15 20:24:22 2009 +0900
@@ -129,6 +129,7 @@
         self.series_path = "series"
         self.status_path = "status"
         self.guards_path = "guards"
+        self.frozens_path = "frozens"
         self.active_guards = None
         self.guards_dirty = False
         self._diffopts = None
@@ -282,15 +283,19 @@
                     write(_('skipping %s - no matching guards\n') %
                           self.series[idx])
 
+    def write_list(self, items, path):
+        fp = self.opener(path, 'w')
+        for i in items:
+            fp.write("%s\n" % i)
+        fp.close()
+
     def save_dirty(self):
-        def write_list(items, path):
-            fp = self.opener(path, 'w')
-            for i in items:
-                fp.write("%s\n" % i)
-            fp.close()
-        if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
-        if self.series_dirty: write_list(self.full_series, self.series_path)
-        if self.guards_dirty: write_list(self.active_guards, self.guards_path)
+        if self.applied_dirty:
+            self.write_list(map(str, self.applied), self.status_path)
+        if self.series_dirty:
+            self.write_list(self.full_series, self.series_path)
+        if self.guards_dirty:
+            self.write_list(self.active_guards, self.guards_path)
 
     def readheaders(self, patch):
         def eatdiff(lines):
@@ -609,15 +614,20 @@
         firstrev = repo[self.applied[0].rev].rev()
         appliedbase = 0
         patches = []
+        frozens = self.get_frozens()
         for rev in util.sort(revs):
             if rev < firstrev:
                 raise util.Abort(_('revision %d is not managed') % rev)
-            base = bin(self.applied[appliedbase].rev)
+            applied = self.applied[appliedbase]
+            base = bin(applied.rev)
             node = repo.changelog.node(rev)
             if node != base:
                 raise util.Abort(_('cannot delete revision %d above '
                                    'applied patches') % rev)
-            patches.append(self.applied[appliedbase].name)
+            if applied.name in frozens:
+                raise util.Abort(_("cannot finish frozen patch %s") %
+                                 applied.name)
+            patches.append(applied.name)
             appliedbase += 1
 
         r = self.qrepo()
@@ -637,6 +647,7 @@
                                'patch name'))
 
         realpatches = []
+        frozens = self.get_frozens()
         for patch in patches:
             patch = self.lookup(patch, strict=True)
             info = self.isapplied(patch)
@@ -644,6 +655,8 @@
                 raise util.Abort(_("cannot delete applied patch %s") % patch)
             if patch not in self.series:
                 raise util.Abort(_("patch %s not in series file") % patch)
+            if patch in frozens:
+                raise util.Abort(_("cannot delete frozen patch %s") % patch)
             realpatches.append(patch)
 
         appliedbase = 0
@@ -1091,6 +1104,10 @@
         if newdate:
             newdate = '%d %d' % util.parsedate(newdate)
         wlock = repo.wlock()
+
+        if self.applied[-1].name in self.get_frozens():
+            raise util.Abort(_("cannot refresh frozen patch"))
+
         try:
             self.check_toppatch(repo)
             (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
@@ -1621,6 +1638,16 @@
         if qrepo:
             qrepo.add(added)
 
+    def get_frozens(self):
+        try:
+            return self.opener(self.frozens_path).read().split()
+        except IOError, err:
+            if err.errno != errno.ENOENT: raise
+            return []
+
+    def set_frozens(self, frozens):
+        self.write_list(frozens, self.frozens_path)
+
 def delete(ui, repo, *patches, **opts):
     """remove patches from queue
 
@@ -1937,12 +1964,15 @@
     parent = q.lookup('qtip')
     patches = []
     messages = []
+    frozens = q.get_frozens()
     for f in files:
         p = q.lookup(f)
         if p in patches or p == parent:
             ui.warn(_('Skipping already folded patch %s') % p)
         if q.isapplied(p):
             raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
+        if p in frozens:
+            raise util.Abort(_('cannot fold frozen patch %s') % p)
         patches.append(p)
 
     for p in patches:
@@ -2166,6 +2196,10 @@
 
     q.save_dirty()
 
+    frozens = q.get_frozens()
+    if patch in frozens:
+        q.set_frozens([x == patch and name or x for x in frozens])
+
 def restore(ui, repo, rev, **opts):
     """restore the queue state saved by a rev"""
     rev = repo.lookup(rev)
@@ -2453,6 +2487,81 @@
                                    kwargs.get('force'))
     return orig(ui, repo, *args, **kwargs)
 
+def freeze(ui, repo, *patches, **opts):
+    """freeze specified patches to protect them from careless modification.
+
+    Frozen patch prevents qrefresh, qdelete, qfold and qfinish from modifing
+    its content. qrename is allowed, because it does not modify content.
+
+    If none of '--series' opt, '--applied' opt, '--unapplied' opt nor 
+    patch name is specified, this treat 'qtop' as target patch.
+
+    With --release option, this command releases frozen status of target 
+    patches.
+
+    With --list option, this command shows all frozen patches.
+    """
+    if opts['list'] and opts['release']:
+        raise util.Abort(_("invalid combination of -l/-r"))
+        
+    v = ((opts['series'] and 1 or 0) +
+         (opts['applied'] and 1 or 0) +
+         (opts['unapplied'] and 1 or 0))
+    c = (v + (patches and 1 or 0))
+
+    if opts['list']:
+        if c:
+            raise util.Abort(_("-l cannot accept patch specification"))
+    else:
+        if 2 <= c:
+            raise util.Abort(_("invalid combination of -s/-A/-U and patches"))
+
+    if not patches: v += 1
+
+    q = repo.mq
+
+    if opts['list']:
+        frozens = q.get_frozens()
+        for frozen in frozens:
+            ui.write('%s\n' % (frozen))
+    else:
+        targets = []
+        if opts['series']:
+            for x in q.series: targets.append(x)
+        elif opts['applied']:
+            for x in q.applied: targets.append(x.name)
+        elif opts['unapplied']:
+            index = q.applied and q.series.index(q.applied[-1].name) + 1 or 0
+            for x in q.series[index:]: targets.append(x)
+        elif patches:
+            for x in patches:
+                normalized = q.lookup(x, strict=True)
+                if normalized not in q.series:
+                    raise util.Abort(_("unknown patch: %s") % x)
+                targets.append(normalized)
+        else:
+            if not q.applied:
+                raise util.Abort(_("cannot determine target patch"))
+            targets.append(q.applied[-1].name)
+
+        frozens = dict.fromkeys(q.get_frozens())
+        for x in targets:
+            if opts['release']:
+                if frozens.has_key(x):
+                    del frozens[x]
+                    if v:
+                        ui.write(_("release: %s\n") % x)
+                else:
+                    ui.note(_("not frozen: %s\n") % x)
+            else:
+                if frozens.has_key(x):
+                    ui.note(_("already frozen: %s\n") % x)
+                else:
+                    frozens[x] = x
+                    if v:
+                        ui.write(_("freeze: %s\n") % x)
+        q.set_frozens([x for x in q.series if frozens.has_key(x)])
+
 def uisetup(ui):
     extensions.wrapcommand(commands.table, 'import', mqimport)
 
@@ -2488,6 +2597,15 @@
           ('k', 'keep', None, _('keep folded patch files')),
          ] + commands.commitopts,
          _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
+    'qfreeze':
+        (freeze,
+         [('s', 'series', None, _('treat patches in series as targets')),
+          ('A', 'applied', None, _('treat patches already applied as targets')),
+          ('U', 'unapplied', None, _('treat patches not yet applied as targets')),
+          ('r', 'release', None, _('release frozen patches')),
+          ('l', 'list', None, _('list all frozen patches')),
+         ],
+         _('hg qfreeze [OPTION]... [PATCH]...')),
     'qgoto':
         (goto,
          [('f', 'force', None, _('overwrite any local changes'))],



More information about the Mercurial-devel mailing list