chiark / gitweb /
Infrastructure for current directory handling
[stgit] / stgit / commands / branch.py
CommitLineData
7b1ba1a6
CL
1"""Branch command
2"""
3
4__copyright__ = """
5Copyright (C) 2005, Chuck Lever <cel@netapp.com>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License version 2 as
9published by the Free Software Foundation.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19"""
20
cc3db2b1 21import sys, os, time
7b1ba1a6
CL
22from optparse import OptionParser, make_option
23
24from stgit.commands.common import *
25from stgit.utils import *
5e888f30 26from stgit.out import *
170f576b 27from stgit import stack, git, basedir
7b1ba1a6
CL
28
29
4ec67741 30help = 'manage patch stacks'
7b1ba1a6
CL
31usage = """%prog [options] branch-name [commit-id]
32
cc3db2b1 33Create, clone, switch between, rename, or delete development branches
7b1ba1a6
CL
34within a git repository. By default, a single branch called 'master'
35is always created in a new repository. This subcommand allows you to
cc3db2b1 36manage several patch series in the same repository via GIT branches.
7b1ba1a6
CL
37
38When displaying the branches, the names can be prefixed with
a8032a85
CL
39's' (StGIT managed) or 'p' (protected).
40
41If not given any options, switch to the named branch."""
7b1ba1a6 42
6dd8fafa 43directory = DirectoryHasRepository()
7b1ba1a6
CL
44options = [make_option('-c', '--create',
45 help = 'create a new development branch',
46 action = 'store_true'),
cc3db2b1
CL
47 make_option('--clone',
48 help = 'clone the contents of the current branch',
49 action = 'store_true'),
7b1ba1a6
CL
50 make_option('--delete',
51 help = 'delete an existing development branch',
52 action = 'store_true'),
302600dc
CM
53 make_option('-d', '--description',
54 help = 'set the branch description'),
7b1ba1a6
CL
55 make_option('--force',
56 help = 'force a delete when the series is not empty',
57 action = 'store_true'),
58 make_option('-l', '--list',
59 help = 'list branches contained in this repository',
60 action = 'store_true'),
0b4b9499 61 make_option('-p', '--protect',
f644f510 62 help = 'prevent StGIT from modifying this branch',
0b4b9499 63 action = 'store_true'),
7b1ba1a6
CL
64 make_option('-r', '--rename',
65 help = 'rename an existing development branch',
0b4b9499
CL
66 action = 'store_true'),
67 make_option('-u', '--unprotect',
f644f510 68 help = 'allow StGIT to modify this branch',
7b1ba1a6
CL
69 action = 'store_true')]
70
71
fe4f6d58 72def __is_current_branch(branch_name):
d37ff079 73 return crt_series.get_name() == branch_name
7b1ba1a6 74
862ba51a 75def __print_branch(branch_name, length):
7b1ba1a6
CL
76 initialized = ' '
77 current = ' '
0b4b9499 78 protected = ' '
ebfd0156
CL
79
80 branch = stack.Series(branch_name)
81
2d00440c 82 if branch.is_initialised():
7b1ba1a6 83 initialized = 's'
fe4f6d58 84 if __is_current_branch(branch_name):
7b1ba1a6 85 current = '>'
ebfd0156 86 if branch.get_protected():
0b4b9499 87 protected = 'p'
27ac2b7e
KH
88 out.stdout(current + ' ' + initialized + protected + '\t'
89 + branch_name.ljust(length) + ' | ' + branch.get_description())
7b1ba1a6 90
fe4f6d58 91def __delete_branch(doomed_name, force = False):
ebfd0156
CL
92 doomed = stack.Series(doomed_name)
93
94 if doomed.get_protected():
0b4b9499
CL
95 raise CmdException, 'This branch is protected. Delete is not permitted'
96
27ac2b7e 97 out.start('Deleting branch "%s"' % doomed_name)
fe4f6d58
CM
98
99 if __is_current_branch(doomed_name):
57059512 100 raise CmdException('Cannot delete the current branch')
7b1ba1a6 101
ebfd0156 102 doomed.delete(force)
7b1ba1a6 103
27ac2b7e 104 out.done()
7b1ba1a6 105
7b1ba1a6
CL
106def func(parser, options, args):
107
108 if options.create:
109
110 if len(args) == 0 or len(args) > 2:
111 parser.error('incorrect number of arguments')
fe4f6d58
CM
112
113 check_local_changes()
114 check_conflicts()
115 check_head_top_equal()
116
7b1ba1a6 117 tree_id = None
4f5a8c72 118 if len(args) >= 2:
0d90f0ac 119 parentbranch = None
4f5a8c72 120 try:
0d90f0ac
YD
121 branchpoint = git.rev_parse(args[1])
122
123 # first, look for branchpoint in well-known branch namespaces
124 for namespace in ('refs/heads/', 'remotes/'):
125 # check if branchpoint exists in namespace
126 try:
127 maybehead = git.rev_parse(namespace + args[1])
128 except git.GitException:
129 maybehead = None
130
131 # check if git resolved branchpoint to this namespace
132 if maybehead and branchpoint == maybehead:
133 # we are for sure referring to a branch
134 parentbranch = namespace + args[1]
135
4f5a8c72 136 except git.GitException:
27ac2b7e
KH
137 # should use a more specific exception to catch only
138 # non-git refs ?
139 out.info('Don\'t know how to determine parent branch'
140 ' from "%s"' % args[1])
0d90f0ac
YD
141 # exception in branch = rev_parse() leaves branchpoint unbound
142 branchpoint = None
4f5a8c72 143
0d90f0ac
YD
144 tree_id = branchpoint or git_id(args[1])
145
146 if parentbranch:
147 out.info('Recording "%s" as parent branch' % parentbranch)
148 else:
149 out.info('Don\'t know how to determine parent branch'
150 ' from "%s"' % args[1])
4f5a8c72
YD
151 else:
152 # branch stack off current branch
153 parentbranch = git.get_head_file()
154
155 if parentbranch:
156 parentremote = git.identify_remote(parentbranch)
157 if parentremote:
27ac2b7e
KH
158 out.info('Using remote "%s" to pull parent from'
159 % parentremote)
4f5a8c72 160 else:
27ac2b7e 161 out.info('Recording as a local branch')
4f5a8c72 162 else:
4646e7a3 163 # no known parent branch, can't guess the remote
4f5a8c72
YD
164 parentremote = None
165
166 stack.Series(args[0]).init(create_at = tree_id,
167 parent_remote = parentremote,
168 parent_branch = parentbranch)
7b1ba1a6 169
27ac2b7e 170 out.info('Branch "%s" created' % args[0])
7b1ba1a6
CL
171 return
172
cc3db2b1
CL
173 elif options.clone:
174
175 if len(args) == 0:
d37ff079 176 clone = crt_series.get_name() + \
cc3db2b1
CL
177 time.strftime('-%C%y%m%d-%H%M%S')
178 elif len(args) == 1:
179 clone = args[0]
180 else:
181 parser.error('incorrect number of arguments')
182
183 check_local_changes()
184 check_conflicts()
185 check_head_top_equal()
186
27ac2b7e 187 out.start('Cloning current branch to "%s"' % clone)
cc3db2b1 188 crt_series.clone(clone)
27ac2b7e 189 out.done()
cc3db2b1
CL
190
191 return
192
7b1ba1a6
CL
193 elif options.delete:
194
195 if len(args) != 1:
196 parser.error('incorrect number of arguments')
fe4f6d58 197 __delete_branch(args[0], options.force)
7b1ba1a6
CL
198 return
199
200 elif options.list:
201
202 if len(args) != 0:
203 parser.error('incorrect number of arguments')
204
262d31dc 205 branches = git.get_heads()
7b1ba1a6
CL
206 branches.sort()
207