[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