diff --git a/hgext/mq.py b/hgext/mq.py --- a/hgext/mq.py +++ b/hgext/mq.py @@ -234,7 +234,12 @@ class patchheader(object): 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 @@ def finish(ui, repo, *revrange, **opts): 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 @@ cmdtable = { (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', diff --git a/tests/test-mq-qqueue b/tests/test-mq-qqueue new file mode 100755 --- /dev/null +++ b/tests/test-mq-qqueue @@ -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 .. diff --git a/tests/test-mq-qqueue.out b/tests/test-mq-qqueue.out new file mode 100644 --- /dev/null +++ b/tests/test-mq-qqueue.out @@ -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) diff --git a/tests/test-mq.out b/tests/test-mq.out --- a/tests/test-mq.out +++ b/tests/test-mq.out @@ -49,6 +49,7 @@ list of commands: 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