Import git show output easily
[stgit] / stgit / commands / commit.py
index a3b7277..dd8d6e6 100644 (file)
@@ -15,51 +15,94 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
-import sys, os
-from optparse import OptionParser, make_option
-
-from stgit.commands.common import *
-from stgit.utils import *
-from stgit import stack, git
-
-help = 'permanently store the applied patches into stack base'
-usage = """%prog [options]
-
-Merge the applied patches into the base of the current stack and
-remove them from the series while advancing the base.
-
-Use this command only if you want to permanently store the applied
-patches and no longer manage them with StGIT."""
-
-options = []
-
+from stgit.argparse import opt
+from stgit.commands import common
+from stgit.lib import transaction
+from stgit.out import *
+from stgit import argparse
+
+help = 'Permanently store the applied patches into the stack base'
+kind = 'stack'
+usage = ['',
+         '<patchnames>',
+         '-n NUM',
+         '--all']
+description = """
+Merge one or more patches into the base of the current stack and
+remove them from the series while advancing the base. This is the
+opposite of 'stg uncommit'. Use this command if you no longer want to
+manage a patch with StGIT.
+
+By default, the bottommost patch is committed. If patch names are
+given, the stack is rearranged so that those patches are at the
+bottom, and then they are committed.
+
+The -n/--number option specifies the number of applied patches to
+commit (counting from the bottom of the stack). If -a/--all is given,
+all applied patches are committed."""
+
+args = [argparse.patch_range(argparse.applied_patches,
+                             argparse.unapplied_patches)]
+options = [
+    opt('-n', '--number', type = 'int',
+        short = 'Commit the specified number of patches'),
+    opt('-a', '--all', action = 'store_true',
+        short = 'Commit all applied patches')]
+
+directory = common.DirectoryHasRepositoryLib()
 
 def func(parser, options, args):
-    """Merge the applied patches into the base of the current stack
-       and remove them from the series while advancing the base
-    """
-    if len(args) != 0:
-        parser.error('incorrect number of arguments')
-
-    check_local_changes()
-    check_conflicts()
-    check_head_top_equal()
-
-    applied = crt_series.get_applied()
-    if not applied:
-        raise CmdException, 'No patches applied'
-
-    if crt_series.get_protected():
-        raise CmdException, 'This branch is protected.  Commit is not permitted'
-
-    crt_head = git.get_head()
-
-    print 'Committing %d patches...' % len(applied),
-
-    crt_series.pop_patch(applied[0])
-    git.switch(crt_head)
-
-    for patch in applied:
-        crt_series.delete_patch(patch)
-
-    print 'done'
+    """Commit a number of patches."""
+    stack = directory.repository.current_stack
+    args = common.parse_patches(args, list(stack.patchorder.all_visible))
+    if len([x for x in [args, options.number != None, options.all] if x]) > 1:
+        parser.error('too many options')
+    if args:
+        patches = [pn for pn in stack.patchorder.all_visible if pn in args]
+        bad = set(args) - set(patches)
+        if bad:
+            raise common.CmdException('Nonexistent or hidden patch names: %s'
+                                      % ', '.join(sorted(bad)))
+    elif options.number != None:
+        if options.number <= len(stack.patchorder.applied):
+            patches = stack.patchorder.applied[:options.number]
+        else:
+            raise common.CmdException('There are not that many applied patches')
+    elif options.all:
+        patches = stack.patchorder.applied
+    else:
+        patches = stack.patchorder.applied[:1]
+    if not patches:
+        raise common.CmdException('No patches to commit')
+
+    iw = stack.repository.default_iw
+    def allow_conflicts(trans):
+        # As long as the topmost patch stays where it is, it's OK to
+        # run "stg commit" with conflicts in the index.
+        return len(trans.applied) >= 1
+    trans = transaction.StackTransaction(stack, 'commit',
+                                         allow_conflicts = allow_conflicts)
+    try:
+        common_prefix = 0
+        for i in xrange(min(len(stack.patchorder.applied), len(patches))):
+            if stack.patchorder.applied[i] == patches[i]:
+                common_prefix += 1
+        if common_prefix < len(patches):
+            to_push = trans.pop_patches(
+                lambda pn: pn in stack.patchorder.applied[common_prefix:])
+            for pn in patches[common_prefix:]:
+                trans.push_patch(pn, iw)
+        else:
+            to_push = []
+        new_base = trans.patches[patches[-1]]
+        for pn in patches:
+            trans.patches[pn] = None
+        trans.applied = [pn for pn in trans.applied if pn not in patches]
+        trans.base = new_base
+        out.info('Committed %d patch%s' % (len(patches),
+                                           ['es', ''][len(patches) == 1]))
+        for pn in to_push:
+            trans.push_patch(pn, iw)
+    except transaction.TransactionHalted:
+        pass
+    return trans.run(iw)