Recognize refs under remotes/ as parent branch on stack creation.
[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 *
170f576b 26from stgit import stack, git, basedir
7b1ba1a6
CL
27
28
4ec67741 29help = 'manage patch stacks'
7b1ba1a6
CL
30usage = """%prog [options] branch-name [commit-id]
31
cc3db2b1 32Create, clone, switch between, rename, or delete development branches
7b1ba1a6
CL
33within a git repository. By default, a single branch called 'master'
34is always created in a new repository. This subcommand allows you to
cc3db2b1 35manage several patch series in the same repository via GIT branches.
7b1ba1a6
CL
36
37When displaying the branches, the names can be prefixed with
a8032a85
CL
38's' (StGIT managed) or 'p' (protected).
39
40If not given any options, switch to the named branch."""
7b1ba1a6
CL
41
42options = [make_option('-c', '--create',
43 help = 'create a new development branch',
44 action = 'store_true'),
cc3db2b1
CL
45 make_option('--clone',
46 help = 'clone the contents of the current branch',
47 action = 'store_true'),
7b1ba1a6
CL
48 make_option('--delete',
49 help = 'delete an existing development branch',
50 action = 'store_true'),
302600dc
CM
51 make_option('-d', '--description',
52 help = 'set the branch description'),
7b1ba1a6
CL
53 make_option('--force',
54 help = 'force a delete when the series is not empty',
55 action = 'store_true'),
56 make_option('-l', '--list',
57 help = 'list branches contained in this repository',
58 action = 'store_true'),
0b4b9499 59 make_option('-p', '--protect',
f644f510 60 help = 'prevent StGIT from modifying this branch',
0b4b9499 61 action = 'store_true'),
7b1ba1a6
CL
62 make_option('-r', '--rename',
63 help = 'rename an existing development branch',
0b4b9499
CL
64 action = 'store_true'),
65 make_option('-u', '--unprotect',
f644f510 66 help = 'allow StGIT to modify this branch',
7b1ba1a6
CL
67 action = 'store_true')]
68
69
fe4f6d58 70def __is_current_branch(branch_name):
d37ff079 71 return crt_series.get_name() == branch_name
7b1ba1a6 72
862ba51a 73def __print_branch(branch_name, length):
7b1ba1a6
CL
74 initialized = ' '
75 current = ' '
0b4b9499 76 protected = ' '
ebfd0156
CL
77
78 branch = stack.Series(branch_name)
79
2d00440c 80 if branch.is_initialised():
7b1ba1a6 81 initialized = 's'
fe4f6d58 82 if __is_current_branch(branch_name):
7b1ba1a6 83 current = '>'
ebfd0156 84 if branch.get_protected():
0b4b9499 85 protected = 'p'
27ac2b7e
KH
86 out.stdout(current + ' ' + initialized + protected + '\t'
87 + branch_name.ljust(length) + ' | ' + branch.get_description())
7b1ba1a6 88
fe4f6d58 89def __delete_branch(doomed_name, force = False):
ebfd0156
CL
90 doomed = stack.Series(doomed_name)
91
92 if doomed.get_protected():
0b4b9499
CL
93 raise CmdException, 'This branch is protected. Delete is not permitted'
94
27ac2b7e 95 out.start('Deleting branch "%s"' % doomed_name)
fe4f6d58
CM
96
97 if __is_current_branch(doomed_name):
98 check_local_changes()
99 check_conflicts()
100 check_head_top_equal()
101
102 if doomed_name != 'master':
103 git.switch_branch('master')
7b1ba1a6 104
ebfd0156 105 doomed.delete(force)
7b1ba1a6
CL
106
107 if doomed_name != 'master':
108 git.delete_branch(doomed_name)
109
27ac2b7e 110 out.done()
7b1ba1a6 111
7b1ba1a6
CL
112def func(parser, options, args):
113
114 if options.create:
115
116 if len(args) == 0 or len(args) > 2:
117 parser.error('incorrect number of arguments')
fe4f6d58
CM
118
119 check_local_changes()
120 check_conflicts()
121 check_head_top_equal()
122
7b1ba1a6 123 tree_id = None
4f5a8c72 124 if len(args) >= 2:
0d90f0ac 125 parentbranch = None
4f5a8c72 126 try:
0d90f0ac
YD
127 branchpoint = git.rev_parse(args[1])
128
129 # first, look for branchpoint in well-known branch namespaces
130 for namespace in ('refs/heads/', 'remotes/'):
131 # check if branchpoint exists in namespace
132 try:
133 maybehead = git.rev_parse(namespace + args[1])
134 except git.GitException:
135 maybehead = None
136
137 # check if git resolved branchpoint to this namespace
138 if maybehead and branchpoint == maybehead:
139 # we are for sure referring to a branch
140 parentbranch = namespace + args[1]
141
4f5a8c72 142 except git.GitException:
27ac2b7e
KH
143 # should use a more specific exception to catch only
144 # non-git refs ?
145 out.info('Don\'t know how to determine parent branch'
146 ' from "%s"' % args[1])
0d90f0ac
YD
147 # exception in branch = rev_parse() leaves branchpoint unbound
148 branchpoint = None
4f5a8c72 149
0d90f0ac
YD
150 tree_id = branchpoint or git_id(args[1])
151
152 if parentbranch:
153 out.info('Recording "%s" as parent branch' % parentbranch)
154 else:
155 out.info('Don\'t know how to determine parent branch'
156 ' from "%s"' % args[1])
4f5a8c72
YD
157 else:
158 # branch stack off current branch
159 parentbranch = git.get_head_file()
160
161 if parentbranch:
162 parentremote = git.identify_remote(parentbranch)
163 if parentremote:
27ac2b7e
KH
164 out.info('Using remote "%s" to pull parent from'
165 % parentremote)
4f5a8c72 166 else:
27ac2b7e 167 out.info('Recording as a local branch')
4f5a8c72 168 else:
4646e7a3 169 # no known parent branch, can't guess the remote
4f5a8c72
YD
170 parentremote = None
171
172 stack.Series(args[0]).init(create_at = tree_id,
173 parent_remote = parentremote,
174 parent_branch = parentbranch)
7b1ba1a6 175
27ac2b7e 176 out.info('Branch "%s" created' % args[0])
7b1ba1a6
CL
177 return
178
cc3db2b1
CL
179 elif options.clone:
180
181 if len(args) == 0:
d37ff079 182 clone = crt_series.get_name() + \
cc3db2b1
CL
183 time.strftime('-%C%y%m%d-%H%M%S')
184 elif len(args) == 1:
185 clone = args[0]
186 else:
187 parser.error('incorrect number of arguments')
188
189 check_local_changes()
190 check_conflicts()
191 check_head_top_equal()
192
27ac2b7e 193 out.start('Cloning current branch to "%s"' % clone)
cc3db2b1 194 crt_series.clone(clone)
27ac2b7e 195 out.done()
cc3db2b1
CL
196
197 return
198
7b1ba1a6
CL
199 elif options.delete:
200
201 if len(args) != 1:
202 parser.error('incorrect number of arguments')
fe4f6d58 203 __delete_branch(args[0], options.force)
7b1ba1a6
CL
204 return
205
206 elif options.list:
207
208 if len(args) != 0:
209 parser.error('incorrect number of arguments')
210