Allow user to protect some branches against "stg pull"
[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
21import sys, os
22from optparse import OptionParser, make_option
23
24from stgit.commands.common import *
25from stgit.utils import *
26from stgit import stack, git
27
28
29help = 'manage development branches'
30usage = """%prog [options] branch-name [commit-id]
31
32Create, list, switch between, rename, or delete development branches
33within a git repository. By default, a single branch called 'master'
34is always created in a new repository. This subcommand allows you to
35manage several patch series in the same repository.
36
37When displaying the branches, the names can be prefixed with
38's' (StGIT managed) or 'p' (protected)."""
39
40options = [make_option('-c', '--create',
41 help = 'create a new development branch',
42 action = 'store_true'),
43 make_option('--delete',
44 help = 'delete an existing development branch',
45 action = 'store_true'),
46 make_option('--force',
47 help = 'force a delete when the series is not empty',
48 action = 'store_true'),
49 make_option('-l', '--list',
50 help = 'list branches contained in this repository',
51 action = 'store_true'),
0b4b9499
CL
52 make_option('-p', '--protect',
53 help = 'prevent "stg pull" from modifying this branch',
54 action = 'store_true'),
7b1ba1a6
CL
55 make_option('-r', '--rename',
56 help = 'rename an existing development branch',
0b4b9499
CL
57 action = 'store_true'),
58 make_option('-u', '--unprotect',
59 help = 'allow "stg pull" to modify this branch',
7b1ba1a6
CL
60 action = 'store_true')]
61
62
63def is_current_branch(branch_name):
64 return git.get_head_file() == branch_name
65
66def print_branch(branch_name):
67 initialized = ' '
68 current = ' '
0b4b9499 69 protected = ' '
7b1ba1a6
CL
70 if os.path.isdir(os.path.join(git.base_dir, 'patches', branch_name)):
71 initialized = 's'
72 if is_current_branch(branch_name):
73 current = '>'
0b4b9499
CL
74 if stack.Series(branch_name).get_protected():
75 protected = 'p'
76 print '%s %s%s\t%s' % (current, initialized, protected, branch_name)
7b1ba1a6
CL
77
78def delete_branch(doomed_name, force = False):
0b4b9499
CL
79 if stack.Series(doomed_name).get_protected():
80 raise CmdException, 'This branch is protected. Delete is not permitted'
81
7b1ba1a6
CL
82 if is_current_branch(doomed_name) and doomed_name != 'master':
83 git.switch_branch('master')
84
85 stack.Series(doomed_name).delete(force)
86
87 if doomed_name != 'master':
88 git.delete_branch(doomed_name)
89
90 print 'Branch "%s" has been deleted.' % doomed_name
91
92def rename_branch(from_name, to_name):
93 if from_name == 'master':
94 raise CmdException, 'Renaming the master branch is not allowed'
95
96 to_patchdir = os.path.join(git.base_dir, 'patches', to_name)
97 if os.path.isdir(to_patchdir):
98 raise CmdException, '"%s" already exists' % to_patchdir
99 to_base = os.path.join(git.base_dir, 'refs', 'bases', to_name)
100 if os.path.isfile(to_base):
101 raise CmdException, '"%s" already exists' % to_base
102
103 git.rename_branch(from_name, to_name)
104
105 from_patchdir = os.path.join(git.base_dir, 'patches', from_name)
106 if os.path.isdir(from_patchdir):
107 os.rename(from_patchdir, to_patchdir)
108 from_base = os.path.join(git.base_dir, 'refs', 'bases', from_name)
109 if os.path.isfile(from_base):
110 os.rename(from_base, to_base)
111
112 print 'Renamed branch "%s" as "%s".' % (from_name, to_name)
113
114def func(parser, options, args):
115
116 if options.create:
117
118 if len(args) == 0 or len(args) > 2:
119 parser.error('incorrect number of arguments')
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.delete:
131
132 if len(args) != 1:
133 parser.error('incorrect number of arguments')
134 delete_branch(args[0], options.force)
135 return
136
137 elif options.list:
138
139 if len(args) != 0:
140 parser.error('incorrect number of arguments')
141
142 branches = os.listdir(os.path.join(git.base_dir, 'refs', 'heads'))
143 branches.sort()
144
145 print 'Available branches:'
146 for i in branches:
147 print_branch(i)
148 return
149
0b4b9499
CL
150 elif options.protect:
151
152 if len(args) == 0:
153 branch = git.get_head_file()
154 elif len(args) == 1:
155 branch = args[0]
156 else:
157 parser.error('incorrect number of arguments')
158
159 base = os.path.join(git.base_dir, 'refs', 'bases', branch)
160 if not os.path.isfile(base):
161 raise CmdException, 'Branch "%s" is not controlled by StGit' % branch
162
163 print 'Protecting branch "%s"...' % branch
164 stack.Series(branch).protect()
165 return
166
7b1ba1a6
CL
167 elif options.rename:
168
169 if len(args) != 2:
170 parser.error('incorrect number of arguments')
171 rename_branch(args[0], args[1])
172 return
173
0b4b9499
CL
174 elif options.unprotect:
175
176 if len(args) == 0:
177 branch = git.get_head_file()
178 elif len(args) == 1:
179 branch = args[0]
180 else:
181 parser.error('incorrect number of arguments')
182
183 base = os.path.join(git.base_dir, 'refs', 'bases', branch)
184 if not os.path.isfile(base):
185 raise CmdException, 'Branch "%s" is not controlled by StGit' % branch
186
187 print 'Unprotecting branch "%s"...' % branch
188 stack.Series(branch).unprotect()
189 return
190
7b1ba1a6
CL
191 elif len(args) == 1:
192
193 print 'Switching to branch "%s"...' % args[0],
194 sys.stdout.flush()
195
196 git.switch_branch(args[0])
197
198 print 'done'
199 return
200
201 # default action: print the current branch
202 if len(args) != 0:
203 parser.error('incorrect number of arguments')
204
205 print git.get_head_file()