Convert 'hide' to the lib infrastructure
[stgit] / stgit / commands / sink.py
index d8f79b4..4677a75 100644 (file)
@@ -16,59 +16,80 @@ 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 = 'send patches deeper down the stack'
-usage = """%prog [-t <target patch>] [-n] [<patches>]
-
-Pop all patches (or all patches including <target patch>), then
-push the specified <patches> (the current patch by default), and
-then push back into place the formerly-applied patches (unless -n
-is also given)."""
-
-directory = DirectoryGotoToplevel()
-options = [make_option('-n', '--nopush',
-                       help = 'do not push the patches back after sinking',
-                       action = 'store_true'),
-           make_option('-t', '--to', metavar = 'TARGET',
-                       help = 'sink patches below TARGET patch')]
+from stgit.argparse import opt
+from stgit.commands import common
+from stgit.lib import transaction
+from stgit import argparse
+
+help = 'Send patches deeper down the stack'
+kind = 'stack'
+usage = ['[-t <target patch>] [-n] [<patches>]']
+description = """
+This is the opposite operation of linkstg:float[]: move the specified
+patches down the stack.  It is for example useful to group stable
+patches near the bottom of the stack, where they are less likely to be
+impacted by the push of another patch, and from where they can be more
+easily committed or pushed.
+
+If no patch is specified on command-line, the current patch gets sunk.
+By default patches are sunk to the bottom of the stack, but the '--to'
+option allows to place them under any applied patch.
+
+Sinking internally involves popping all patches (or all patches
+including <target patch>), then pushing the patches to sink, and then
+(unless '--nopush' is also given) pushing back into place the
+formerly-applied patches."""
+
+args = [argparse.patch_range(argparse.applied_patches,
+                             argparse.unapplied_patches)]
+options = [
+    opt('-n', '--nopush', action = 'store_true',
+        short = 'Do not push the patches back after sinking', long = """
+        Do not push back on the stack the formerly-applied patches.
+        Only the patches to sink are pushed."""),
+    opt('-t', '--to', metavar = 'TARGET', args = [argparse.applied_patches],
+        short = 'Sink patches below the TARGET patch', long = """
+        Specify a target patch to place the patches below, instead of
+        sinking them to the bottom of the stack.""")
+    ] + argparse.keep_option()
+
+directory = common.DirectoryHasRepositoryLib()
 
 def func(parser, options, args):
     """Sink patches down the stack.
     """
+    stack = directory.repository.current_stack
 
-    check_local_changes()
-    check_conflicts()
-    check_head_top_equal(crt_series)
+    if options.to and not options.to in stack.patchorder.applied:
+        raise common.CmdException('Cannot sink below %s since it is not applied'
+                                  % options.to)
 
-    oldapplied = crt_series.get_applied()
-    unapplied = crt_series.get_unapplied()
-    all = unapplied + oldapplied
+    if len(args) > 0:
+        patches = common.parse_patches(args, stack.patchorder.all)
+    else:
+        # current patch
+        patches = list(stack.patchorder.applied[-1:])
 
-    if options.to and not options.to in oldapplied:
-        raise CmdException('Cannot sink below %s, since it is not applied'
-                           % options.to)
+    if not patches:
+        raise common.CmdException('No patches to sink')
+    if options.to and options.to in patches:
+        raise common.CmdException('Cannot have a sinked patch as target')
 
-    if len(args) > 0:
-        patches = parse_patches(args, all)
+    applied = [p for p in stack.patchorder.applied if p not in patches]
+    if options.to:
+        insert_idx = applied.index(options.to)
     else:
-        current = crt_series.get_current()
-        if not current:
-            raise CmdException('No patch applied')
-        patches = [current]
-
-    if oldapplied:
-        crt_series.pop_patch(options.to or oldapplied[0])
-    push_patches(crt_series, patches)
-
-    if not options.nopush:
-        newapplied = crt_series.get_applied()
-        def not_reapplied_yet(p):
-            return not p in newapplied
-        push_patches(crt_series, filter(not_reapplied_yet, oldapplied))
+        insert_idx = 0
+    applied = applied[:insert_idx] + patches + applied[insert_idx:]
+    unapplied = [p for p in stack.patchorder.unapplied if p not in patches]
+
+    iw = stack.repository.default_iw
+    clean_iw = (not options.keep and iw) or None
+    trans = transaction.StackTransaction(stack, 'sink',
+                                         check_clean_iw = clean_iw)
+
+    try:
+        trans.reorder_patches(applied, unapplied, iw = iw)
+    except transaction.TransactionHalted:
+        pass
+    return trans.run(iw)