Automatically generate patch names for uncommit
[stgit] / stgit / commands / uncommit.py
index 52ce5a8..45a2087 100644 (file)
@@ -24,20 +24,24 @@ from stgit.commands.common import *
 from stgit.utils import *
 from stgit import stack, git
 
-help = 'turn regular git commits into StGIT patches'
-usage = """%prog [options] <patchname1> [<patchname2> ... ]
+help = 'turn regular GIT commits into StGIT patches'
+usage = """%prog [options] [<patchname1> [<patchname2> ... ]]
 
 Take one or more git commits at the base of the current stack and turn
 them into StGIT patches. The new patches are created as applied patches
 at the bottom of the stack. This is the exact opposite of 'stg commit'.
 
-By default, the number of the patches to uncommit is determined by the
+By default, the number of patches to uncommit is determined by the
 number of patch names provided on the command line. First name is used
 for the first patch to uncommit, i.e. for the newest patch.
 
 The --number option specifies the number of patches to uncommit.  In
-this case, only one patch name may be specified. It is used as prefix to
-which the patch number is appended.
+this case, at most one patch name may be specified. It is used as
+prefix to which the patch number is appended.
+
+If no patch names are provided on the command line, StGIT
+automatically generates them based on the first line of the patch
+description.
 
 Only commits with exactly one parent can be uncommitted; in other
 words, you can't uncommit a merge."""
@@ -46,33 +50,57 @@ options = [make_option('-n', '--number', type = 'int',
                        help = 'uncommit the specified number of commits')]
 
 def func(parser, options, args):
-    if len(args) == 0:
-        parser.error('you must specify at least one patch name')
+    """Uncommit a number of patches.
+    """
     if options.number:
-        if len(args) != 1:
-            parser.error('when using --number, specify exactly one patch name')
-        patchnames = ['%s%d' % (args[0], i)
-                      for i in xrange(options.number - 1, -1, -1)]
+        if options.number <= 0:
+            parser.error('invalid value passed to --number')
+
+        patch_nr = options.number
+
+        if len(args) == 0:
+            patchnames = None
+        elif len(args) == 1:
+            # prefix specified
+            patchnames = ['%s%d' % (args[0], i)
+                          for i in xrange(patch_nr, 0, -1)]
+        else:
+            parser.error('when using --number, specify at most one patch name')
+    elif len(args) == 0:
+        patchnames = None
+        patch_nr = 1
     else:
         patchnames = args
+        patch_nr = len(patchnames)
 
     if crt_series.get_protected():
-        raise CmdException, 'This branch is protected. Uncommit is not permitted'
+        raise CmdException, \
+              'This branch is protected. Uncommit is not permitted'
 
-    print 'Uncommitting %d patches...' % len(patchnames),
+    print 'Uncommitting %d patches...' % patch_nr,
     sys.stdout.flush()
 
-    for patchname in patchnames:
-        base_file = crt_series.get_base_file()
+    base_file = crt_series.get_base_file()
+
+    for n in xrange(0, patch_nr):
+        # retrieve the commit (only commits with a single parent are allowed)
         commit_id = read_string(base_file)
         commit = git.Commit(commit_id)
         try:
             parent, = commit.get_parents()
         except ValueError:
-            raise CmdException, ('Commit %s does not have exactly one parent'
-                                 % commit_id)
-        author_name, author_email, author_date = name_email_date(
-            commit.get_author())
+            raise CmdException, 'Commit %s does not have exactly one parent' \
+                  % commit_id
+        author_name, author_email, author_date = \
+                     name_email_date(commit.get_author())
+
+        if patchnames:
+            patchname = patchnames[n]
+        else:
+            patchname = make_patch_name(commit.get_log())
+        if not patchname:
+            raise CmdException, 'Unknown patch name for commit %s' % commit_id
+
         crt_series.new_patch(patchname,
                              can_edit = False, before_existing = True,
                              top = commit_id, bottom = parent,