Add a couple of safety checks to series creation
[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 *
170f576b 26from stgit import stack, git, basedir
7b1ba1a6
CL
27
28
29help = 'manage development branches'
30usage = """%prog [options] branch-name [commit-id]
31
cc3db2b1 32Create, clone, switch between, rename, or delete development branches
7b1ba1a6
CL
33within a git repository. By default, a single branch called 'master'
34is always created in a new repository. This subcommand allows you to
cc3db2b1 35manage several patch series in the same repository via GIT branches.
7b1ba1a6
CL
36
37When displaying the branches, the names can be prefixed with
a8032a85
CL
38's' (StGIT managed) or 'p' (protected).
39
40If not given any options, switch to the named branch."""
7b1ba1a6
CL
41
42options = [make_option('-c', '--create',
43 help = 'create a new development branch',
44 action = 'store_true'),
cc3db2b1
CL
45 make_option('--clone',
46 help = 'clone the contents of the current branch',
47 action = 'store_true'),
bad9dcfc
CL
48 make_option('--convert',
49 help = 'switch between old and new format branches',
50 action = 'store_true'),
7b1ba1a6
CL
51 make_option('--delete',
52 help = 'delete an existing development branch',
53 action = 'store_true'),
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
CL
60 make_option('-p', '--protect',
61 help = 'prevent "stg pull" from modifying this branch',
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',
67 help = 'allow "stg pull" to modify this branch',
7b1ba1a6
CL
68 action = 'store_true')]
69
70
fe4f6d58
CM
71def __is_current_branch(branch_name):
72 return crt_series.get_branch() == 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'
862ba51a
CL
87 print 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
fe4f6d58
CM
96 print 'Deleting branch "%s"...' % doomed_name,
97 sys.stdout.flush()
98
99 if __is_current_branch(doomed_name):
100 check_local_changes()
101 check_conflicts()
102 check_head_top_equal()
103
104 if doomed_name != 'master':
105 git.switch_branch('master')
7b1ba1a6 106
ebfd0156 107 doomed.delete(force)
7b1ba1a6
CL
108
109 if doomed_name != 'master':
110 git.delete_branch(doomed_name)
111
fe4f6d58 112 print 'done'
7b1ba1a6 113
7b1ba1a6
CL
114def func(parser, options, args):
115
116 if options.create:
117
118 if len(args) == 0 or len(args) > 2:
119 parser.error('incorrect number of arguments')
fe4f6d58
CM
120
121 check_local_changes()
122 check_conflicts()
123 check_head_top_equal()
124
7b1ba1a6
CL
125 tree_id = None
126 if len(args) == 2:
2fef9462 127 tree_id = git_id(args[1])
7b1ba1a6 128
fe847176
YD
129 if git.branch_exists(args[0]):
130 raise CmdException, 'Branch "%s" already exists' % args[0]
131
7b1ba1a6 132 stack.Series(args[0]).init()
fe847176 133 git.create_branch(args[0], tree_id)
7b1ba1a6
CL
134
135 print 'Branch "%s" created.' % args[0]
136 return
137
cc3db2b1
CL
138 elif options.clone:
139
140 if len(args) == 0:
141 clone = crt_series.get_branch() + \
142 time.strftime('-%C%y%m%d-%H%M%S')
143 elif len(args) == 1:
144 clone = args[0]
145 else:
146 parser.error('incorrect number of arguments')
147
148 check_local_changes()
149 check_conflicts()
150 check_head_top_equal()
151
152 print 'Cloning current branch to "%s"...' % clone,
153 sys.stdout.flush()
154 crt_series.clone(clone)
155 print 'done'
156
157 return
158
bad9dcfc
CL
159 elif options.convert:
160
161 if len(args) != 0:
162 parser.error('incorrect number of arguments')
163
164 crt_series.convert()
165 return
166
7b1ba1a6
CL
167 elif options.delete:
168
169 if len(args) != 1:
170 parser.error('incorrect number of arguments')
fe4f6d58 171 __delete_branch(args[0], options.force)
7b1ba1a6
CL
172 return
173
174 elif options.list:
175
176 if len(args) != 0:
177 parser.error('incorrect number of arguments')
178
170f576b 179 branches = os.listdir(os.path.join(basedir.get(), 'refs', 'heads'))
7b1ba1a6 180 branches.sort()
862ba51a 181 max_len = max([len(i) for i in branches])
7b1ba1a6
CL
182
183 print 'Available branches:'
184 for i in branches:
862ba51a 185 __print_branch(i, max_len)
7b1ba1a6
CL
186 return
187
0b4b9499
CL
188 elif options.protect:
189
190 if len(args) == 0:
5fab7247 191 branch_name = crt_series.get_branch()
0b4b9499 192 elif len(args) == 1:
2d00440c 193 branch_name = args[0]
0b4b9499
CL
194 else:
195 parser.error('incorrect number of arguments')
2d00440c 196 branch = stack.Series(branch_name)
0b4b9499 197
2d00440c 198 if not branch.is_initialised():
fe4f6d58
CM
199 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
200 % branch_name
0b4b9499 201
2d00440c 202 print 'Protecting branch "%s"...' % branch_name,
fa08ec08 203 sys.stdout.flush()
2d00440c 204 branch.protect()
fa08ec08
CM
205 print 'done'
206
0b4b9499
CL
207 return
208
7b1ba1a6
CL
209 elif options.rename:
210
211 if len(args) != 2:
212 parser.error('incorrect number of arguments')
660ba985 213
fe4f6d58
CM
214 if __is_current_branch(args[0]):
215 raise CmdException, 'Renaming the current branch is not supported'
216
660ba985
CL
217 stack.Series(args[0]).rename(args[1])
218
219 print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
220
7b1ba1a6
CL
221 return
222
0b4b9499
CL
223 elif options.unprotect:
224
225 if len(args) == 0:
5fab7247 226 branch_name = crt_series.get_branch()
0b4b9499 227 elif len(args) == 1:
2d00440c 228 branch_name = args[0]
0b4b9499
CL
229 else:
230 parser.error('incorrect number of arguments')
2d00440c 231 branch = stack.Series(branch_name)
0b4b9499 232
2d00440c 233 if not branch.is_initialised():
fe4f6d58
CM
234 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
235 % branch_name
0b4b9499 236
2d00440c 237 print 'Unprotecting branch "%s"...' % branch_name,
fa08ec08 238 sys.stdout.flush()
2d00440c 239 branch.unprotect()
fa08ec08
CM
240 print 'done'
241
0b4b9499
CL
242 return
243
7b1ba1a6
CL
244 elif len(args) == 1:
245
fe4f6d58
CM
246 if __is_current_branch(args[0]):
247 raise CmdException, 'Branch "%s" is already the current branch' \
248 % args[0]
249
250 check_local_changes()
251 check_conflicts()
252 check_head_top_equal()
9756a202 253
7b1ba1a6
CL
254 print 'Switching to branch "%s"...' % args[0],
255 sys.stdout.flush()
256
257 git.switch_branch(args[0])
258
259 print 'done'
260 return
261
262 # default action: print the current branch
263 if len(args) != 0:
264 parser.error('incorrect number of arguments')
265
5fab7247 266 print crt_series.get_branch()