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 out.stdout(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 out.start('Deleting branch "%s"' % doomed_name)
97 if __is_current_branch(doomed_name):
100 check_head_top_equal()
102 if doomed_name != 'master':
103 git.switch_branch('master')
107 if doomed_name != 'master':
108 git.delete_branch(doomed_name)
112 def func(parser, options, args):
116 if len(args) == 0 or len(args) > 2:
117 parser.error('incorrect number of arguments')
119 check_local_changes()
121 check_head_top_equal()
126 if git.rev_parse(args[1]) == git.rev_parse('refs/heads/' + args[1]):
127 # we are for sure referring to a branch
128 parentbranch = 'refs/heads/' + args[1]
129 out.info('Recording "%s" as parent branch' % parentbranch)
130 elif git.rev_parse(args[1]) and re.search('/', args[1]):
131 # FIXME: should the test be more strict ?
132 parentbranch = args[1]
134 # Note: this includes refs to StGIT patches
135 out.info('Don\'t know how to determine parent branch'
136 ' from "%s"' % args[1])
138 except git.GitException:
139 # should use a more specific exception to catch only
141 out.info('Don\'t know how to determine parent branch'
142 ' from "%s"' % args[1])
145 tree_id = git_id(args[1])
147 # branch stack off current branch
148 parentbranch = git.get_head_file()
151 parentremote = git.identify_remote(parentbranch)
153 out.info('Using remote "%s" to pull parent from'
156 out.info('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 out.info('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 out.start('Cloning current branch to "%s"' % clone)
183 crt_series.clone(clone)
191 parser.error('incorrect number of arguments')
192 __delete_branch(args[0], options.force)
198 parser.error('incorrect number of arguments')
201 basepath = os.path.join(basedir.get(), 'refs', 'heads')
202 for path, files, dirs in walk_tree(basepath):
203 branches += [os.path.join(path, f) for f in files]
207 out.info('Available branches:')
208 max_len = max([len(i) for i in branches])
210 __print_branch(i, max_len)
212 out.info('No branches')
215 elif options.protect:
218 branch_name = crt_series.get_branch()
220 branch_name = args[0]
222 parser.error('incorrect number of arguments')
223 branch = stack.Series(branch_name)
225 if not branch.is_initialised():
226 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
229 out.start('Protecting branch "%s"' % branch_name)
238 parser.error('incorrect number of arguments')
240 if __is_current_branch(args[0]):
241 raise CmdException, 'Renaming the current branch is not supported'
243 stack.Series(args[0]).rename(args[1])
245 out.info('Renamed branch "%s" to "%s"' % (args[0], args[1]))
249 elif options.unprotect:
252 branch_name = crt_series.get_branch()
254 branch_name = args[0]
256 parser.error('incorrect number of arguments')
257 branch = stack.Series(branch_name)
259 if not branch.is_initialised():
260 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
263 out.info('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 out.start('Switching to branch "%s"' % args[0])
298 git.switch_branch(args[0])
302 # default action: print the current branch
304 parser.error('incorrect number of arguments')
306 print crt_series.get_branch()