Don't try to delete the branch twice
[stgit] / stgit / commands / branch.py
CommitLineData
7b1ba1a6
CL
1"""Branch command
2"""
3
4__copyright__ = """
5Copyright (C) 2005, Chuck Lever <cel@netapp.com>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License version 2 as
9published by the Free Software Foundation.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19"""
20
cc3db2b1 21import sys, os, time
7b1ba1a6
CL
22from optparse import OptionParser, make_option
23
24from stgit.commands.common import *
25from stgit.utils import *
5e888f30 26from stgit.out import *
170f576b 27from stgit import stack, git, basedir
7b1ba1a6
CL
28
29
4ec67741 30help = 'manage patch stacks'
7b1ba1a6
CL
31usage = """%prog [options] branch-name [commit-id]
32
cc3db2b1 33Create, clone, switch between, rename, or delete development branches
7b1ba1a6
CL
34within a git repository. By default, a single branch called 'master'
35is always created in a new repository. This subcommand allows you to
cc3db2b1 36manage several patch series in the same repository via GIT branches.
7b1ba1a6
CL
37
38When displaying the branches, the names can be prefixed with
a8032a85
CL
39's' (StGIT managed) or 'p' (protected).
40
41If not given any options, switch to the named branch."""
7b1ba1a6
CL
42
43options = [make_option('-c', '--create',
44 help = 'create a new development branch',
45 action = 'store_true'),
cc3db2b1
CL
46 make_option('--clone',
47 help = 'clone the contents of the current branch',
48 action = 'store_true'),
7b1ba1a6
CL
49 make_option('--delete',
50 help = 'delete an existing development branch',
51 action = 'store_true'),
302600dc
CM
52 make_option('-d', '--description',
53 help = 'set the branch description'),
7b1ba1a6
CL
54 make_option('--force',
55 help = 'force a delete when the series is not empty',
56 action = 'store_true'),
57 make_option('-l', '--list',
58 help = 'list branches contained in this repository',
59 action = 'store_true'),
0b4b9499 60 make_option('-p', '--protect',
f644f510 61 help = 'prevent StGIT from modifying this branch',
0b4b9499 62 action = 'store_true'),
7b1ba1a6
CL
63 make_option('-r', '--rename',
64 help = 'rename an existing development branch',
0b4b9499
CL
65 action = 'store_true'),
66 make_option('-u', '--unprotect',
f644f510 67 help = 'allow StGIT to modify this branch',
7b1ba1a6
CL
68 action = 'store_true')]
69
70
fe4f6d58 71def __is_current_branch(branch_name):
d37ff079 72 return crt_series.get_name() == branch_name
7b1ba1a6 73
862ba51a 74def __print_branch(branch_name, length):
7b1ba1a6
CL
75 initialized = ' '
76 current = ' '
0b4b9499 77 protected = ' '
ebfd0156
CL
78
79 branch = stack.Series(branch_name)
80
2d00440c 81 if branch.is_initialised():
7b1ba1a6 82 initialized = 's'
fe4f6d58 83 if __is_current_branch(branch_name):
7b1ba1a6 84 current = '>'
ebfd0156 85 if branch.get_protected():
0b4b9499 86 protected = 'p'
27ac2b7e
KH
87 out.stdout(current + ' ' + initialized + protected + '\t'
88 + branch_name.ljust(length) + ' | ' + branch.get_description())
7b1ba1a6 89
fe4f6d58 90def __delete_branch(doomed_name, force = False):
ebfd0156
CL
91 doomed = stack.Series(doomed_name)
92
93 if doomed.get_protected():
0b4b9499
CL
94 raise CmdException, 'This branch is protected. Delete is not permitted'
95
27ac2b7e 96 out.start('Deleting branch "%s"' % doomed_name)
fe4f6d58
CM
97
98 if __is_current_branch(doomed_name):
57059512 99 raise CmdException('Cannot delete the current branch')
7b1ba1a6 100
ebfd0156 101 doomed.delete(force)
7b1ba1a6 102
27ac2b7e 103 out.done()
7b1ba1a6 104
7b1ba1a6
CL
105def func(parser, options, args):
106
107 if options.create:
108
109 if len(args) == 0 or len(args) > 2:
110 parser.error('incorrect number of arguments')
fe4f6d58
CM
111
112 check_local_changes()
113 check_conflicts()
114 check_head_top_equal()
115
7b1ba1a6 116 tree_id = None
4f5a8c72 117 if len(args) >= 2:
0d90f0ac 118 parentbranch = None
4f5a8c72 119 try:
0d90f0ac
YD
120 branchpoint = git.rev_parse(args[1])
121
122 # first, look for branchpoint in well-known branch namespaces
123 for namespace in ('refs/heads/', 'remotes/'):
124 # check if branchpoint exists in namespace
125 try:
126 maybehead = git.rev_parse(namespace + args[1])
127 except git.GitException:
128 maybehead = None
129
130 # check if git resolved branchpoint to this namespace
131 if maybehead and branchpoint == maybehead:
132 # we are for sure referring to a branch
133 parentbranch = namespace + args[1]
134
4f5a8c72 135 except git.GitException:
27ac2b7e
KH
136 # should use a more specific exception to catch only
137 # non-git refs ?
138 out.info('Don\'t know how to determine parent branch'
139 ' from "%s"' % args[1])
0d90f0ac
YD
140 # exception in branch = rev_parse() leaves branchpoint unbound
141 branchpoint = None
4f5a8c72 142
0d90f0ac
YD
143 tree_id = branchpoint or git_id(args[1])
144
145 if parentbranch:
146 out.info('Recording "%s" as parent branch' % parentbranch)
147 else:
148 out.info('Don\'t know how to determine parent branch'
149 ' from "%s"' % args[1])
4f5a8c72
YD
150 else:
151 # branch stack off current branch
152 parentbranch = git.get_head_file()
153
154 if parentbranch:
155 parentremote = git.identify_remote(parentbranch)
156 if parentremote:
27ac2b7e
KH
157 out.info('Using remote "%s" to pull parent from'
158 % parentremote)
4f5a8c72 159 else:
27ac2b7e 160 out.info('Recording as a local branch')
4f5a8c72 161 else:
4646e7a3 162 # no known parent branch, can't guess the remote
4f5a8c72
YD
163 parentremote = None
164
165 stack.Series(args[0]).init(create_at = tree_id,
166 parent_remote = parentremote,
167 parent_branch = parentbranch)
7b1ba1a6 168
27ac2b7e 169 out.info('Branch "%s" created' % args[0])
7b1ba1a6
CL
170 return
171
cc3db2b1
CL
172 elif options.clone:
173
174 if len(args) == 0:
d37ff079 175 clone = crt_series.get_name() + \
cc3db2b1
CL
176 time.strftime('-%C%y%m%d-%H%M%S')
177 elif len(args) == 1:
178 clone = args[0]
179 else:
180 parser.error('incorrect number of arguments')
181
182 check_local_changes()
183 check_conflicts()
184 check_head_top_equal()
185
27ac2b7e 186 out.start('Cloning current branch to "%s"' % clone)
cc3db2b1 187 crt_series.clone(clone)
27ac2b7e 188 out.done()
cc3db2b1
CL
189
190 return
191
7b1ba1a6
CL
192 elif options.delete:
193
194 if len(args) != 1:
195 parser.error('incorrect number of arguments')
fe4f6d58 196 __delete_branch(args[0], options.force)
7b1ba1a6
CL
197 return
198
199 elif options.list:
200
201 if len(args) != 0:
202 parser.error('incorrect number of arguments')
203
262d31dc 204 branches = git.get_heads()
7b1ba1a6
CL
205 branches.sort()
206