mq: support multiple patch queues using qqueue
authorHenrik Stuart <hg@hstuart.dk>
Sat, 29 May 2010 20:32:39 +0200
changeset 11229 1e701ffd9df4
parent 11228 5cdac5c35e68
child 11230 5116a077c3da
mq: support multiple patch queues using qqueue
hgext/mq.py
tests/test-mq-qqueue
tests/test-mq-qqueue.out
tests/test-mq.out
--- a/hgext/mq.py	Fri May 28 16:01:57 2010 -0500
+++ b/hgext/mq.py	Sat May 29 20:32:39 2010 +0200
@@ -234,7 +234,12 @@
 class queue(object):
     def __init__(self, ui, path, patchdir=None):
         self.basepath = path
-        self.path = patchdir or os.path.join(path, "patches")
+        try:
+            fh = open(os.path.join(path, '.queue'))
+            curpath = os.path.join(path, fh.read().rstrip())
+        except IOError:
+            curpath = os.path.join(path, 'patches')
+        self.path = patchdir or curpath
         self.opener = util.opener(self.path)
         self.ui = ui
         self.applied_dirty = 0
@@ -2533,6 +2538,107 @@
     q.save_dirty()
     return 0
 
+def qqueue(ui, repo, name=None, **opts):
+    '''manage multiple patch queues
+
+    Supports switching between different patch queues, as well as creating
+    new patch queues and deleting existing ones.
+
+    Omitting a queue name or specifying -l/--list will show you the registered
+    queues - by default the "normal" patches queue is registered. The currently
+    active queue will be marked with "(active)".
+
+    To create a new queue, use -c/--create. The queue is automatically made
+    active, except in the case where there are applied patches from the
+    currently active queue in the repository. Then the queue will only be
+    created and switching will fail.
+
+    To delete an existing queue, use --delete. You cannot delete the currently
+    active queue.
+    '''
+
+    q = repo.mq
+
+    _defaultqueue = 'patches'
+    _allqueues = '.queues'
+    _activequeue = '.queue'
+
+    def _getcurrent():
+        return os.path.basename(q.path)
+
+    def _noqueues():
+        try:
+            fh = repo.opener(_allqueues, 'r')
+            fh.close()
+        except IOError:
+            return True
+
+        return False
+
+    def _getqueues():
+        current = _getcurrent()
+
+        try:
+            fh = repo.opener(_allqueues, 'r')
+            queues = [queue.strip() for queue in fh if queue.strip()]
+            if current not in queues:
+                queues.append(current)
+        except IOError:
+            queues = [_defaultqueue]
+
+        return sorted(queues)
+
+    def _setactive(name):
+        if q.applied:
+            raise util.Abort(_('patches applied - cannot set new queue active'))
+
+        fh = repo.opener(_activequeue, 'w')
+        fh.write(name)
+        fh.close()
+
+    def _addqueue(name):
+        fh = repo.opener(_allqueues, 'a')
+        fh.write('%s\n' % (name,))
+        fh.close()
+
+    if not name or opts.get('list'):
+        current = _getcurrent()
+        for queue in _getqueues():
+            ui.write('%s' % (queue,))
+            if queue == current:
+                ui.write(_(' (active)\n'))
+            else:
+                ui.write('\n')
+        return
+
+    existing = _getqueues()
+
+    if name not in existing and opts.get('delete'):
+        raise util.Abort(_('cannot delete queue that does not exist'))
+    elif name not in existing and not opts.get('create'):
+        raise util.Abort(_('use --create to create a new queue'))
+
+    if opts.get('create'):
+        if _noqueues():
+            _addqueue(_defaultqueue)
+        _addqueue(name)
+        _setactive(name)
+    elif opts.get('delete'):
+        current = _getcurrent()
+
+        if name == current:
+            raise util.Abort(_('cannot delete currently active queue'))
+
+        fh = repo.opener('.queues.new', 'w')
+        for queue in existing:
+            if queue == name:
+                continue
+            fh.write('%s\n' % (queue,))
+        fh.close()
+        util.rename(repo.join('.queues.new'), repo.join(_allqueues))
+    else:
+        _setactive(name)
+
 def reposetup(ui, repo):
     class mqrepo(repo.__class__):
         @util.propertycache
@@ -2839,6 +2945,14 @@
         (finish,
          [('a', 'applied', None, _('finish all applied changesets'))],
          _('hg qfinish [-a] [REV]...')),
+    'qqueue':
+        (qqueue,
+         [
+             ('l', 'list', False, _('list all available queues')),
+             ('c', 'create', False, _('create new queue')),
+             ('', 'delete', False, _('delete reference to queue')),
+         ],
+         _('[OPTION] [QUEUE]')),
 }
 
 colortable = {'qguard.negative': 'red',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qqueue	Sat May 29 20:32:39 2010 +0200
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+echo "[extensions]" >> $HGRCPATH
+echo "mq=" >> $HGRCPATH
+
+hg init foo
+cd foo
+echo a > a
+hg ci -qAm a
+
+echo %% default queue
+hg qqueue
+
+echo b > a
+hg qnew -fgDU somestuff
+
+echo %% applied patches in default queue
+hg qap
+
+echo %% try to change patch \(create succeeds, switch fails\)
+hg qqueue foo --create
+hg qqueue
+
+echo %% empty default queue
+hg qpop
+
+echo %% switch queue
+hg qqueue foo
+hg qqueue
+
+echo %% unapplied patches
+hg qun
+echo c > a
+hg qnew -fgDU otherstuff
+
+echo %% fail switching back
+hg qqueue patches
+
+echo %% fail deleting current
+hg qqueue foo --delete
+
+echo %% switch back and delete foo
+hg qpop -a
+hg qqueue patches
+hg qqueue foo --delete
+hg qqueue
+cd ..
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qqueue.out	Sat May 29 20:32:39 2010 +0200
@@ -0,0 +1,23 @@
+%% default queue
+patches (active)
+%% applied patches in default queue
+somestuff
+%% try to change patch (create succeeds, switch fails)
+abort: patches applied - cannot set new queue active
+foo
+patches (active)
+%% empty default queue
+popping somestuff
+patch queue now empty
+%% switch queue
+foo (active)
+patches
+%% unapplied patches
+%% fail switching back
+abort: patches applied - cannot set new queue active
+%% fail deleting current
+abort: cannot delete currently active queue
+%% switch back and delete foo
+popping otherstuff
+patch queue now empty
+patches (active)
--- a/tests/test-mq.out	Fri May 28 16:01:57 2010 -0500
+++ b/tests/test-mq.out	Sat May 29 20:32:39 2010 +0200
@@ -49,6 +49,7 @@
  qpop         pop the current patch off the stack
  qprev        print the name of the previous patch
  qpush        push the next patch onto the stack
+ qqueue       manage multiple patch queues
  qrefresh     update the current patch
  qrename      rename a patch
  qselect      set or print guarded patches to push