Documentation: Rename link macros
[stgit] / stgit / commands / branch.py
CommitLineData
7b1ba1a6
CL
1__copyright__ = """
2Copyright (C) 2005, Chuck Lever <cel@netapp.com>
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License version 2 as
6published by the Free Software Foundation.
7
8This program is distributed in the hope that it will be useful,
9but WITHOUT ANY WARRANTY; without even the implied warranty of
10MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11GNU General Public License for more details.
12
13You should have received a copy of the GNU General Public License
14along with this program; if not, write to the Free Software
15Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16"""
17
8319e9ba 18import sys, os, time, re
575bbdae 19from stgit.argparse import opt
7b1ba1a6
CL
20from stgit.commands.common import *
21from stgit.utils import *
5e888f30 22from stgit.out import *
6c8a90e1 23from stgit import argparse, stack, git, basedir
117ed129 24from stgit.lib import log
7b1ba1a6 25
575bbdae 26help = 'Branch operations: switch, list, create, rename, delete, ...'
33ff9cdd 27kind = 'stack'
575bbdae
KH
28usage = ['',
29 '<branch>',
30 '--list',
31 '--create <new-branch> [<committish>]',
32 '--clone [<new-branch>]',
33 '--rename <old-name> <new-name>',
34 '--protect [<branch>]',
35 '--unprotect [<branch>]',
36 '--delete [--force] <branch>',
37 '--description=<description> [<branch>]']
38description = """
cc3db2b1 39Create, clone, switch between, rename, or delete development branches
575bbdae
KH
40within a git repository.
41
42'stg branch'::
43 Display the name of the current branch.
44
45'stg branch' <branch>::
46 Switch to the given branch."""
47
6c8a90e1 48args = [argparse.all_branches]
575bbdae
KH
49options = [
50 opt('-l', '--list', action = 'store_true',
51 short = 'List the branches contained in this repository', long = """
52 List each branch in the current repository, followed by its
53 branch description (if any). The current branch is prefixed
54 with '>'. Branches that have been initialized for StGit (with
760a7cfc 55 linkstg:init[]) are prefixed with 's'. Protected branches are
575bbdae
KH
56 prefixed with 'p'."""),
57 opt('-c', '--create', action = 'store_true',
58 short = 'Create (and switch to) a new branch', long = """
59 Create (and switch to) a new branch. The new branch is already
60 initialized as an StGit patch stack, so you do not have to run
760a7cfc 61 linkstg:init[] manually. If you give a committish argument,
575bbdae
KH
62 the new branch is based there; otherwise, it is based at the
63 current HEAD.
64
65 StGit will try to detect the branch off of which the new
66 branch is forked, as well as the remote repository from which
67 that parent branch is taken (if any), so that running
760a7cfc 68 linkstg:pull[] will automatically pull new commits from the
575bbdae
KH
69 correct branch. It will warn if it cannot guess the parent
70 branch (e.g. if you do not specify a branch name as
71 committish)."""),
72 opt('--clone', action = 'store_true',
73 short = 'Clone the contents of the current branch', long = """
74 Clone the current branch, under the name <new-branch> if
75 specified, or using the current branch's name plus a
76 timestamp.
77
78 The description of the new branch is set to tell it is a clone
79 of the current branch. The parent information of the new
80 branch is copied from the current branch."""),
81 opt('-r', '--rename', action = 'store_true',
82 short = 'Rename an existing branch'),
83 opt('-p', '--protect', action = 'store_true',
84 short = 'Prevent StGit from modifying a branch', long = """
85 Prevent StGit from modifying a branch -- either the current
86 one, or one named on the command line."""),
87 opt('-u', '--unprotect', action = 'store_true',
88 short = 'Allow StGit to modify a branch', long = """
89 Allow StGit to modify a branch -- either the current one, or
90 one named on the command line. This undoes the effect of an
91 earlier 'stg branch --protect' command."""),
92 opt('--delete', action = 'store_true',
93 short = 'Delete a branch', long = """
94 Delete the named branch. If there are any patches left in the
95 branch, StGit will refuse to delete it unless you give the
96 '--force' flag.
97
98 A protected branch cannot be deleted; it must be unprotected
99 first (see '--unprotect' above).
100
101 If you delete the current branch, you are switched to the
102 "master" branch, if it exists."""),
103 opt('-d', '--description', short = 'Set the branch description'),
104 opt('--force', action = 'store_true',
105 short = 'Force a delete when the series is not empty')]
7b1ba1a6 106
117ed129 107directory = DirectoryGotoToplevel(log = False)
7b1ba1a6 108
fe4f6d58 109def __is_current_branch(branch_name):
d37ff079 110 return crt_series.get_name() == branch_name
7b1ba1a6 111
862ba51a 112def __print_branch(branch_name, length):
7b1ba1a6
CL
113 initialized = ' '
114 current = ' '
0b4b9499 115 protected = ' '
ebfd0156
CL
116
117 branch = stack.Series(branch_name)
118
2d00440c 119 if branch.is_initialised():
7b1ba1a6 120 initialized = 's'
fe4f6d58 121 if __is_current_branch(branch_name):
7b1ba1a6 122 current = '>'
ebfd0156 123 if branch.get_protected():
0b4b9499 124 protected = 'p'
27ac2b7e
KH
125 out.stdout(current + ' ' + initialized + protected + '\t'
126 + branch_name.ljust(length) + ' | ' + branch.get_description())
7b1ba1a6 127
fe4f6d58 128def __delete_branch(doomed_name, force = False):
ebfd0156
CL
129 doomed = stack.Series(doomed_name)
130
ff432158
CM
131 if __is_current_branch(doomed_name):
132 raise CmdException('Cannot delete the current branch')
ebfd0156 133 if doomed.get_protected():
0b4b9499
CL
134 raise CmdException, 'This branch is protected. Delete is not permitted'
135
27ac2b7e 136 out.start('Deleting branch "%s"' % doomed_name)
ebfd0156 137 doomed.delete(force)
27ac2b7e 138 out.done()
7b1ba1a6 139
7b1ba1a6
CL
140def func(parser, options, args):
141
142 if options.create:
143
144 if len(args) == 0 or len(args) > 2:
145 parser.error('incorrect number of arguments')
fe4f6d58
CM
146
147 check_local_changes()
148 check_conflicts()
6972fd6b 149 check_head_top_equal(crt_series)
fe4f6d58 150
7b1ba1a6 151 tree_id = None
4f5a8c72 152 if len(args) >= 2:
0d90f0ac 153 parentbranch = None
4f5a8c72 154 try:
0d90f0ac
YD
155 branchpoint = git.rev_parse(args[1])
156
8319e9ba
CM
157 # parent branch?
158 head_re = re.compile('refs/(heads|remotes)/')
159 ref_re = re.compile(args[1] + '$')
160 for ref in git.all_refs():
161 if head_re.match(ref) and ref_re.search(ref):
162 # args[1] is a valid ref from the branchpoint
163 # setting above
164 parentbranch = args[1]
165 break;
4f5a8c72 166 except git.GitException:
27ac2b7e
KH
167 # should use a more specific exception to catch only
168 # non-git refs ?
169 out.info('Don\'t know how to determine parent branch'
170 ' from "%s"' % args[1])
0d90f0ac
YD
171 # exception in branch = rev_parse() leaves branchpoint unbound
172 branchpoint = None
4f5a8c72 173
42cd003e 174 tree_id = git_id(crt_series, branchpoint or args[1])
0d90f0ac
YD
175
176 if parentbranch:
177 out.info('Recording "%s" as parent branch' % parentbranch)
178 else:
179 out.info('Don\'t know how to determine parent branch'
180 ' from "%s"' % args[1])
4f5a8c72
YD
181 else:
182 # branch stack off current branch
183 parentbranch = git.get_head_file()
184
185 if parentbranch:
186 parentremote = git.identify_remote(parentbranch)
187 if parentremote:
27ac2b7e
KH
188 out.info('Using remote "%s" to pull parent from'
189 % parentremote)
4f5a8c72 190 else:
27ac2b7e 191 out.info('Recording as a local branch')
4f5a8c72 192 else:
4646e7a3 193 # no known parent branch, can't guess the remote
4f5a8c72
YD
194 parentremote = None
195
196 stack.Series(args[0]).init(create_at = tree_id,
197 parent_remote = parentremote,
198 parent_branch = parentbranch)
7b1ba1a6 199
27ac2b7e 200 out.info('Branch "%s" created' % args[0])
117ed129 201 log.compat_log_entry('branch --create')
7b1ba1a6
CL
202 return
203
cc3db2b1
CL
204 elif options.clone:
205
206 if len(args) == 0:
d37ff079 207 clone = crt_series.get_name() + \
cc3db2b1
CL
208 time.strftime('-%C%y%m%d-%H%M%S')
209 elif len(args) == 1:
210 clone = args[0]
211 else:
212 parser.error('incorrect number of arguments')
213
214 check_local_changes()
215 check_conflicts()
6972fd6b 216 check_head_top_equal(crt_series)
cc3db2b1 217
27ac2b7e 218 out.start('Cloning current branch to "%s"' % clone)
cc3db2b1 219 crt_series.clone(clone)
27ac2b7e 220 out.done()
cc3db2b1 221
117ed129
KH
222 log.copy_log(log.default_repo(), crt_series.get_name(), clone,
223 'branch --clone')
cc3db2b1
CL
224 return
225
7b1ba1a6
CL
226 elif options.delete:
227
228 if len(args) != 1:
229 parser.error('incorrect number of arguments')
fe4f6d58 230 __delete_branch(args[0], options.force)
117ed129 231 log.delete_log(log.default_repo(), args[0])
7b1ba1a6
CL
232 return
233
234 elif options.list:
235
236 if len(args) != 0:
237 parser.error('incorrect number of arguments')
238
117ed129
KH
239 branches = set(git.get_heads())
240 for br in set(branches):
241 m = re.match(r'^(.*)\.stgit$', br)
242 if m and m.group(1) in branches:
243 branches.remove(br)
7b1ba1a6 244