Allow patch ranges for the 'pop' command
[stgit] / stgit / commands / pop.py
index 64848f8..0dfaad9 100644 (file)
@@ -24,63 +24,66 @@ from stgit.utils import *
 from stgit import stack, git
 
 
-help = 'pop the top of the series'
-usage = """%prog [options]
+help = 'pop one or more patches from the stack'
+usage = """%prog [options] [<patch1>] [<patch2>] [<patch3>..<patch4>]
 
-Pop the topmost patch or a range of patches starting with the topmost
-one from the stack. The command fails if there are local changes or
-conflicts."""
+Pop the topmost patch or a range of patches from the stack. The
+command fails if there are conflicts or local changes (and --keep was
+not specified).
+
+A series of pop and push operations are performed so that only the
+patches passed on the command line are popped from the stack. Some of
+the push operations may fail because of conflicts (push --undo would
+revert the last push operation)."""
 
 options = [make_option('-a', '--all',
                        help = 'pop all the applied patches',
                        action = 'store_true'),
            make_option('-n', '--number', type = 'int',
                        help = 'pop the specified number of patches'),
-           make_option('-t', '--to', metavar = 'PATCH',
-                       help = 'pop all patches up to PATCH')]
+           make_option('-k', '--keep',
+                       help = 'keep the local changes',
+                       action = 'store_true')]
 
 
 def func(parser, options, args):
     """Pop the topmost patch from the stack
     """
-    if len(args) != 0:
-        parser.error('incorrect number of arguments')
-
-    check_local_changes()
     check_conflicts()
     check_head_top_equal()
 
+    if not options.keep:
+        check_local_changes()
+
     applied = crt_series.get_applied()
     if not applied:
         raise CmdException, 'No patches applied'
-    applied.reverse()
-
-    if options.to:
-        if options.to not in applied:
-            if options.to in crt_series.get_unapplied():
-                raise CmdException, 'Patch "%s" is not currently applied.' % options.to
-            else:
-                raise CmdException, 'Patch "%s" does not exist.' % options.to
-        patches = applied[:applied.index(options.to)]
-    elif options.number:
-        patches = applied[:options.number]
-    elif options.all:
+
+    if options.all:
         patches = applied
+    elif options.number:
+        # reverse it twice to also work with negative or bigger than
+        # the length numbers
+        patches = applied[::-1][:options.number][::-1]
+    elif len(args) == 0:
+        patches = [applied[-1]]
     else:
-        patches = [applied[0]]
+        patches = parse_patches(args, applied, ordered = True)
 
-    if patches == []:
+    if not patches:
         raise CmdException, 'No patches to pop'
 
-    # pop everything to the given patch
-    p = patches[-1]
-    if len(patches) == 1:
-        print 'Popping patch "%s"...' % p,
-    else:
-        print 'Popping "%s" - "%s" patches...' % (patches[0], p),
-    sys.stdout.flush()
+    # pop to the most distant popped patch
+    topop = applied[applied.index(patches[0]):]
+    # push those not in the popped range
+    topush = [p for p in topop if p not in patches]
+
+    if options.keep and topush:
+        raise CmdException, 'Cannot pop arbitrary patches with --keep'
 
-    crt_series.pop_patch(p)
+    topop.reverse()
+    pop_patches(topop, options.keep)
+    if topush:
+        push_patches(topush)
 
-    print 'done'
     print_crt_patch()