comparison tests/fixtures/rsvn.py @ 581:90efea2c19df

Add rsvn.py to test tools
author Patrick Mezard <pmezard@gmail.com>
date Tue, 02 Mar 2010 17:06:06 +0100
parents
children e9af7eba88db
comparison
equal deleted inserted replaced
580:35529f736fa2 581:90efea2c19df
1 #!/usr/bin/env python2
2
3 # LICENSE
4 #
5 # Copyright (c) 2004, Francois Beausoleil
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 #
12 # * Redistributions of source code must retain the above copyright
13 # notice, this list of conditions and the following disclaimer.
14 # * Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in
16 # the documentation and/or other materials provided with the
17 # distribution.
18 # * Neither the name of the Francois Beausoleil nor the names of its
19 # contributors may be used to endorse or promote products derived
20 # from this software without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34 import getopt
35 import sys
36 import os
37 import re
38 import traceback
39 from svn import core, repos, fs
40
41 VERSION='$Id: rsvn.py 20 2005-09-23 15:08:08Z fbos $'
42 RCOPY_RE = re.compile('^\s*rcopy\s+(.+)\s+(.+)$')
43 RMOVE_RE = re.compile('^\s*rmove\s+(.+)\s+(.+)$')
44 RMKDIR_RE = re.compile('^\s*rmkdir\s+(.+)$')
45 RDELETE_RE = re.compile('^\s*rdelete\s+(.+)$')
46 COMMENT_RE = re.compile('(?:^\s*#)|(?:^\s*$)')
47
48 def usage(error=None):
49 if error:
50 print 'Error: %s\n' % error
51 print 'USAGE: %s --message=MESSAGE repos_path [--username=USERNAME]' % (
52 os.path.basename(sys.argv[0]))
53 print ''
54 print ' --help, -h print this usage message and exit with success'
55 print ' --version print the version number'
56 print ' --username=USERNAME Username to execute the commands under'
57 print ' --message=LOG_MSG Log message to execute the commit with'
58 print ''
59 print 'Reads STDIN and parses the following commands, to execute them on the server, '
60 print 'all within the same transaction:'
61 print ''
62 print ' rcopy SRC DEST Copy the HEAD revision of a file or folder'
63 print ' rmove SRC DEST Copy + delete the HEAD revision of a file or folder'
64 print ' rdelete TARGET Deletes something from the repository'
65 print ' rmkdir TARGET Creates a new folder (must create parents first)'
66 print ' # Initiates a comment'
67 print ''
68 # 12345678901234567890123456789012345678901234567890123456789012345678901234567890
69
70
71 class Transaction:
72 """Represents a transaction in a Subversion repository
73
74 Transactions are long-lived objects which exist in the repository,
75 and are used to build an intermediate representation of a new
76 revision. Once the transaction is committed, the repository
77 bumps the revision number, and links the new transaction in the
78 Subversion filesystem."""
79
80 def __init__(self, repository, rev, username, message, pool, logger=None):
81 if logger:
82 self.logger = logger
83 else:
84 self.logger = sys.stdout
85 self.pool = pool
86 self.rev = rev
87
88 self.fsptr = repos.svn_repos_fs(repository)
89 self.rev_root = fs.revision_root(self.fsptr, self.rev,
90 self.pool)
91 self.txnp = repos.svn_repos_fs_begin_txn_for_commit(
92 repository, self.rev, username, message, self.pool)
93 self.txn_root = fs.txn_root(self.txnp, self.pool)
94 self.log('Base revision %d\n' % rev)
95
96 def commit(self):
97 values = fs.commit_txn(self.txnp, self.pool)
98 return values[1]
99
100 def rollback(self):
101 fs.abort_txn(self.txnp, self.pool)
102
103 def copy(self, src, dest, subpool):
104 self.log('A + %s\n' % dest)
105 fs.copy(self.rev_root, src, self.txn_root, dest, subpool)
106
107 def delete(self, entry, subpool):
108 self.log('D %s\n' % entry)
109 fs.delete(self.txn_root, entry, subpool)
110
111 def mkdir(self, entry, subpool):
112 self.log('A %s\n' % entry)
113 fs.make_dir(self.txn_root, entry, subpool)
114
115 def move(self, src, dest, subpool):
116 self.copy(src, dest, subpool)
117 self.delete(src, subpool)
118
119 def log(self, msg):
120 self.logger.write(msg)
121
122
123 class Repository:
124 """Represents a Subversion repository, and allows common operations
125 on it."""
126
127 def __init__(self, repos_path, pool, logger=None):
128 if logger:
129 self.logger = logger
130 else:
131 self.logger = sys.stdout
132 self.pool = pool
133 assert self.pool
134
135 self.repo = repos.svn_repos_open(repos_path, self.pool)
136 self.fsptr = repos.svn_repos_fs(self.repo)
137
138 def get_youngest(self):
139 """Returns the youngest revision in the repository."""
140 return fs.youngest_rev(self.fsptr, self.pool)
141
142 def begin(self, username, log_msg):
143 """Initiate a new Transaction"""
144 return Transaction(self.repo, self.get_youngest(), username,
145 log_msg, self.pool, self.logger)
146
147 def close(self):
148 """Close the repository, aborting any uncommitted transactions"""
149 core.svn_pool_destroy(self.pool)
150 core.apr_terminate()
151
152 def subpool(self):
153 """Instantiates a new pool from the master pool"""
154 return core.svn_pool_create(self.pool)
155
156 def delete_pool(self, pool):
157 """Deletes the passed-in pool. Returns None, to assign to pool in
158 caller."""
159 core.svn_pool_destroy(pool)
160 return None
161
162 def rsvn(pool):
163 log_msg = None
164
165 try:
166 opts, args = getopt.getopt(sys.argv[1:], 'vh',
167 ["help", "username=", "message=", "version"])
168 except getopt.GetoptError, e:
169 sys.stderr.write(str(e) + '\n\n')
170 usage()
171 sys.exit(1)
172
173 for opt, value in opts:
174 if opt == '--version':
175 print '%s version %s' % (os.path.basename(sys.argv[0]), VERSION)
176 sys.exit(0)
177 elif opt == '--help' or opt == '-h':
178 usage()
179 sys.exit(0)
180 elif opt == '--username':
181 username = value
182 elif opt == '--message':
183 log_msg = value
184
185 if log_msg == None:
186 usage('Missing --message argument')
187 sys.exit(1)
188
189 if len(args) != 1:
190 usage('Missing repository path argument')
191 sys.exit(1)
192
193 repos_path = args[0]
194 print 'Accessing repository at [%s]' % repos_path
195
196 repository = Repository(repos_path, pool)
197 sub = repository.subpool()
198
199 try:
200 txn = repository.begin(username, log_msg)
201
202 # Read commands from STDIN
203 lineno = 0
204 for line in sys.stdin:
205 lineno += 1
206
207 core.svn_pool_clear(sub)
208 try:
209 if COMMENT_RE.search(line):
210 continue
211
212 match = RCOPY_RE.search(line)
213 if match:
214 src = match.group(1)
215 dest = match.group(2)
216 txn.copy(src, dest, sub)
217 continue
218
219 match = RMOVE_RE.search(line)
220 if match:
221 src = match.group(1)
222 dest = match.group(2)
223 txn.move(src, dest, sub)
224 continue
225
226 match = RMKDIR_RE.search(line)
227 if match:
228 entry = match.group(1)
229 txn.mkdir(entry, sub)
230 continue
231
232 match = RDELETE_RE.search(line)
233 if match:
234 entry = match.group(1)
235 txn.delete(entry, sub)
236 continue
237
238 raise NameError, ('Unknown command [%s] on line %d' %
239 (line, lineno))
240
241 except:
242 sys.stderr.write(('Exception occured while processing line %d:\n' %
243 lineno))
244 etype, value, tb = sys.exc_info()
245 traceback.print_exception(etype, value, tb, None, sys.stderr)
246 sys.stderr.write('\n')
247 txn.rollback()
248 sys.exit(1)
249
250 new_rev = txn.commit()
251 print '\nCommitted revision %d.' % new_rev
252
253 finally:
254 print '\nRepository closed.'
255
256 def main():
257 core.run_app(rsvn)
258
259 if __name__ == '__main__':
260 main()