Add a couple of safety checks to series creation
[stgit] / stgit / commands / branch.py
1 """Branch command
2 """
3
4 __copyright__ = """
5 Copyright (C) 2005, Chuck Lever <cel@netapp.com>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 """
20
21 import sys, os, time
22 from optparse import OptionParser, make_option
23
24 from stgit.commands.common import *
25 from stgit.utils import *
26 from stgit import stack, git, basedir
27
28
29 help = 'manage development branches'
30 usage = """%prog [options] branch-name [commit-id]
31
32 Create, clone, switch between, rename, or delete development branches
33 within a git repository. By default, a single branch called 'master'
34 is always created in a new repository. This subcommand allows you to
35 manage several patch series in the same repository via GIT branches.
36
37 When displaying the branches, the names can be prefixed with
38 's' (StGIT managed) or 'p' (protected).
39
40 If not given any options, switch to the named branch."""
41
42 options = [make_option('-c', '--create',
43 help = 'create a new development branch',
44 action = 'store_true'),
45 make_option('--clone',
46 help = 'clone the contents of the current branch',
47 action = 'store_true'),
48 make_option('--convert',
49 help = 'switch between old and new format branches',
50 action = 'store_true'),
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'),
60 make_option('-p', '--protect',
61 help = 'prevent "stg pull" from modifying this branch',
62 action = 'store_true'),
63 make_option('-r', '--rename',
64 help = 'rename an existing development branch',
65 action = 'store_true'),
66 make_option('-u', '--unprotect',
67 help = 'allow "stg pull" to modify this branch',
68 action = 'store_true')]
69
70
71 def __is_current_branch(branch_name):
72 return crt_series.get_branch() == branch_name
73
74 def __print_branch(branch_name, length):
75 initialized = ' '
76 current = ' '
77 protected = ' '
78
79 branch = stack.Series(branch_name)
80
81 if branch.is_initialised():
82 initialized = 's'
83 if __is_current_branch(branch_name):
84 current = '>'
85 if branch.get_protected():
86 protected = 'p'
87 print current + ' ' + initialized + protected + '\t' + \
88 branch_name.ljust(length) + ' | ' + branch.get_description()
89
90 def __delete_branch(doomed_name, force = False):
91 doomed = stack.Series(doomed_name)
92
93 if doomed.get_protected():
94 raise CmdException, 'This branch is protected. Delete is not permitted'
95
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')
106
107 doomed.delete(force)
108
109 if doomed_name != 'master':
110 git.delete_branch(doomed_name)
111
112 print 'done'
113
114 def 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')
120
121 check_local_changes()
122 check_conflicts()
123 check_head_top_equal()
124
125 tree_id = None
126 if len(args) == 2:
127 tree_id = git_id(args[1])
128
129 if git.branch_exists(args[0]):
130 raise CmdException, 'Branch "%s" already exists' % args[0]
131
132 stack.Series(args[0]).init()
133 git.create_branch(args[0], tree_id)
134
135 print 'Branch "%s" created.' % args[0]
136 return
137
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
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
167 elif options.delete:
168
169 if len(args) != 1:
170 parser.error('incorrect number of arguments')
171 __delete_branch(args[0], options.force)
172 return
173
174 elif options.list:
175
176 if len(args) != 0:
177 parser.error('incorrect number of arguments')
178
179 branches = os.listdir(os.path.join(basedir.get(), 'refs', 'heads'))
180 branches.sort()
181 max_len = max([len(i) for i in branches])
182
183 print 'Available branches:'
184 for i in branches:
185 __print_branch(i, max_len)
186 return
187
188 elif options.protect:
189
190 if len(args) == 0:
191 branch_name = crt_series.get_branch()
192 elif len(args) == 1:
193 branch_name = args[0]
194 else:
195 parser.error('incorrect number of arguments')
196 branch = stack.Series(branch_name)
197
198 if not branch.is_initialised():
199 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
200 % branch_name
201
202 print 'Protecting branch "%s"...' % branch_name,
203 sys.stdout.flush()
204 branch.protect()
205 print 'done'
206
207 return
208
209 elif options.rename:
210
211 if len(args) != 2:
212 parser.error('incorrect number of arguments')
213
214 if __is_current_branch(args[0]):
215 raise CmdException, 'Renaming the current branch is not supported'
216
217 stack.Series(args[0]).rename(args[1])
218
219 print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
220
221 return
222
223 elif options.unprotect:
224
225 if len(args) == 0:
226 branch_name = crt_series.get_branch()
227 elif len(args) == 1:
228 branch_name = args[0]
229 else:
230 parser.error('incorrect number of arguments')
231 branch = stack.Series(branch_name)
232
233 if not branch.is_initialised():
234 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
235 % branch_name
236
237 print 'Unprotecting branch "%s"...' % branch_name,
238 sys.stdout.flush()
239 branch.unprotect()
240 print 'done'
241
242 return
243
244 elif len(args) == 1:
245
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()
253
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
266 print crt_series.get_branch()