Mercurial > hgsubversion
annotate tests/fixtures/rsvn.py @ 1006:7a3b938825cd
layouts: refactor layout loading and persisting out of svnmeta.py
author | David Schleimer <dschleimer@fb.com> |
---|---|
date | Wed, 17 Apr 2013 16:14:15 -0700 |
parents | e9af7eba88db |
children |
rev | line source |
---|---|
581 | 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) | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
172 |
581 | 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 | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
184 |
581 | 185 if log_msg == None: |
186 usage('Missing --message argument') | |
187 sys.exit(1) | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
188 |
581 | 189 if len(args) != 1: |
190 usage('Missing repository path argument') | |
191 sys.exit(1) | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
192 |
581 | 193 repos_path = args[0] |
194 print 'Accessing repository at [%s]' % repos_path | |
195 | |
196 repository = Repository(repos_path, pool) | |
197 sub = repository.subpool() | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
198 |
581 | 199 try: |
200 txn = repository.begin(username, log_msg) | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
201 |
581 | 202 # Read commands from STDIN |
203 lineno = 0 | |
204 for line in sys.stdin: | |
205 lineno += 1 | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
206 |
581 | 207 core.svn_pool_clear(sub) |
208 try: | |
209 if COMMENT_RE.search(line): | |
210 continue | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
211 |
581 | 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 | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
218 |
581 | 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 | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
225 |
581 | 226 match = RMKDIR_RE.search(line) |
227 if match: | |
228 entry = match.group(1) | |
229 txn.mkdir(entry, sub) | |
230 continue | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
231 |
581 | 232 match = RDELETE_RE.search(line) |
233 if match: | |
234 entry = match.group(1) | |
235 txn.delete(entry, sub) | |
236 continue | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
237 |
581 | 238 raise NameError, ('Unknown command [%s] on line %d' % |
239 (line, lineno)) | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
240 |
581 | 241 except: |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
242 sys.stderr.write(('Exception occured while processing line %d:\n' % |
581 | 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) | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
249 |
581 | 250 new_rev = txn.commit() |
251 print '\nCommitted revision %d.' % new_rev | |
832
e9af7eba88db
globally: clean up whitespace around operators and commas to conform with PEP8
Yonggang Luo <luoyonggang@gmail.com>
parents:
581
diff
changeset
|
252 |
581 | 253 finally: |
254 print '\nRepository closed.' | |
255 | |
256 def main(): | |
257 core.run_app(rsvn) | |
258 | |
259 if __name__ == '__main__': | |
260 main() |