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
21 import sys
, os
, time
, re
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 __is_current_branch(doomed_name
):
95 raise CmdException('Cannot delete the current branch')
96 if doomed
.get_protected():
97 raise CmdException
, 'This branch is protected. Delete is not permitted'
99 out
.start('Deleting branch "%s"' % doomed_name
)
103 def func(parser
, options
, args
):
107 if len(args
) == 0 or len(args
) > 2:
108 parser
.error('incorrect number of arguments')
110 check_local_changes()
112 check_head_top_equal(crt_series
)
118 branchpoint
= git
.rev_parse(args
[1])
121 head_re
= re
.compile('refs/(heads|remotes)/')
122 ref_re
= re
.compile(args
[1] + '$')
123 for ref
in git
.all_refs():
124 if head_re
.match(ref
) and ref_re
.search(ref
):
125 # args[1] is a valid ref from the branchpoint
127 parentbranch
= args
[1]
129 except git
.GitException
:
130 # should use a more specific exception to catch only
132 out
.info('Don\'t know how to determine parent branch'
133 ' from "%s"' % args
[1])
134 # exception in branch = rev_parse() leaves branchpoint unbound
137 tree_id
= git_id(crt_series
, branchpoint
or args
[1])
140 out
.info('Recording "%s" as parent branch' % parentbranch
)
142 out
.info('Don\'t know how to determine parent branch'
143 ' from "%s"' % args
[1])
145 # branch stack off current branch
146 parentbranch
= git
.get_head_file()
149 parentremote
= git
.identify_remote(parentbranch
)
151 out
.info('Using remote "%s" to pull parent from'
154 out
.info('Recording as a local branch')
156 # no known parent branch, can't guess the remote
159 stack
.Series(args
[0]).init(create_at
= tree_id
,
160 parent_remote
= parentremote
,
161 parent_branch
= parentbranch
)
163 out
.info('Branch "%s" created' % args
[0])
169 clone
= crt_series
.get_name() + \
170 time
.strftime('-%C%y%m%d-%H%M%S')
174 parser
.error('incorrect number of arguments')
176 check_local_changes()
178 check_head_top_equal(crt_series
)
180 out
.start('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')
198 branches
= git
.get_heads()
202 out
.info('Available branches:')
203 max_len
= max([len(i
) for i
in branches
])
205 __print_branch(i
, max_len
)
207 out
.info('No branches')
210 elif options
.protect
:
213 branch_name
= crt_series
.get_name()
215 branch_name
= args
[0]
217 parser
.error('incorrect number of arguments')
218 branch
= stack
.Series(branch_name
)
220 if not branch
.is_initialised():
221 raise CmdException
, 'Branch "%s" is not controlled by StGIT' \
224 out
.start('Protecting branch "%s"' % branch_name
)
233 parser
.error('incorrect number of arguments')
235 if __is_current_branch(args
[0]):
236 raise CmdException
, 'Renaming the current branch is not supported'
238 stack
.Series(args
[0]).rename(args
[1])
240 out
.info('Renamed branch "%s" to "%s"' %
(args
[0], args
[1]))
244 elif options
.unprotect
:
247 branch_name
= crt_series
.get_name()
249 branch_name
= args
[0]
251 parser
.error('incorrect number of arguments')
252 branch
= stack
.Series(branch_name
)
254 if not branch
.is_initialised():
255 raise CmdException
, 'Branch "%s" is not controlled by StGIT' \
258 out
.info('Unprotecting branch "%s"' % branch_name
)
264 elif options
.description
is not None:
267 branch_name
= crt_series
.get_name()
269 branch_name
= args
[0]
271 parser
.error('incorrect number of arguments')
272 branch
= stack
.Series(branch_name
)
274 if not branch
.is_initialised():
275 raise CmdException
, 'Branch "%s" is not controlled by StGIT' \
278 branch
.set_description(options
.description
)
284 if __is_current_branch(args
[0]):
285 raise CmdException
, 'Branch "%s" is already the current branch' \
288 check_local_changes()
290 check_head_top_equal(crt_series
)
292 out
.start('Switching to branch "%s"' % args
[0])
293 git
.switch_branch(args
[0])
297 # default action: print the current branch
299 parser
.error('incorrect number of arguments')
301 print crt_series
.get_name()