chiark / gitweb /
Correctly identify the parent branch (bug #10014)
[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
8319e9ba 21import sys, os, time, re
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
7b601c9e 43directory = DirectoryGotoToplevel()
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()
6972fd6b 115 check_head_top_equal(crt_series)
fe4f6d58 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
8319e9ba
CM
123 # parent branch?
124 head_re = re.compile('refs/(heads|remotes)/')
125 ref_re = re.compile(args[1] + '$')
126 for ref in git.all_refs():
127 if head_re.match(ref) and ref_re.search(ref):
128 # args[1] is a valid ref from the branchpoint
129 # setting above
130 parentbranch = args[1]
131 break;
4f5a8c72 132 except git.GitException:
27ac2b7e
KH
133 # should use a more specific exception to catch only
134 # non-git refs ?
135 out.info('Don\'t know how to determine parent branch'
136 ' from "%s"' % args[1])
0d90f0ac
YD
137 # exception in branch = rev_parse() leaves branchpoint unbound
138 branchpoint = None
4f5a8c72 139
42cd003e 140 tree_id = git_id(crt_series, branchpoint or args[1])
0d90f0ac
YD
141
142 if parentbranch:
143 out.info('Recording "%s" as parent branch' % parentbranch)
144 else:
145 out.info('Don\'t know how to determine parent branch'
146 ' from "%s"' % args[1])
4f5a8c72
YD
147 else:
148 # branch stack off current branch
149 parentbranch = git.get_head_file()
150
151 if parentbranch:
152 parentremote = git.identify_remote(parentbranch)
153 if parentremote:
27ac2b7e
KH
154 out.info('Using remote "%s" to pull parent from'
155 % parentremote)
4f5a8c72 156 else:
27ac2b7e 157 out.info('Recording as a local branch')
4f5a8c72 158 else:
4646e7a3 159 # no known parent branch, can't guess the remote
4f5a8c72
YD
160 parentremote = None
161
162 stack.Series(args[0]).init(create_at = tree_id,
163 parent_remote = parentremote,
164 parent_branch = parentbranch)
7b1ba1a6 165
27ac2b7e 166 out.info('Branch "%s" created' % args[0])
7b1ba1a6
CL
167 return
168
cc3db2b1
CL
169 elif options.clone:
170
171 if len(args) == 0:
d37ff079 172 clone = crt_series.get_name() + \
cc3db2b1
CL
173 time.strftime('-%C%y%m%d-%H%M%S')
174 elif len(args) == 1:
175 clone = args[0]
176 else:
177 parser.error('incorrect number of arguments')
178
179 check_local_changes()
180 check_conflicts()
6972fd6b 181 check_head_top_equal(crt_series)
cc3db2b1 182
27ac2b7e 183 out.start('Cloning current branch to "%s"' % clone)
cc3db2b1 184 crt_series.clone(clone)
27ac2b7e 185 out.done()
cc3db2b1
CL
186
187 return
188
7b1ba1a6
CL
189 elif options.delete:
190
191 if len(args) != 1:
192 parser.error('incorrect number of arguments')
fe4f6d58 193 __delete_branch(args[0], options.force)
7b1ba1a6
CL
194 return
195
196 elif options.list:
197
198 if len(args) != 0:
199 parser.error('incorrect number of arguments')
200
262d31dc 201 branches = git.get_heads()
7b1ba1a6
CL
202 branches.sort()
203