chiark / gitweb /
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()