Add a "--clone" option to "stg branch"
[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 options = [make_option('-c', '--create',
41 help = 'create a new development branch',
42 action = 'store_true'),
43 make_option('--clone',
44 help = 'clone the contents of the current branch',
45 action = 'store_true'),
46 make_option('--delete',
47 help = 'delete an existing development branch',
48 action = 'store_true'),
49 make_option('--force',
50 help = 'force a delete when the series is not empty',
51 action = 'store_true'),
52 make_option('-l', '--list',
53 help = 'list branches contained in this repository',
54 action = 'store_true'),
55 make_option('-p', '--protect',
56 help = 'prevent "stg pull" from modifying this branch',
57 action = 'store_true'),
58 make_option('-r', '--rename',
59 help = 'rename an existing development branch',
60 action = 'store_true'),
61 make_option('-u', '--unprotect',
62 help = 'allow "stg pull" to modify this branch',
63 action = 'store_true')]
64
65
66 def __is_current_branch(branch_name):
67 return crt_series.get_branch() == branch_name
68
69 def __print_branch(branch_name, length):
70 initialized = ' '
71 current = ' '
72 protected = ' '
73
74 branch = stack.Series(branch_name)
75
76 if branch.is_initialised():
77 initialized = 's'
78 if __is_current_branch(branch_name):
79 current = '>'
80 if branch.get_protected():
81 protected = 'p'
82 print current + ' ' + initialized + protected + '\t' + \
83 branch_name.ljust(length) + ' | ' + branch.get_description()
84
85 def __delete_branch(doomed_name, force = False):
86 doomed = stack.Series(doomed_name)
87
88 if doomed.get_protected():
89 raise CmdException, 'This branch is protected. Delete is not permitted'
90
91 print 'Deleting branch "%s"...' % doomed_name,
92 sys.stdout.flush()
93
94 if __is_current_branch(doomed_name):
95 check_local_changes()
96 check_conflicts()
97 check_head_top_equal()
98
99 if doomed_name != 'master':
100 git.switch_branch('master')
101
102 doomed.delete(force)
103
104 if doomed_name != 'master':
105 git.delete_branch(doomed_name)
106
107 print 'done'
108
109 def func(parser, options, args):
110
111 if options.create:
112
113 if len(args) == 0 or len(args) > 2:
114 parser.error('incorrect number of arguments')
115
116 check_local_changes()
117 check_conflicts()
118 check_head_top_equal()
119
120 tree_id = None
121 if len(args) == 2:
122 tree_id = args[1]
123
124 git.create_branch(args[0], tree_id)
125 stack.Series(args[0]).init()
126
127 print 'Branch "%s" created.' % args[0]
128 return
129
130 elif options.clone:
131
132 if len(args) == 0:
133 clone = crt_series.get_branch() + \
134 time.strftime('-%C%y%m%d-%H%M%S')
135 elif len(args) == 1:
136 clone = args[0]
137 else:
138 parser.error('incorrect number of arguments')
139
140 check_local_changes()
141 check_conflicts()
142 check_head_top_equal()
143
144 print 'Cloning current branch to "%s"...' % clone,
145 sys.stdout.flush()
146 crt_series.clone(clone)
147 print 'done'
148
149 return
150
151 elif options.delete:
152
153 if len(args) != 1:
154 parser.error('incorrect number of arguments')
155 __delete_branch(args[0], options.force)
156 return
157
158 elif options.list:
159
160 if len(args) != 0:
161 parser.error('incorrect number of arguments')
162
163 branches = os.listdir(os.path.join(git.get_base_dir(), 'refs', 'heads'))
164 branches.sort()
165 max_len = max([len(i) for i in branches])
166
167 print 'Available branches:'
168 for i in branches:
169 __print_branch(i, max_len)
170 return
171
172 elif options.protect:
173
174 if len(args) == 0:
175 branch_name = crt_series.get_branch()
176 elif len(args) == 1:
177 branch_name = args[0]
178 else:
179 parser.error('incorrect number of arguments')
180 branch = stack.Series(branch_name)
181
182 if not branch.is_initialised():
183 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
184 % branch_name
185
186 print 'Protecting branch "%s"...' % branch_name,
187 sys.stdout.flush()
188 branch.protect()
189 print 'done'
190
191 return
192
193 elif options.rename:
194
195 if len(args) != 2:
196 parser.error('incorrect number of arguments')
197
198 if __is_current_branch(args[0]):
199 raise CmdException, 'Renaming the current branch is not supported'
200
201 stack.Series(args[0]).rename(args[1])
202
203 print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
204
205 return
206
207 elif options.unprotect:
208
209 if len(args) == 0:
210 branch_name = crt_series.get_branch()
211 elif len(args) == 1:
212 branch_name = args[0]
213 else:
214 parser.error('incorrect number of arguments')
215 branch = stack.Series(branch_name)
216
217 if not branch.is_initialised():
218 raise CmdException, 'Branch "%s" is not controlled by StGIT' \
219 % branch_name
220
221 print 'Unprotecting branch "%s"...' % branch_name,
222 sys.stdout.flush()
223 branch.unprotect()
224 print 'done'
225
226 return
227
228 elif len(args) == 1:
229
230 if __is_current_branch(args[0]):
231 raise CmdException, 'Branch "%s" is already the current branch' \
232 % args[0]
233
234 check_local_changes()
235 check_conflicts()
236 check_head_top_equal()
237
238 print 'Switching to branch "%s"...' % args[0],
239 sys.stdout.flush()
240
241 git.switch_branch(args[0])
242
243 print 'done'
244 return
245
246 # default action: print the current branch
247 if len(args) != 0:
248 parser.error('incorrect number of arguments')
249
250 print crt_series.get_branch()