chiark / gitweb /
c7561a82e83f193241daabfe7a6f030e3ac52bb5
[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 = os.listdir(os.path.join(basedir.get(), 'refs', 'heads'))
176         branches.sort()
177         max_len = max([len(i) for i in branches])
178
179         print 'Available branches:'
180         for i in branches:
181             __print_branch(i, max_len)
182         return
183
184     elif options.protect:
185
186         if len(args) == 0:
187             branch_name = crt_series.get_branch()
188         elif len(args) == 1:
189             branch_name = args[0]
190         else:
191             parser.error('incorrect number of arguments')
192         branch = stack.Series(branch_name)
193
194         if not branch.is_initialised():
195             raise CmdException, 'Branch "%s" is not controlled by StGIT' \
196                   % branch_name
197
198         print 'Protecting branch "%s"...' % branch_name,
199         sys.stdout.flush()
200         branch.protect()
201         print 'done'
202
203         return
204
205     elif options.rename:
206
207         if len(args) != 2:
208             parser.error('incorrect number of arguments')
209
210         if __is_current_branch(args[0]):
211             raise CmdException, 'Renaming the current branch is not supported'
212
213         stack.Series(args[0]).rename(args[1])
214
215         print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
216
217         return
218
219     elif options.unprotect:
220
221         if len(args) == 0:
222             branch_name = crt_series.get_branch()
223         elif len(args) == 1:
224             branch_name = args[0]
225         else:
226             parser.error('incorrect number of arguments')
227         branch = stack.Series(branch_name)
228
229         if not branch.is_initialised():
230             raise CmdException, 'Branch "%s" is not controlled by StGIT' \
231                   % branch_name
232
233         print 'Unprotecting branch "%s"...' % branch_name,
234         sys.stdout.flush()
235         branch.unprotect()
236         print 'done'
237
238         return
239
240     elif len(args) == 1:
241
242         if __is_current_branch(args[0]):
243             raise CmdException, 'Branch "%s" is already the current branch' \
244                   % args[0]
245
246         check_local_changes()
247         check_conflicts()
248         check_head_top_equal()
249
250         print 'Switching to branch "%s"...' % args[0],
251         sys.stdout.flush()
252
253         git.switch_branch(args[0])
254
255         print 'done'
256         return
257
258     # default action: print the current branch
259     if len(args) != 0:
260         parser.error('incorrect number of arguments')
261
262     print crt_series.get_branch()