Check bad head and clean iw before StackTransaction
[stgit.git] / stgit / commands / push.py
blobfca827ef8af1777e869280299242e3e33ae2990d
1 from stgit.argparse import keep_option, merged_option, opt, patch_range
2 from stgit.commands.common import (
3 CmdException,
4 DirectoryHasRepository,
5 check_head_top_equal,
6 check_index_and_worktree_clean,
7 parse_patches,
9 from stgit.lib import transaction
11 __copyright__ = """
12 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License version 2 as
16 published by the Free Software Foundation.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, see http://www.gnu.org/licenses/.
25 """
27 help = 'Push one or more patches onto the stack'
28 kind = 'stack'
29 usage = ['[options] [--] [<patch1>] [<patch2>] [<patch3>..<patch4>]']
30 description = """
31 Push one or more patches (defaulting to the first unapplied one) onto
32 the stack. The 'push' operation allows patch reordering by commuting
33 them with the three-way merge algorithm. If there are conflicts while
34 pushing a patch, those conflicts are written to the work tree, and the
35 command halts. Conflicts raised during the push operation have to be
36 fixed and the 'git add --update' command run (alternatively, you may
37 undo the conflicting push with 'stg undo').
39 The command also notifies when the patch becomes empty (fully merged
40 upstream) or is modified (three-way merged) by the 'push' operation."""
42 args = [patch_range('unapplied_patches')]
43 options = [
44 opt(
45 '-a',
46 '--all',
47 action='store_true',
48 short='Push all the unapplied patches',
50 opt(
51 '-n',
52 '--number',
53 type='int',
54 short='Push the specified number of patches',
55 long='''
56 Push the specified number of patches.
58 With a negative number, push all but that many patches.''',
60 opt(
61 '--reverse',
62 action='store_true',
63 short='Push the patches in reverse order',
65 opt(
66 '--noapply',
67 action='store_true',
68 short='Reorder patches by pushing without applying.',
70 opt(
71 '--set-tree',
72 action='store_true',
73 short='Push the patch with the original tree',
74 long="""
75 Push the patches, but don't perform a merge. Instead, the
76 resulting tree will be identical to the tree that the patch
77 previously created.
79 This can be useful when splitting a patch by first popping the
80 patch and creating a new patch with some of the
81 changes. Pushing the original patch with '--set-tree' will
82 avoid conflicts and only the remaining changes will be in the
83 patch.""",
86 options.extend(keep_option())
87 options.extend(merged_option())
89 directory = DirectoryHasRepository()
92 def func(parser, options, args):
93 """Pushes the given patches or the first unapplied onto the stack."""
94 stack = directory.repository.current_stack
95 iw = stack.repository.default_iw
97 if options.number == 0:
98 # explicitly allow this without any warning/error message
99 return
101 if not stack.patchorder.unapplied:
102 raise CmdException('No patches to push')
104 if options.all:
105 if options.noapply:
106 raise CmdException('Cannot use --noapply with --all')
107 patches = list(stack.patchorder.unapplied)
108 elif options.number is not None:
109 if options.noapply:
110 raise CmdException('Cannot use --noapply with --number')
111 patches = list(stack.patchorder.unapplied[: options.number])
112 elif not args:
113 if options.noapply:
114 raise CmdException('Must supply patch names with --noapply')
115 patches = [stack.patchorder.unapplied[0]]
116 else:
117 try:
118 patches = parse_patches(args, stack.patchorder.unapplied)
119 except CmdException as e:
120 try:
121 patches = parse_patches(args, stack.patchorder.applied)
122 except CmdException:
123 raise e
124 else:
125 raise CmdException(
126 'Patch%s already applied: %s'
127 % ('es' if len(patches) > 1 else '', ', '.join(patches))
130 assert patches
132 check_head_top_equal(stack)
133 if not options.keep and not options.noapply:
134 check_index_and_worktree_clean(stack)
136 trans = transaction.StackTransaction(stack)
138 if options.reverse:
139 patches.reverse()
141 if options.set_tree:
142 if options.noapply:
143 raise CmdException('Cannot use --noapply with --set-tree')
144 for pn in patches:
145 trans.push_tree(pn)
146 elif options.noapply:
147 if options.merged:
148 raise CmdException('Cannot use --noapply with --merged')
149 unapplied = patches + [pn for pn in trans.unapplied if pn not in patches]
150 trans.reorder_patches(trans.applied, unapplied)
151 else:
152 try:
153 if options.merged:
154 merged = set(trans.check_merged(patches))
155 else:
156 merged = set()
157 for pn in patches:
158 trans.push_patch(
159 pn, iw, allow_interactive=True, already_merged=pn in merged
161 except transaction.TransactionHalted:
162 pass
163 return trans.run('push', iw)