def get_changes(self): root = fs.revision_root(self.fs_ptr, self.rev) editor = repos.RevisionChangeCollector(self.fs_ptr, self.rev) e_ptr, e_baton = delta.make_editor(editor) repos.svn_repos_replay(root, e_ptr, e_baton) idx = 0 # Variables to record copy/deletes for later move detection copies, deletions = {}, {} changes = [] for path, change in core._as_list(editor.changes.items()): if not self.authz.has_permission(path): # FIXME: what about base_path? continue if not path.startswith(self.scope[1:]): continue base_path = None if change.base_path: if change.base_path.startswith(self.scope): base_path = change.base_path[len(self.scope):] else: base_path = None action = '' if change.action == repos.CHANGE_ACTION_DELETE: action = Changeset.DELETE # Save off the index within changes of this deletion deletions[change.base_path] = idx elif change.added: if change.base_path and change.base_rev: action = Changeset.COPY # Save off the index within changes of this copy copies[change.base_path] = idx else: action = Changeset.ADD else: action = Changeset.EDIT kind = _kindmap[change.item_kind] path = path[len(self.scope) - 1:] changes.append([path, kind, action, base_path, change.base_rev]) idx += 1 # Detect moves by checking for copies whose source was deleted in this # change set. moves = set() for k, v in core._as_list(copies.items()): if k in deletions: changes[v][2] = Changeset.MOVE # Record the index of the now redundant delete action. moves.add(deletions[k]) for i, change in enumerate(changes): # Do not return the 'delete' changes that were part of moves. if i not in moves: yield tuple(change)
def props_changed(path, propchanges): for (name, value) in core._as_list(propchanges.items()): (kind, _) = core.svn_property_kind(name) if kind != core.svn_prop_regular_kind: continue got_prop_changes.append( (path[len(self.path) + 1:], name, value))
def test_mergeinfo_leakage__incorrect_range_t_refcounts(self): """Ensure that the ref counts on svn_merge_range_t objects returned by svn_mergeinfo_parse() are correct.""" # When reference counting is working properly, each svn_merge_range_t in # the returned mergeinfo will have a ref count of 1... mergeinfo = core.svn_mergeinfo_parse(self.TEXT_MERGEINFO1) for (path, rangelist) in core._as_list(mergeinfo.items()): # ....and now 2 (incref during iteration of rangelist) for (i, r) in enumerate(rangelist): # ....and now 3 (incref during iteration of each range object) refcount = sys.getrefcount(r) # ....and finally, 4 (getrefcount() also increfs) expected = 4 # Note: if path and index are not '/trunk' and 0 respectively, then # only some of the range objects are leaking, which is, as far as # leaks go, even more impressive. self.assertEqual(refcount, expected, ( "Memory leak! Expected a ref count of %d for svn_merge_range_t " "object, but got %d instead (path: %s, index: %d). Probable " "cause: incorrect Py_INCREF/Py_DECREF usage in libsvn_swig_py/" "swigutil_py.c." % (expected, refcount, path, i))) del mergeinfo gc.collect()
def receiver(log_entry, pool): called[0] = True self.assertEqual(log_entry.revision, rev) if discover_changed_paths: self.assertEqual(core._as_list(log_entry.changed_paths.keys()), [b'/bla3']) changed_path = log_entry.changed_paths[b'/bla3'] self.assertTrue( changed_path.action in [b'A', b'D', b'R', b'M']) self.assertEqual(changed_path.copyfrom_path, None) self.assertEqual(changed_path.copyfrom_rev, -1) else: self.assertEqual(log_entry.changed_paths, None) if log_revprops is None: self.assertEqual(log_entry.revprops, revprops) elif len(log_revprops) == 0: self.assertTrue(log_entry.revprops == None or len(log_entry.revprops) == 0) else: revprop_names = sorted(log_entry.revprops.keys()) log_revprops.sort() self.assertEqual(revprop_names, log_revprops) for i in log_revprops: self.assertEqual(log_entry.revprops[i], revprops[i], msg="%s != %s on %s" % (log_entry.revprops[i], revprops[i], (log_revprops, discover_changed_paths)))
def receiver(changed_paths, revision, author, date, message, pool): receiver_called[0] = True self.assertEqual(revision, info.revision) self.assertEqual(author, info.author) self.assertEqual(date, info.date) self.assertEqual(message, revprops[b'svn:log']) for (path, change) in core._as_list(changed_paths.items()): path = path.lstrip(b'/') self.assertTrue(path in all_paths) if path in to_delete: self.assertEqual(change.action, b'D') elif path in to_mkdir or path in to_add: self.assertEqual(change.action, b'A') elif path in to_dir_prop or path in to_file_prop: self.assertEqual(change.action, b'M')
def test_get_logs(self): """Test scope of get_logs callbacks""" logs = [] def addLog(paths, revision, author, date, message, pool): if paths is not None: logs.append(paths) # Run get_logs repos.get_logs(self.repos, [b'/'], self.rev, 0, True, 0, addLog) # Count and verify changes change_count = 0 for log in logs: for path_changed in core._as_list(log.values()): change_count += 1 path_changed.assert_valid() self.assertEqual(logs[2][b"/tags/v1.1"].action, b"A") self.assertEqual(logs[2][b"/tags/v1.1"].copyfrom_path, b"/branches/v1x") self.assertEqual(len(logs), 12) self.assertEqual(change_count, 19)
def get_properties(self): props = fs.node_proplist(self.root, self.scoped_path) for name, value in core._as_list(props.items()): props[name] = value return props
# # diff.py: public Python interface for diff components # # Subversion is a tool for revision control. # See http://subversion.apache.org for more information. # ###################################################################### # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. ###################################################################### from libsvn.diff import * from svn.core import _unprefix_names, _as_list _unprefix_names(locals(), 'svn_diff_') __all__ = [x for x in _as_list(locals()) if x.lower().startswith('svn_')] del _unprefix_names
def entries(root, path, pool=None): "Call dir_entries returning a dictionary mappings names to IDs." e = dir_entries(root, path, pool) for name, entry in _as_list(e.items()): e[name] = dirent_t_id_get(entry) return e
def test_delta_driver_commit(self): # Setup paths we'll commit in this test. to_delete = [b'trunk/README.txt', b'trunk/dir1/dir2'] to_mkdir = [b'test_delta_driver_commit.d', b'test_delta_driver_commit2.d'] to_add = [b'test_delta_driver_commit', b'test_delta_driver_commit2'] to_dir_prop = [b'trunk/dir1/dir3', b'test_delta_driver_commit2.d'] to_file_prop = [b'trunk/README2.txt', b'test_delta_driver_commit2'] all_paths = {} for i in to_delete + to_mkdir + to_add + to_dir_prop + to_file_prop: all_paths[i] = True # base revision for the commit revision = fs.youngest_rev(self.fs) commit_info = [] def commit_cb(info, pool): commit_info.append(info) revprops = {b"svn:log": b"foobar", b"testprop": b""} (editor, edit_baton) = ra.get_commit_editor3(self.ra_ctx, revprops, commit_cb, None, False) try: def driver_cb(parent, path, pool): self.assertTrue(path in all_paths) dir_baton = file_baton = None if path in to_delete: # Leave dir_baton alone, as it must be None for delete. editor.delete_entry(path, revision, parent, pool) elif path in to_mkdir: dir_baton = editor.add_directory(path, parent, None, -1, # copyfrom pool) elif path in to_add: file_baton = editor.add_file(path, parent, None, -1, # copyfrom pool) # wc.py:test_commit tests apply_textdelta . if path in to_dir_prop: if dir_baton is None: dir_baton = editor.open_directory(path, parent, revision, pool) editor.change_dir_prop(dir_baton, b'test_delta_driver_commit', b'foo', pool) elif path in to_file_prop: if file_baton is None: file_baton = editor.open_file(path, parent, revision, pool) editor.change_file_prop(file_baton, b'test_delta_driver_commit', b'foo', pool) if file_baton is not None: editor.close_file(file_baton, None, pool) return dir_baton delta.path_driver(editor, edit_baton, -1, core._as_list(all_paths.keys()), driver_cb) editor.close_edit(edit_baton) except: try: editor.abort_edit(edit_baton) except: # We already have an exception in progress, not much we can do # about this. pass raise info = commit_info[0] if info.author is not None: revprops[b'svn:author'] = info.author revprops[b'svn:date'] = info.date self.assertEqual(ra.rev_proplist(self.ra_ctx, info.revision), revprops) receiver_called = [False] def receiver(changed_paths, revision, author, date, message, pool): receiver_called[0] = True self.assertEqual(revision, info.revision) self.assertEqual(author, info.author) self.assertEqual(date, info.date) self.assertEqual(message, revprops[b'svn:log']) for (path, change) in core._as_list(changed_paths.items()): path = path.lstrip(b'/') self.assertTrue(path in all_paths) if path in to_delete: self.assertEqual(change.action, b'D') elif path in to_mkdir or path in to_add: self.assertEqual(change.action, b'A') elif path in to_dir_prop or path in to_file_prop: self.assertEqual(change.action, b'M') ra.get_log(self.ra_ctx, [b''], info.revision, info.revision, 0, # limit True, # discover_changed_paths True, # strict_node_history receiver) self.assertTrue(receiver_called[0])
def test_entries_read(self): entries = wc.entries_read(self.wc, True) keys = core._as_list(entries.keys()) keys.sort() self.assertEqual([b'', b'branches', b'tags', b'trunk'], keys)