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('--delete',
49 help = 'delete an existing development branch',
50 action = 'store_true'),
51 make_option('-d', '--description',
52 help = 'set the branch description'),
53 make_option('--force',
54 help = 'force a delete when the series is not empty',
55 action = 'store_true'),
56 make_option('-l', '--list',
57 help = 'list branches contained in this repository',
58 action = 'store_true'),
59 make_option('-p', '--protect',
60 help = 'prevent StGIT from modifying this branch',
61 action = 'store_true'),
62 make_option('-r', '--rename',
63 help = 'rename an existing development branch',
64 action = 'store_true'),
65 make_option('-u', '--unprotect',
66 help = 'allow StGIT to modify this branch',
67 action = 'store_true')]
70 def __is_current_branch(branch_name):
71 return crt_series.get_branch() == branch_name
73 def __print_branch(branch_name, length):
78 branch = stack.Series(branch_name)
80 if branch.is_initialised():
82 if __is_current_branch(branch_name):
84 if branch.get_protected():
86 print current + ' ' + initialized + protected + '\t' + \
87 branch_name.ljust(length) + ' | ' + branch.get_description()
89 def __delete_branch(doomed_name, force = False):
90 doomed = stack.Series(doomed_name)
92 if doomed.get_protected():
93 raise CmdException, 'This branch is protected. Delete is not permitted'
95 print 'Deleting branch "%s"...' % doomed_name,
98 if __is_current_branch(doomed_name):
101 check_head_top_equal()
103 if doomed_name != 'master':
104 git.switch_branch('master')
108 if doomed_name != 'master':
109 git.delete_branch(doomed_name)
113 def func(parser, options, args):
117 if len(args) == 0 or len(args) > 2:
118 parser.error('incorrect number of arguments')
120 check_local_changes()
122 check_head_top_equal()
127 if git.rev_parse(args[1]) == git.rev_parse('refs/heads/' + args[1]):
128 # we are for sure referring to a branch
129 parentbranch = 'refs/heads/' + args[1]
130 print 'Recording "%s" as parent branch.' % parentbranch
131 elif git.rev_parse(args[1]) and re.search('/', args[1]):
132 # FIXME: should the test be more strict ?
133 parentbranch = args[1]
135 # Note: this includes refs to StGIT patches
136 print 'Don\'t know how to determine parent branch from "%s".' % args[1]
138 except git.GitException:
139 # should use a more specific exception to catch only non-git refs ?
140 print 'Don\'t know how to determine parent branch from "%s".' % args[1]
143 tree_id = git_id(args[1])
145 # branch stack off current branch
146 parentbranch = git.get_head_file()
149 parentremote = git.identify_remote(parentbranch)
151 print 'Using "%s" remote to pull parent from.' % parentremote
153 print 'Recording as a local branch.'
155 # no known parent branch, can't guess the remote
158 stack.Series(args[0]).init(create_at = tree_id,
159 parent_remote = parentremote,
160 parent_branch = parentbranch)
162 print 'Branch "%s" created.' % args[0]
168 clone = crt_series.get_branch() + \
169 time.strftime('-%C%y%m%d-%H%M%S')
173 parser.error('incorrect number of arguments')
175 check_local_changes()
177 check_head_top_equal()
179 print 'Cloning current branch to "%s"...' % clone,
181 crt_series.clone(clone)
189 parser.error('incorrect number of arguments')
190 __delete_branch(args[0], options.force)
196 parser.error('incorrect number of arguments')
199 basepath = os.path.join(basedir.get(), 'refs', 'heads')
200 for path, files, dirs in walk_tree(basepath):
201 branches += [os.path.join(path, f) for f in files]
205 print 'Available branches:'
206 max_len = max([len(i) for i in branches])
208 __print_branch(i, max_len)
213 elif options.protect:
216 branch_name = crt_series.get_branch()
218 branch_name = args[0]
220 parser.error('incorrect number of arguments')
221 branch = stack.Series(branch_name)
223 if not branch.is_initialised():
224 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
227 print 'Protecting branch "%s"...' % branch_name,
237 parser.error('incorrect number of arguments')
239 if __is_current_branch(args[0]):
240 raise CmdException, 'Renaming the current branch is not supported'
242 stack.Series(args[0]).rename(args[1])
244 print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
248 elif options.unprotect:
251 branch_name = crt_series.get_branch()
253 branch_name = args[0]
255 parser.error('incorrect number of arguments')
256 branch = stack.Series(branch_name)
258 if not branch.is_initialised():
259 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
262 print 'Unprotecting branch "%s"...' % branch_name,
269 elif options.description is not None:
272 branch_name = crt_series.get_branch()
274 branch_name = args[0]
276 parser.error('incorrect number of arguments')
277 branch = stack.Series(branch_name)
279 if not branch.is_initialised():
280 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
283 branch.set_description(options.description)
289 if __is_current_branch(args[0]):
290 raise CmdException, 'Branch "%s" is already the current branch' \
293 check_local_changes()
295 check_head_top_equal()
297 print 'Switching to branch "%s"...' % args[0],
300 git.switch_branch(args[0])
305 # default action: print the current branch
307 parser.error('incorrect number of arguments')
309 print crt_series.get_branch()