d3484097704b9f6cc492424d015d0617d3aea0fb
[stgit] / stgit / commands / branch.py
1 """Branch command
2 """
3
4 __copyright__ = """
5 Copyright (C) 2005, Chuck Lever <cel@netapp.com>
6
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.
10
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.
15
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
19 """
20
21 import sys, os, time
22 from optparse import OptionParser, make_option
23
24 from stgit.commands.common import *
25 from stgit.utils import *
26 from stgit import stack, git, basedir
27
28
29 help = 'manage development branches'
30 usage = """%prog [options] branch-name [commit-id]
31
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.
36
37 When displaying the branches, the names can be prefixed with
38 's' (StGIT managed) or 'p' (protected).
39
40 If not given any options, switch to the named branch."""
41
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('--convert',
49 help = 'switch between old and new format branches',
50 action = 'store_true'),
51 make_option('--delete',
52 help = 'delete an existing development branch',
53 action = 'store_true'),
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 "stg pull" 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 "stg pull" to modify this branch',
68 action = 'store_true')]
69
70
71 def __is_current_branch(branch_name):
72 return crt_series.get_branch() == branch_name
73
74 def __print_branch(branch_name, length):
75 initialized = ' '
76 current = ' '
77 protected = ' '
78
79 branch = stack.Series(branch_name)
80
81 if branch.is_initialised():
82 initialized = 's'
83 if __is_current_branch(branch_name):
84 current = '>'
85 if branch.get_protected():
86 protected = 'p'
87 print current + ' ' + initialized + protected + '\t' + \
88 branch_name.ljust(length) + ' | ' + branch.get_description()
89
90 def __delete_branch(doomed_name, force = False):
91 doomed = stack.Series(doomed_name)
92
93 if doomed.get_protected():
94 raise CmdException, 'This branch is protected. Delete is not permitted'
95
96 print 'Deleting branch "%s"...' % doomed_name,
97 sys.stdout.flush()
98
99 if __is_current_branch(doomed_name):
100 check_local_changes()
101 check_conflicts()
102 check_head_top_equal()
103
104 if doomed_name != 'master':
105 git.switch_branch('master')
106
107 doomed.delete(force)
108
109 if doomed_name != 'master':
110 git.delete_branch(doomed_name)
111
112 print 'done'
113
114 def func(parser, options, args):
115
116 if options.create:
117
118 if len(args) == 0 or len(args) > 2:
119 parser.error('incorrect number of arguments')
120
121 check_local_changes()
122 check_conflicts()
123 check_head_top_equal()
124
125 tree_id = None
126 if len(args) == 2:
127 tree_id = git_id(args[1])
128
129 stack.Series(args[0]).init(create_at = tree_id)
130
131 print 'Branch "%s" created.' % args[0]
132 return
133
134 elif options.clone:
135
136 if len(args) == 0:
137 clone = crt_series.get_branch() + \
138 time.strftime('-%C%y%m%d-%H%M%S')
139 elif len(args) == 1:
140 clone = args[0]
141 else:
142 parser.error('incorrect number of arguments')
143
144 check_local_changes()
145 check_conflicts()
146 check_head_top_equal()
147
148 print 'Cloning current branch to "%s"...' % clone,
149 sys.stdout.flush()
150 crt_series.clone(clone)
151 print 'done'
152
153 return
154
155 elif options.convert:
156
157 if len(args) != 0:
158 parser.error('incorrect number of arguments')
159
160 crt_series.convert()
161 return
162
163 elif options.delete:
164
165 if len(args) != 1:
166 parser.error('incorrect number of arguments')
167 __delete_branch(args[0], options.force)
168 return
169
170 elif options.list:
171
172 if len(args) != 0:
173 parser.error('incorrect number of arguments')
174
175 branches = []
176 basepath = os.path.join(basedir.get(), 'refs', 'heads')
177 for path, files, dirs in walk_tree(basepath):
178 branches += [os.path.join(path, f) for f in files]
179 branches.sort()
180
181 if branches:
182 print 'Available branches:'
183 max_len = max([len(i) for i in branches])
184 for i in branches:
185 __print_branch(i, max_len)
186 else:
187 print 'No branches'
188 return
189
190 elif options.protect:
191
192 if len(args) == 0:
193 branch_name = crt_series.get_branch()
194 elif len(args) == 1:
195 branch_name = args[0]
196 else:
197 parser.error('incorrect number of arguments')
198 branch = stack.Series(branch_name)
199
200 if not branch.is_initialised():
201 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
202 % branch_name
203
204 print 'Protecting branch "%s"...' % branch_name,
205 sys.stdout.flush()
206 branch.protect()
207 print 'done'
208
209 return
210
211 elif options.rename:
212
213 if len(args) != 2:
214 parser.error('incorrect number of arguments')
215
216 if __is_current_branch(args[0]):
217 raise CmdException, 'Renaming the current branch is not supported'
218
219 stack.Series(args[0]).rename(args[1])
220
221 print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
222
223 return
224
225 elif options.unprotect:
226
227 if len(args) == 0:
228 branch_name = crt_series.get_branch()
229 elif len(args) == 1:
230 branch_name = args[0]
231 else:
232 parser.error('incorrect number of arguments')
233 branch = stack.Series(branch_name)
234
235 if not branch.is_initialised():
236 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
237 % branch_name
238
239 print 'Unprotecting branch "%s"...' % branch_name,
240 sys.stdout.flush()
241 branch.unprotect()
242 print 'done'
243
244 return
245
246 elif len(args) == 1:
247
248 if __is_current_branch(args[0]):
249 raise CmdException, 'Branch "%s" is already the current branch' \
250 % args[0]
251
252 check_local_changes()
253 check_conflicts()
254 check_head_top_equal()
255
256 print 'Switching to branch "%s"...' % args[0],
257 sys.stdout.flush()
258
259 git.switch_branch(args[0])
260
261 print 'done'
262 return
263
264 # default action: print the current branch
265 if len(args) != 0:
266 parser.error('incorrect number of arguments')
267
268 print crt_series.get_branch()