Add support for branch description files
[stgit] / stgit / commands / branch.py
1 """Branch command
2 """
3
4 __copyright__ = """
5 Copyright (C) 2005, Chuck Lever <cel@netapp.com>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 """
20
21 import sys, os
22 from optparse import OptionParser, make_option
23
24 from stgit.commands.common import *
25 from stgit.utils import *
26 from stgit import stack, git
27
28
29 help = 'manage development branches'
30 usage = """%prog [options] branch-name [commit-id]
31
32 Create, list, switch between, rename, or delete development branches
33 within a git repository. By default, a single branch called 'master'
34 is always created in a new repository. This subcommand allows you to
35 manage several patch series in the same repository.
36
37 When displaying the branches, the names can be prefixed with
38 's' (StGIT managed) or 'p' (protected)."""
39
40 options = [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'),
52 make_option('-p', '--protect',
53 help = 'prevent "stg pull" from modifying this branch',
54 action = 'store_true'),
55 make_option('-r', '--rename',
56 help = 'rename an existing development branch',
57 action = 'store_true'),
58 make_option('-u', '--unprotect',
59 help = 'allow "stg pull" to modify this branch',
60 action = 'store_true')]
61
62
63 def is_current_branch(branch_name):
64 return git.get_head_file() == branch_name
65
66 def print_branch(branch_name):
67 initialized = ' '
68 current = ' '
69 protected = ' '
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 = '>'
74 if stack.Series(branch_name).get_protected():
75 protected = 'p'
76 print '%s %s%s\t%s\t%s' % (current, initialized, protected, branch_name, \
77 stack.Series(branch_name).get_description())
78
79 def delete_branch(doomed_name, force = False):
80 if stack.Series(doomed_name).get_protected():
81 raise CmdException, 'This branch is protected. Delete is not permitted'
82
83 if is_current_branch(doomed_name) and doomed_name != 'master':
84 git.switch_branch('master')
85
86 stack.Series(doomed_name).delete(force)
87
88 if doomed_name != 'master':
89 git.delete_branch(doomed_name)
90
91 print 'Branch "%s" has been deleted.' % doomed_name
92
93 def rename_branch(from_name, to_name):
94 if from_name == 'master':
95 raise CmdException, 'Renaming the master branch is not allowed'
96
97 to_patchdir = os.path.join(git.base_dir, 'patches', to_name)
98 if os.path.isdir(to_patchdir):
99 raise CmdException, '"%s" already exists' % to_patchdir
100 to_base = os.path.join(git.base_dir, 'refs', 'bases', to_name)
101 if os.path.isfile(to_base):
102 raise CmdException, '"%s" already exists' % to_base
103
104 git.rename_branch(from_name, to_name)
105
106 from_patchdir = os.path.join(git.base_dir, 'patches', from_name)
107 if os.path.isdir(from_patchdir):
108 os.rename(from_patchdir, to_patchdir)
109 from_base = os.path.join(git.base_dir, 'refs', 'bases', from_name)
110 if os.path.isfile(from_base):
111 os.rename(from_base, to_base)
112
113 print 'Renamed branch "%s" as "%s".' % (from_name, to_name)
114
115 def func(parser, options, args):
116
117 if options.create:
118
119 if len(args) == 0 or len(args) > 2:
120 parser.error('incorrect number of arguments')
121 tree_id = None
122 if len(args) == 2:
123 tree_id = args[1]
124
125 git.create_branch(args[0], tree_id)
126 stack.Series(args[0]).init()
127
128 print 'Branch "%s" created.' % args[0]
129 return
130
131 elif options.delete:
132
133 if len(args) != 1:
134 parser.error('incorrect number of arguments')
135 delete_branch(args[0], options.force)
136 return
137
138 elif options.list:
139
140 if len(args) != 0:
141 parser.error('incorrect number of arguments')
142
143 branches = os.listdir(os.path.join(git.base_dir, 'refs', 'heads'))
144 branches.sort()
145
146 print 'Available branches:'
147 for i in branches:
148 print_branch(i)
149 return
150
151 elif options.protect:
152
153 if len(args) == 0:
154 branch = git.get_head_file()
155 elif len(args) == 1:
156 branch = args[0]
157 else:
158 parser.error('incorrect number of arguments')
159
160 base = os.path.join(git.base_dir, 'refs', 'bases', branch)
161 if not os.path.isfile(base):
162 raise CmdException, 'Branch "%s" is not controlled by StGit' % branch
163
164 print 'Protecting branch "%s"...' % branch
165 stack.Series(branch).protect()
166 return
167
168 elif options.rename:
169
170 if len(args) != 2:
171 parser.error('incorrect number of arguments')
172 rename_branch(args[0], args[1])
173 return
174
175 elif options.unprotect:
176
177 if len(args) == 0:
178 branch = git.get_head_file()
179 elif len(args) == 1:
180 branch = args[0]
181 else:
182 parser.error('incorrect number of arguments')
183
184 base = os.path.join(git.base_dir, 'refs', 'bases', branch)
185 if not os.path.isfile(base):
186 raise CmdException, 'Branch "%s" is not controlled by StGit' % branch
187
188 print 'Unprotecting branch "%s"...' % branch
189 stack.Series(branch).unprotect()
190 return
191
192 elif len(args) == 1:
193
194 print 'Switching to branch "%s"...' % args[0],
195 sys.stdout.flush()
196
197 git.switch_branch(args[0])
198
199 print 'done'
200 return
201
202 # default action: print the current branch
203 if len(args) != 0:
204 parser.error('incorrect number of arguments')
205
206 print git.get_head_file()