ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/src/vendor/apache/subversion/1.8.17/subversion/bindings/ctypes-python/csvn/txn.py
Revision: 9598
Committed: Sun Oct 1 19:41:23 2017 UTC (6 years, 7 months ago) by laffer1
Content type: text/x-python
File size: 17910 byte(s)
Log Message:
subversion 1.8.17

File Contents

# Content
1 # Licensed to the Apache Software Foundation (ASF) under one
2 # or more contributor license agreements. See the NOTICE file
3 # distributed with this work for additional information
4 # regarding copyright ownership. The ASF licenses this file
5 # to you under the Apache License, Version 2.0 (the
6 # "License"); you may not use this file except in compliance
7 # with the License. You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing,
12 # software distributed under the License is distributed on an
13 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 # KIND, either express or implied. See the License for the
15 # specific language governing permissions and limitations
16 # under the License.
17
18
19 import csvn.core as svn
20 from csvn.core import *
21 import os
22
23 class Txn(object):
24 def __init__(self, session):
25 self.pool = Pool()
26 self.iterpool = Pool()
27 self.session = session
28 self.root = _txn_operation(None, "OPEN", svn_node_dir)
29 self.commit_callback = None
30 self.ignore_func = None
31 self.autoprop_func = None
32
33 def ignore(self, ignore_func):
34 """Setup a callback function which decides whether a
35 new directory or path should be added to the repository.
36
37 IGNORE_FUNC must be a function which accepts two arguments:
38 (path, kind)
39
40 PATH is the path which is about to be added to the repository.
41 KIND is either svn_node_file or svn_node_dir, depending
42 on whether the proposed path is a file or a directory.
43
44 If IGNORE_FUNC returns True, the path will be ignored. Otherwise,
45 the path will be added.
46
47 Note that IGNORE_FUNC is only called when new files or
48 directories are added to the repository. It is not called,
49 for example, when directories within the repository are moved
50 or copied, since these copies are not new."""
51
52 self.ignore_func = ignore_func
53
54 def autoprop(self, autoprop_func):
55 """Setup a callback function which automatically sets up
56 properties on new files or directories added to the
57 repository.
58
59 AUTOPROP_FUNC must be a function which accepts three
60 arguments: (txn, path, kind)
61
62 TXN is this transaction object.
63 PATH is the path which was just added to the repository.
64 KIND is either svn_node_file or svn_node_dir, depending
65 on whether the newly added path is a file or a directory.
66
67 If AUTOPROP_FUNC wants to set properties on PATH, it should
68 call TXN.propset with the appropriate arguments.
69
70 Note that AUTOPROP_FUNC is only called when new files or
71 directories are added to the repository. It is not called,
72 for example, when directories within the repository are moved
73 or copied, since these copies are not new."""
74
75 self.autoprop_func = autoprop_func
76
77 def check_path(self, path, rev=None):
78 """Check the status of PATH@REV. If PATH or any of its
79 parents have been modified in this transaction, take this
80 into consideration."""
81 path = self.session._relative_path(path)
82 return self._check_path(path, rev)[0]
83
84 def delete(self, path, base_rev=None):
85 """Delete PATH from the repository as of base_rev"""
86
87 path = self.session._relative_path(path)
88
89 kind, parent = self._check_path(path, base_rev)
90
91 if kind == svn_node_none:
92 if base_rev:
93 message = "'%s' not found in r%d" % (path, base_rev)
94 else:
95 message = "'%s' not found" % (path)
96 raise SubversionException(SVN_ERR_BAD_URL, message)
97
98 parent.open(path, "DELETE", kind)
99
100 def mkdir(self, path):
101 """Create a directory at PATH."""
102
103 path = self.session._relative_path(path)
104
105 if self.ignore_func and self.ignore_func(path, svn_node_dir):
106 return
107
108 kind, parent = self._check_path(path)
109
110 if kind != svn_node_none:
111 if kind == svn_node_dir:
112 message = ("Can't create directory '%s': "
113 "Directory already exists" % path)
114 else:
115 message = ("Can't create directory '%s': "
116 "Path obstructed by file" % path)
117 raise SubversionException(SVN_ERR_BAD_URL, message)
118
119 parent.open(path, "ADD", svn_node_dir)
120
121 # Trigger autoprop_func on new directory adds
122 if self.autoprop_func:
123 self.autoprop_func(self, path, svn_node_dir)
124
125 def propset(self, path, key, value):
126 """Set the property named KEY to VALUE on the specified PATH"""
127
128 path = self.session._relative_path(path)
129
130 kind, parent = self._check_path(path)
131
132 if kind == svn_node_none:
133 message = ("Can't set property on '%s': "
134 "No such file or directory" % path)
135 raise SubversionException(SVN_ERR_BAD_URL, message)
136
137 node = parent.open(path, "OPEN", kind)
138 node.propset(key, value)
139
140 def propdel(self, path, key):
141 """Delete the property named KEY on the specified PATH"""
142
143 path = self.session._relative_path(path)
144
145 kind, parent = self._check_path(path)
146
147 if kind == svn_node_none:
148 message = ("Can't delete property on '%s': "
149 "No such file or directory" % path)
150 raise SubversionException(SVN_ERR_BAD_URL, message)
151
152 node = parent.open(path, "OPEN", kind)
153 node.propdel(key)
154
155
156 def copy(self, src_path, dest_path, src_rev=None, local_path=None):
157 """Copy a file or directory from SRC_PATH@SRC_REV to DEST_PATH.
158 If SRC_REV is not supplied, use the latest revision of SRC_PATH.
159 If LOCAL_PATH is supplied, update the new copy to match
160 LOCAL_PATH."""
161
162 src_path = self.session._relative_path(src_path)
163 dest_path = self.session._relative_path(dest_path)
164
165 if not src_rev:
166 src_rev = self.session.latest_revnum()
167
168 kind = self.session.check_path(src_path, src_rev, encoded=False)
169 _, parent = self._check_path(dest_path)
170
171 if kind == svn_node_none:
172 message = ("Can't copy '%s': "
173 "No such file or directory" % src_path)
174 raise SubversionException(SVN_ERR_BAD_URL, message)
175
176 if kind == svn_node_file or local_path is None:
177 # Mark the file or directory as copied
178 parent.open(dest_path, "ADD",
179 kind, copyfrom_path=src_path,
180 copyfrom_rev=src_rev,
181 local_path=local_path)
182 else:
183 # Mark the directory as copied
184 parent.open(dest_path, "ADD",
185 kind, copyfrom_path=src_path,
186 copyfrom_rev=src_rev)
187
188 # Upload any changes from the supplied local path
189 # to the remote repository
190 self.upload(dest_path, local_path)
191
192 def upload(self, remote_path, local_path):
193 """Upload a local file or directory into the remote repository.
194 If the given file or directory already exists in the
195 repository, overwrite it.
196
197 This function does not add or update ignored files or
198 directories."""
199
200 remote_path = self.session._relative_path(remote_path)
201
202 kind = svn_node_none
203 if os.path.isdir(local_path):
204 kind = svn_node_dir
205 elif os.path.exists(local_path):
206 kind = svn_node_file
207
208 # Don't add ignored files or directories
209 if self.ignore_func and self.ignore_func(remote_path, kind):
210 return
211
212 if (os.path.isdir(local_path) and
213 self.check_path(remote_path) != svn_node_dir):
214 self.mkdir(remote_path)
215 elif not os.path.isdir(local_path) and os.path.exists(local_path):
216 self._upload_file(remote_path, local_path)
217
218 ignores = []
219
220 for root, dirs, files in os.walk(local_path):
221
222 # Convert the local root into a remote root
223 remote_root = root.replace(local_path.rstrip(os.path.sep),
224 remote_path.rstrip("/"))
225 remote_root = remote_root.replace(os.path.sep, "/").rstrip("/")
226
227 # Don't process ignored subdirectories
228 if (self.ignore_func and self.ignore_func(root, svn_node_dir)
229 or root in ignores):
230
231 # Ignore children too
232 for name in dirs:
233 ignores.append("%s/%s" % (remote_root, name))
234
235 # Skip to the next tuple
236 continue
237
238 # Add all subdirectories
239 for name in dirs:
240 remote_dir = "%s/%s" % (remote_root, name)
241 self.mkdir(remote_dir)
242
243 # Add all files in this directory
244 for name in files:
245 remote_file = "%s/%s" % (remote_root, name)
246 local_file = os.path.join(root, name)
247 self._upload_file(remote_file, local_file)
248
249 def _txn_commit_callback(self, info, baton, pool):
250 self._txn_committed(info[0])
251
252 def commit(self, message, base_rev = None):
253 """Commit all changes to the remote repository"""
254
255 if base_rev is None:
256 base_rev = self.session.latest_revnum()
257
258 commit_baton = c_void_p()
259
260 self.commit_callback = svn_commit_callback2_t(self._txn_commit_callback)
261 (editor, editor_baton) = self.session._get_commit_editor(message,
262 self.commit_callback, commit_baton, self.pool)
263
264 child_baton = c_void_p()
265 try:
266 self.root.replay(editor[0], self.session, base_rev, editor_baton)
267 except SubversionException:
268 try:
269 SVN_ERR(editor[0].abort_edit(editor_baton, self.pool))
270 except SubversionException:
271 pass
272 raise
273
274 return self.committed_rev
275
276 # This private function handles commits and saves
277 # information about them in this object
278 def _txn_committed(self, info):
279 self.committed_rev = info.revision
280 self.committed_date = info.date
281 self.committed_author = info.author
282 self.post_commit_err = info.post_commit_err
283
284 # This private function uploads a single file to the
285 # remote repository. Don't use this function directly.
286 # Use 'upload' instead.
287 def _upload_file(self, remote_path, local_path):
288
289 if self.ignore_func and self.ignore_func(remote_path, svn_node_file):
290 return
291
292 kind, parent = self._check_path(remote_path)
293 if svn_node_none == kind:
294 mode = "ADD"
295 else:
296 mode = "OPEN"
297
298 parent.open(remote_path, mode, svn_node_file,
299 local_path=local_path)
300
301 # Trigger autoprop_func on new file adds
302 if mode == "ADD" and self.autoprop_func:
303 self.autoprop_func(self, remote_path, svn_node_file)
304
305 # Calculate the kind of the specified file, and open a handle
306 # to its parent operation.
307 def _check_path(self, path, rev=None):
308 path_components = path.split("/")
309 parent = self.root
310 copyfrom_path = None
311 total_path = path_components[0]
312 for path_component in path_components[1:]:
313 parent = parent.open(total_path, "OPEN")
314 if parent.copyfrom_path:
315 copyfrom_path = parent.copyfrom_path
316 rev = parent.copyfrom_rev
317
318 total_path = "%s/%s" % (total_path, path_component)
319 if copyfrom_path:
320 copyfrom_path = "%s/%s" % (copyfrom_path, path_component)
321
322 if path in parent.ops:
323 node = parent.open(path)
324 if node.action == "DELETE":
325 kind = svn_node_none
326 else:
327 kind = node.kind
328 else:
329 kind = self.session.check_path(copyfrom_path or total_path, rev,
330 encoded=False)
331
332 return (kind, parent)
333
334
335
336 class _txn_operation(object):
337 def __init__(self, path, action, kind, copyfrom_path = None,
338 copyfrom_rev = -1, local_path = None):
339 self.path = path
340 self.action = action
341 self.kind = kind
342 self.copyfrom_path = copyfrom_path
343 self.copyfrom_rev = copyfrom_rev
344 self.local_path = local_path
345 self.ops = {}
346 self.properties = {}
347
348 def propset(self, key, value):
349 """Set the property named KEY to VALUE on this file/dir"""
350 self.properties[key] = value
351
352 def propdel(self, key):
353 """Delete the property named KEY on this file/dir"""
354 self.properties[key] = None
355
356 def open(self, path, action="OPEN", kind=svn_node_dir,
357 copyfrom_path = None, copyfrom_rev = -1, local_path = None):
358 if path in self.ops:
359 op = self.ops[path]
360 if action == "OPEN" and op.kind in (svn_node_dir, svn_node_file):
361 return op
362 elif action == "ADD" and op.action == "DELETE":
363 op.action = "REPLACE"
364 op.local_path = local_path
365 op.copyfrom_path = copyfrom_path
366 op.copyfrom_rev = copyfrom_rev
367 op.kind = kind
368 return op
369 elif (action == "DELETE" and op.action == "OPEN" and
370 kind == svn_node_dir):
371 op.action = action
372 return op
373 else:
374 # throw error
375 pass
376 else:
377 self.ops[path] = _txn_operation(path, action, kind,
378 copyfrom_path = copyfrom_path,
379 copyfrom_rev = copyfrom_rev,
380 local_path = local_path)
381 return self.ops[path]
382
383 def replay(self, editor, session, base_rev, baton):
384 subpool = Pool()
385 child_baton = c_void_p()
386 file_baton = c_void_p()
387 if self.path is None:
388 SVN_ERR(editor.open_root(baton, svn_revnum_t(base_rev), subpool,
389 byref(child_baton)))
390 else:
391 if self.action == "DELETE" or self.action == "REPLACE":
392 SVN_ERR(editor.delete_entry(self.path, base_rev, baton,
393 subpool))
394 elif self.action == "OPEN":
395 if self.kind == svn_node_dir:
396 SVN_ERR(editor.open_directory(self.path, baton,
397 svn_revnum_t(base_rev), subpool,
398 byref(child_baton)))
399 else:
400 SVN_ERR(editor.open_file(self.path, baton,
401 svn_revnum_t(base_rev), subpool,
402 byref(file_baton)))
403
404 if self.action in ("ADD", "REPLACE"):
405 copyfrom_path = None
406 if self.copyfrom_path is not None:
407 copyfrom_path = session._abs_copyfrom_path(
408 self.copyfrom_path)
409 if self.kind == svn_node_dir:
410 SVN_ERR(editor.add_directory(
411 self.path, baton, copyfrom_path,
412 svn_revnum_t(self.copyfrom_rev), subpool,
413 byref(child_baton)))
414 else:
415 SVN_ERR(editor.add_file(self.path, baton,
416 copyfrom_path, svn_revnum_t(self.copyfrom_rev),
417 subpool, byref(file_baton)))
418
419 # Write out changes to properties
420 for (name, value) in self.properties.items():
421 if value is None:
422 svn_value = POINTER(svn_string_t)()
423 else:
424 svn_value = svn_string_ncreate(value, len(value),
425 subpool)
426 if file_baton:
427 SVN_ERR(editor.change_file_prop(file_baton, name,
428 svn_value, subpool))
429 elif child_baton:
430 SVN_ERR(editor.change_dir_prop(child_baton, name,
431 svn_value, subpool))
432
433 # If there's a source file, and we opened a file to write,
434 # write out the contents
435 if self.local_path and file_baton:
436 handler = svn_txdelta_window_handler_t()
437 handler_baton = c_void_p()
438 f = POINTER(apr_file_t)()
439 SVN_ERR(editor.apply_textdelta(file_baton, NULL, subpool,
440 byref(handler), byref(handler_baton)))
441
442 svn_io_file_open(byref(f), self.local_path, APR_READ,
443 APR_OS_DEFAULT, subpool)
444 contents = svn_stream_from_aprfile(f, subpool)
445 svn_txdelta_send_stream(contents, handler, handler_baton,
446 NULL, subpool)
447 svn_io_file_close(f, subpool)
448
449 # If we opened a file, we need to close it
450 if file_baton:
451 SVN_ERR(editor.close_file(file_baton, NULL, subpool))
452
453 if self.kind == svn_node_dir and self.action != "DELETE":
454 assert(child_baton)
455
456 # Look at the children
457 for op in self.ops.values():
458 op.replay(editor, session, base_rev, child_baton)
459
460 if self.path:
461 # Close the directory
462 SVN_ERR(editor.close_directory(child_baton, subpool))
463 else:
464 # Close the editor
465 SVN_ERR(editor.close_edit(baton, subpool))
466