source: valtobtest/subversion-1.6.2/subversion/bindings/ctypes-python/csvn/txn.py @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

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