Allow tag objects to be passed on the command line
[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
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 git.create_branch(args[0], tree_id)
130 stack.Series(args[0]).init()
131
132 print 'Branch "%s" created.' % args[0]
133 return
134
135 elif options.clone:
136
137 if len(args) == 0:
138 clone = crt_series.get_branch() + \
139 time.strftime('-%C%y%m%d-%H%M%S')
140 elif len(args) == 1:
141 clone = args[0]
142 else:
143 parser.error('incorrect number of arguments')
144
145 check_local_changes()
146 check_conflicts()
147 check_head_top_equal()
148
149 print 'Cloning current branch to "%s"...' % clone,
150 sys.stdout.flush()
151 crt_series.clone(clone)
152 print 'done'
153
154 return
155
156 elif options.convert:
157
158 if len(args) != 0:
159 parser.error('incorrect number of arguments')
160
161 crt_series.convert()
162 return
163
164 elif options.delete:
165
166 if len(args) != 1:
167 parser.error('incorrect number of arguments')
168 __delete_branch(args[0], options.force)
169 return
170
171 elif options.list:
172
173 if len(args) != 0:
174 parser.error('incorrect number of arguments')
175
176 branches = os.listdir(os.path.join(git.get_base_dir(), 'refs', 'heads'))
177 branches.sort()
178 max_len = max([len(i) for i in branches])
179
180 print 'Available branches:'
181 for i in branches:
182 __print_branch(i, max_len)
183 return
184
185 elif options.protect:
186
187 if len(args) == 0:
188 branch_name = crt_series.get_branch()
189 elif len(args) == 1:
190 branch_name = args[0]
191 else:
192 parser.error('incorrect number of arguments')
193 branch = stack.Series(branch_name)
194
195 if not branch.is_initialised():
196 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
197 % branch_name
198
199 print 'Protecting branch "%s"...' % branch_name,
200 sys.stdout.flush()
201 branch.protect()
202 print 'done'
203
204 return
205
206 elif options.rename:
207
208 if len(args) != 2:
209 parser.error('incorrect number of arguments')
210
211 if __is_current_branch(args[0]):
212 raise CmdException, 'Renaming the current branch is not supported'
213
214 stack.Series(args[0]).rename(args[1])
215
216 print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
217
218 return
219
220 elif options.unprotect:
221
222 if len(args) == 0:
223 branch_name = crt_series.get_branch()
224 elif len(args) == 1:
225 branch_name = args[0]
226 else:
227 parser.error('incorrect number of arguments')
228 branch = stack.Series(branch_name)
229
230 if not branch.is_initialised():
231 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
232 % branch_name
233
234 print 'Unprotecting branch "%s"...' % branch_name,
235 sys.stdout.flush()
236 branch.unprotect()
237 print 'done'
238
239 return
240
241 elif len(args) == 1:
242
243 if __is_current_branch(args[0]):
244 raise CmdException, 'Branch "%s" is already the current branch' \
245 % args[0]
246
247 check_local_changes()
248 check_conflicts()
249 check_head_top_equal()
250
251 print 'Switching to branch "%s"...' % args[0],
252 sys.stdout.flush()
253
254 git.switch_branch(args[0])
255
256 print 'done'
257 return
258
259 # default action: print the current branch
260 if len(args) != 0:
261 parser.error('incorrect number of arguments')
262
263 print crt_series.get_branch()