5 Copyright (C) 2005, Chuck Lever <cel@netapp.com>
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.
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.
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
22 from optparse import OptionParser, make_option
24 from stgit.commands.common import *
25 from stgit.utils import *
26 from stgit import stack, git, basedir
29 help = 'manage patch stacks'
30 usage = """%prog [options] branch-name [commit-id]
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.
37 When displaying the branches, the names can be prefixed with
38 's' (StGIT managed) or 'p' (protected).
40 If not given any options, switch to the named branch."""
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('-d', '--description',
55 help = 'set the branch description'),
56 make_option('--force',
57 help = 'force a delete when the series is not empty',
58 action = 'store_true'),
59 make_option('-l', '--list',
60 help = 'list branches contained in this repository',
61 action = 'store_true'),
62 make_option('-p', '--protect',
63 help = 'prevent StGIT from modifying this branch',
64 action = 'store_true'),
65 make_option('-r', '--rename',
66 help = 'rename an existing development branch',
67 action = 'store_true'),
68 make_option('-u', '--unprotect',
69 help = 'allow StGIT to modify this branch',
70 action = 'store_true')]
73 def __is_current_branch(branch_name):
74 return crt_series.get_branch() == branch_name
76 def __print_branch(branch_name, length):
81 branch = stack.Series(branch_name)
83 if branch.is_initialised():
85 if __is_current_branch(branch_name):
87 if branch.get_protected():
89 print current + ' ' + initialized + protected + '\t' + \
90 branch_name.ljust(length) + ' | ' + branch.get_description()
92 def __delete_branch(doomed_name, force = False):
93 doomed = stack.Series(doomed_name)
95 if doomed.get_protected():
96 raise CmdException, 'This branch is protected. Delete is not permitted'
98 print 'Deleting branch "%s"...' % doomed_name,
101 if __is_current_branch(doomed_name):
102 check_local_changes()
104 check_head_top_equal()
106 if doomed_name != 'master':
107 git.switch_branch('master')
111 if doomed_name != 'master':
112 git.delete_branch(doomed_name)
116 def func(parser, options, args):
120 if len(args) == 0 or len(args) > 2:
121 parser.error('incorrect number of arguments')
123 check_local_changes()
125 check_head_top_equal()
130 if git.rev_parse(args[1]) == git.rev_parse('refs/heads/' + args[1]):
131 # we are for sure referring to a branch
132 parentbranch = 'refs/heads/' + args[1]
133 print 'Recording "%s" as parent branch.' % parentbranch
134 elif git.rev_parse(args[1]) and re.search('/', args[1]):
135 # FIXME: should the test be more strict ?
136 parentbranch = args[1]
138 # Note: this includes refs to StGIT patches
139 print 'Don\'t know how to determine parent branch from "%s".' % args[1]
141 except git.GitException:
142 # should use a more specific exception to catch only non-git refs ?
143 print 'Don\'t know how to determine parent branch from "%s".' % args[1]
146 tree_id = git_id(args[1])
148 # branch stack off current branch
149 parentbranch = git.get_head_file()
152 parentremote = git.identify_remote(parentbranch)
154 print 'Using "%s" remote to pull parent from.' % parentremote
156 print 'Recording as a local branch.'
158 # no known parent branch, can't guess the remote
161 stack.Series(args[0]).init(create_at = tree_id,
162 parent_remote = parentremote,
163 parent_branch = parentbranch)
165 print 'Branch "%s" created.' % args[0]
171 clone = crt_series.get_branch() + \
172 time.strftime('-%C%y%m%d-%H%M%S')
176 parser.error('incorrect number of arguments')
178 check_local_changes()
180 check_head_top_equal()
182 print 'Cloning current branch to "%s"...' % clone,
184 crt_series.clone(clone)
189 elif options.convert:
192 parser.error('incorrect number of arguments')
200 parser.error('incorrect number of arguments')
201 __delete_branch(args[0], options.force)
207 parser.error('incorrect number of arguments')
210 basepath = os.path.join(basedir.get(), 'refs', 'heads')
211 for path, files, dirs in walk_tree(basepath):
212 branches += [os.path.join(path, f) for f in files]
216 print 'Available branches:'
217 max_len = max([len(i) for i in branches])
219 __print_branch(i, max_len)
224 elif options.protect:
227 branch_name = crt_series.get_branch()
229 branch_name = args[0]
231 parser.error('incorrect number of arguments')
232 branch = stack.Series(branch_name)
234 if not branch.is_initialised():
235 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
238 print 'Protecting branch "%s"...' % branch_name,
248 parser.error('incorrect number of arguments')
250 if __is_current_branch(args[0]):
251 raise CmdException, 'Renaming the current branch is not supported'
253 stack.Series(args[0]).rename(args[1])
255 print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
259 elif options.unprotect:
262 branch_name = crt_series.get_branch()
264 branch_name = args[0]
266 parser.error('incorrect number of arguments')
267 branch = stack.Series(branch_name)
269 if not branch.is_initialised():
270 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
273 print 'Unprotecting branch "%s"...' % branch_name,
280 elif options.description is not None:
283 branch_name = crt_series.get_branch()
285 branch_name = args[0]
287 parser.error('incorrect number of arguments')
288 branch = stack.Series(branch_name)
290 if not branch.is_initialised():
291 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
294 branch.set_description(options.description)
300 if __is_current_branch(args[0]):
301 raise CmdException, 'Branch "%s" is already the current branch' \
304 check_local_changes()
306 check_head_top_equal()
308 print 'Switching to branch "%s"...' % args[0],
311 git.switch_branch(args[0])
316 # default action: print the current branch
318 parser.error('incorrect number of arguments')
320 print crt_series.get_branch()