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