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 directory = DirectoryGotoToplevel()
44 options = [make_option('-c', '--create',
45 help = 'create a new development branch',
46 action = 'store_true'),
47 make_option('--clone',
48 help = 'clone the contents of the current branch',
49 action = 'store_true'),
50 make_option('--delete',
51 help = 'delete an existing development branch',
52 action = 'store_true'),
53 make_option('-d', '--description',
54 help = 'set the branch description'),
55 make_option('--force',
56 help = 'force a delete when the series is not empty',
57 action = 'store_true'),
58 make_option('-l', '--list',
59 help = 'list branches contained in this repository',
60 action = 'store_true'),
61 make_option('-p', '--protect',
62 help = 'prevent StGIT from modifying this branch',
63 action = 'store_true'),
64 make_option('-r', '--rename',
65 help = 'rename an existing development branch',
66 action = 'store_true'),
67 make_option('-u', '--unprotect',
68 help = 'allow StGIT to modify this branch',
69 action = 'store_true')]
72 def __is_current_branch(branch_name):
73 return crt_series.get_name() == branch_name
75 def __print_branch(branch_name, length):
80 branch = stack.Series(branch_name)
82 if branch.is_initialised():
84 if __is_current_branch(branch_name):
86 if branch.get_protected():
88 out.stdout(current + ' ' + initialized + protected + '\t'
89 + branch_name.ljust(length) + ' | ' + branch.get_description())
91 def __delete_branch(doomed_name, force = False):
92 doomed = stack.Series(doomed_name)
94 if doomed.get_protected():
95 raise CmdException, 'This branch is protected. Delete is not permitted'
97 out.start('Deleting branch "%s"' % doomed_name)
99 if __is_current_branch(doomed_name):
100 raise CmdException('Cannot delete the current branch')
106 def func(parser, options, args):
110 if len(args) == 0 or len(args) > 2:
111 parser.error('incorrect number of arguments')
113 check_local_changes()
115 check_head_top_equal(crt_series)
121 branchpoint = git.rev_parse(args[1])
123 # first, look for branchpoint in well-known branch namespaces
124 for namespace in ('refs/heads/', 'remotes/'):
125 # check if branchpoint exists in namespace
127 maybehead = git.rev_parse(namespace + args[1])
128 except git.GitException:
131 # check if git resolved branchpoint to this namespace
132 if maybehead and branchpoint == maybehead:
133 # we are for sure referring to a branch
134 parentbranch = namespace + args[1]
136 except git.GitException:
137 # should use a more specific exception to catch only
139 out.info('Don\'t know how to determine parent branch'
140 ' from "%s"' % args[1])
141 # exception in branch = rev_parse() leaves branchpoint unbound
144 tree_id = git_id(crt_series, branchpoint or args[1])
147 out.info('Recording "%s" as parent branch' % parentbranch)
149 out.info('Don\'t know how to determine parent branch'
150 ' from "%s"' % args[1])
152 # branch stack off current branch
153 parentbranch = git.get_head_file()
156 parentremote = git.identify_remote(parentbranch)
158 out.info('Using remote "%s" to pull parent from'
161 out.info('Recording as a local branch')
163 # no known parent branch, can't guess the remote
166 stack.Series(args[0]).init(create_at = tree_id,
167 parent_remote = parentremote,
168 parent_branch = parentbranch)
170 out.info('Branch "%s" created' % args[0])
176 clone = crt_series.get_name() + \
177 time.strftime('-%C%y%m%d-%H%M%S')
181 parser.error('incorrect number of arguments')
183 check_local_changes()
185 check_head_top_equal(crt_series)
187 out.start('Cloning current branch to "%s"' % clone)
188 crt_series.clone(clone)
196 parser.error('incorrect number of arguments')
197 __delete_branch(args[0], options.force)
203 parser.error('incorrect number of arguments')
205 branches = git.get_heads()
209 out.info('Available branches:')
210 max_len = max([len(i) for i in branches])
212 __print_branch(i, max_len)
214 out.info('No branches')
217 elif options.protect:
220 branch_name = crt_series.get_name()
222 branch_name = args[0]
224 parser.error('incorrect number of arguments')
225 branch = stack.Series(branch_name)
227 if not branch.is_initialised():
228 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
231 out.start('Protecting branch "%s"' % branch_name)
240 parser.error('incorrect number of arguments')
242 if __is_current_branch(args[0]):
243 raise CmdException, 'Renaming the current branch is not supported'
245 stack.Series(args[0]).rename(args[1])
247 out.info('Renamed branch "%s" to "%s"' % (args[0], args[1]))
251 elif options.unprotect:
254 branch_name = crt_series.get_name()
256 branch_name = args[0]
258 parser.error('incorrect number of arguments')
259 branch = stack.Series(branch_name)
261 if not branch.is_initialised():
262 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
265 out.info('Unprotecting branch "%s"' % branch_name)
271 elif options.description is not None:
274 branch_name = crt_series.get_name()
276 branch_name = args[0]
278 parser.error('incorrect number of arguments')
279 branch = stack.Series(branch_name)
281 if not branch.is_initialised():
282 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
285 branch.set_description(options.description)
291 if __is_current_branch(args[0]):
292 raise CmdException, 'Branch "%s" is already the current branch' \
295 check_local_changes()
297 check_head_top_equal(crt_series)
299 out.start('Switching to branch "%s"' % args[0])
300 git.switch_branch(args[0])
304 # default action: print the current branch
306 parser.error('incorrect number of arguments')
308 print crt_series.get_name()