Make "stg commit" fancier
[stgit] / stgit / commands / commit.py
1 __copyright__ = """
2 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License version 2 as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 """
17
18 from optparse import make_option
19 from stgit.commands import common
20 from stgit.lib import transaction
21 from stgit.out import *
22
23 help = 'permanently store the applied patches into stack base'
24 usage = """%prog [<patchnames>] | -n NUM | --all
25
26 Merge one or more patches into the base of the current stack and
27 remove them from the series while advancing the base. This is the
28 opposite of 'stg uncommit'. Use this command if you no longer want to
29 manage a patch with StGIT.
30
31 By default, the bottommost patch is committed. If patch names are
32 given, the stack is rearranged so that those patches are at the
33 bottom, and then they are committed.
34
35 The -n/--number option specifies the number of applied patches to
36 commit (counting from the bottom of the stack). If -a/--all is given,
37 all applied patches are committed."""
38
39 directory = common.DirectoryHasRepositoryLib()
40 options = [make_option('-n', '--number', type = 'int',
41 help = 'commit the specified number of patches'),
42 make_option('-a', '--all', action = 'store_true',
43 help = 'commit all applied patches')]
44
45 def func(parser, options, args):
46 """Commit a number of patches."""
47 stack = directory.repository.current_stack
48 args = common.parse_patches(args, (list(stack.patchorder.applied)
49 + list(stack.patchorder.unapplied)))
50 if len([x for x in [args, options.number != None, options.all] if x]) > 1:
51 parser.error('too many options')
52 if args:
53 patches = [pn for pn in (stack.patchorder.applied
54 + stack.patchorder.unapplied) if pn in args]
55 bad = set(args) - set(patches)
56 if bad:
57 raise common.CmdException('Bad patch names: %s'
58 % ', '.join(sorted(bad)))
59 elif options.number != None:
60 if options.number <= len(stack.patchorder.applied):
61 patches = stack.patchorder.applied[:options.number]
62 else:
63 raise common.CmdException('There are not that many applied patches')
64 elif options.all:
65 patches = stack.patchorder.applied
66 else:
67 patches = stack.patchorder.applied[:1]
68 if not patches:
69 raise common.CmdException('No patches to commit')
70
71 iw = stack.repository.default_iw()
72 trans = transaction.StackTransaction(stack, 'stg commit')
73 try:
74 common_prefix = 0
75 for i in xrange(min(len(stack.patchorder.applied), len(patches))):
76 if stack.patchorder.applied[i] == patches[i]:
77 common_prefix += 1
78 if common_prefix < len(patches):
79 to_push = trans.pop_patches(
80 lambda pn: pn in stack.patchorder.applied[common_prefix:])
81 for pn in patches[common_prefix:]:
82 trans.push_patch(pn, iw)
83 else:
84 to_push = []
85 new_base = trans.patches[patches[-1]]
86 for pn in patches:
87 trans.patches[pn] = None
88 trans.applied = [pn for pn in trans.applied if pn not in patches]
89 trans.base = new_base
90 out.info('Committed %d patch%s' % (len(patches),
91 ['es', ''][len(patches) == 1]))
92 for pn in to_push:
93 trans.push_patch(pn, iw)
94 except transaction.TransactionHalted:
95 pass
96 return trans.run(iw)