chiark / gitweb /
Allow user to protect some branches against "stg pull"
[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' % (current, initialized, protected, branch_name)
77
78 def delete_branch(doomed_name, force = False):
79     if stack.Series(doomed_name).get_protected():
80         raise CmdException, 'This branch is protected. Delete is not permitted'
81
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
92 def 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
114 def 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
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
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
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
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()