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.out import *
27 from stgit import stack, git, basedir
30 help = 'manage patch stacks'
31 usage = """%prog [options] branch-name [commit-id]
33 Create, clone, switch between, rename, or delete development branches
34 within a git repository. By default, a single branch called 'master'
35 is always created in a new repository. This subcommand allows you to
36 manage several patch series in the same repository via GIT branches.
38 When displaying the branches, the names can be prefixed with
39 's' (StGIT managed) or 'p' (protected).
41 If not given any options, switch to the named branch."""
43 options = [make_option('-c', '--create',
44 help = 'create a new development branch',
45 action = 'store_true'),
46 make_option('--clone',
47 help = 'clone the contents of the current branch',
48 action = 'store_true'),
49 make_option('--delete',
50 help = 'delete an existing development branch',
51 action = 'store_true'),
52 make_option('-d', '--description',
53 help = 'set the branch description'),
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 StGIT 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 StGIT to modify this branch',
68 action = 'store_true')]
71 def __is_current_branch(branch_name):
72 return crt_series.get_name() == branch_name
74 def __print_branch(branch_name, length):
79 branch = stack.Series(branch_name)
81 if branch.is_initialised():
83 if __is_current_branch(branch_name):
85 if branch.get_protected():
87 out.stdout(current + ' ' + initialized + protected + '\t'
88 + branch_name.ljust(length) + ' | ' + branch.get_description())
90 def __delete_branch(doomed_name, force = False):
91 doomed = stack.Series(doomed_name)
93 if doomed.get_protected():
94 raise CmdException, 'This branch is protected. Delete is not permitted'
96 out.start('Deleting branch "%s"' % doomed_name)
98 if __is_current_branch(doomed_name):
99 raise CmdException('Cannot delete the current branch')
105 def func(parser, options, args):
109 if len(args) == 0 or len(args) > 2:
110 parser.error('incorrect number of arguments')
112 check_local_changes()
114 check_head_top_equal()
120 branchpoint = git.rev_parse(args[1])
122 # first, look for branchpoint in well-known branch namespaces
123 for namespace in ('refs/heads/', 'remotes/'):
124 # check if branchpoint exists in namespace
126 maybehead = git.rev_parse(namespace + args[1])
127 except git.GitException:
130 # check if git resolved branchpoint to this namespace
131 if maybehead and branchpoint == maybehead:
132 # we are for sure referring to a branch
133 parentbranch = namespace + args[1]
135 except git.GitException:
136 # should use a more specific exception to catch only
138 out.info('Don\'t know how to determine parent branch'
139 ' from "%s"' % args[1])
140 # exception in branch = rev_parse() leaves branchpoint unbound
143 tree_id = branchpoint or git_id(args[1])
146 out.info('Recording "%s" as parent branch' % parentbranch)
148 out.info('Don\'t know how to determine parent branch'
149 ' from "%s"' % args[1])
151 # branch stack off current branch
152 parentbranch = git.get_head_file()
155 parentremote = git.identify_remote(parentbranch)
157 out.info('Using remote "%s" to pull parent from'
160 out.info('Recording as a local branch')
162 # no known parent branch, can't guess the remote
165 stack.Series(args[0]).init(create_at = tree_id,
166 parent_remote = parentremote,
167 parent_branch = parentbranch)
169 out.info('Branch "%s" created' % args[0])
175 clone = crt_series.get_name() + \
176 time.strftime('-%C%y%m%d-%H%M%S')
180 parser.error('incorrect number of arguments')
182 check_local_changes()
184 check_head_top_equal()
186 out.start('Cloning current branch to "%s"' % clone)
187 crt_series.clone(clone)
195 parser.error('incorrect number of arguments')
196 __delete_branch(args[0], options.force)
202 parser.error('incorrect number of arguments')
204 branches = git.get_heads()
208 out.info('Available branches:')
209 max_len = max([len(i) for i in branches])
211 __print_branch(i, max_len)
213 out.info('No branches')
216 elif options.protect:
219 branch_name = crt_series.get_name()
221 branch_name = args[0]
223 parser.error('incorrect number of arguments')
224 branch = stack.Series(branch_name)
226 if not branch.is_initialised():
227 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
230 out.start('Protecting branch "%s"' % branch_name)
239 parser.error('incorrect number of arguments')
241 if __is_current_branch(args[0]):
242 raise CmdException, 'Renaming the current branch is not supported'
244 stack.Series(args[0]).rename(args[1])
246 out.info('Renamed branch "%s" to "%s"' % (args[0], args[1]))
250 elif options.unprotect:
253 branch_name = crt_series.get_name()
255 branch_name = args[0]
257 parser.error('incorrect number of arguments')
258 branch = stack.Series(branch_name)
260 if not branch.is_initialised():
261 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
264 out.info('Unprotecting branch "%s"' % branch_name)
270 elif options.description is not None:
273 branch_name = crt_series.get_name()
275 branch_name = args[0]
277 parser.error('incorrect number of arguments')
278 branch = stack.Series(branch_name)
280 if not branch.is_initialised():
281 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
284 branch.set_description(options.description)
290 if __is_current_branch(args[0]):
291 raise CmdException, 'Branch "%s" is already the current branch' \
294 check_local_changes()
296 check_head_top_equal()
298 out.start('Switching to branch "%s"' % args[0])
299 git.switch_branch(args[0])
303 # default action: print the current branch
305 parser.error('incorrect number of arguments')
307 print crt_series.get_name()