def _print_tree(self, e_factory, base_rev=None, pass_root=0):
    if base_rev is None:
      # a specific base rev was not provided. use the transaction base,
      # or the previous revision
      if self.txn_ptr:
        base_rev = fs.txn_base_revision(self.txn_ptr)
      else:
        base_rev = self.rev - 1

    # get the current root
    if self.txn_ptr:
      root = fs.txn_root(self.txn_ptr)
    else:
      root = fs.revision_root(self.fs_ptr, self.rev)

    # the base of the comparison
    base_root = fs.revision_root(self.fs_ptr, base_rev)

    if pass_root:
      editor = e_factory(root, base_root)
    else:
      editor = e_factory()

    # construct the editor for printing these things out
    e_ptr, e_baton = delta.make_editor(editor)

    # compute the delta, printing as we go
    def authz_cb(root, path, pool):
      return 1
    repos.dir_delta(base_root, '', '', root, '',
		    e_ptr, e_baton, authz_cb, 0, 1, 0, 0)
def inner(pool, path, rev):
    repos_ptr = repos.svn_repos_open(path, pool)
    fs_ptr = repos.svn_repos_fs(repos_ptr)

    root = fs.revision_root(fs_ptr, rev, pool)
    base_rev = rev - 1

    # get all changes
    editor = repos.RevisionChangeCollector(fs_ptr, rev, pool)
    e_ptr, e_baton = delta.make_editor(editor, pool)
    repos.svn_repos_replay(root, e_ptr, e_baton, pool)

    changelist = editor.changes.items()
    changelist.sort()

    base_root = fs.revision_root(fs_ptr, base_rev, pool)

    l = []
    for filepath, change in changelist:
        d = {'path': filepath, 'info': ''}
        if change.path:
            if change.added:
                d['action'] = 'new'
            else:
                d['action'] = 'modify'
                differ = fs.FileDiff(base_root, change.path, root, filepath,
                    pool, '-L \t(original) -L \t(new) -u'.split(' '))
                d['info'] = differ.get_pipe().read()
        else:
            d['action'] = 'delete'
        l.append(d)
    return l
def main(pool, repos_dir, txn):
  # Construct a ChangeCollector to fetch our changes.
  fs_ptr = repos.svn_repos_fs(repos.svn_repos_open(repos_dir, pool))
  root = fs.txn_root(fs.open_txn(fs_ptr, txn, pool), pool)
  cc = repos.ChangeCollector(fs_ptr, root, pool)

  # Call the transaction property validator.  Might as well get the
  # cheap checks outta the way first.
  retval = test_props(cc.get_root_props())
  if retval:
    return retval

  # Generate the path-based changes list.
  e_ptr, e_baton = delta.make_editor(cc, pool)
  repos.svn_repos_replay(root, e_ptr, e_baton, pool)

  # Call the path change validator.
  changes = cc.get_changes()
  paths = changes.keys()
  paths.sort(lambda a, b: core.svn_path_compare_paths(a, b))
  for path in paths:
    change = changes[path]
    retval = test_path_change(path, change)
    if retval:
      return retval
    
  return 0
    def load(self, repo_path):
	repo_path = core.svn_path_canonicalize(repo_path)
	repos_ptr = repos.open(repo_path)
	fs_ptr = repos.fs(repos_ptr)
	rev = fs.youngest_rev(fs_ptr)
	base_root = fs.revision_root(fs_ptr, 0)
	root = fs.revision_root(fs_ptr, rev)
	hist = fs.node_history(root, self.root)
	while hist is not None:
	    hist = fs.history_prev(hist,0)
	    dummy,rev = fs.history_location(hist)
	    d = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_DATE)
	    author = fs.revision_prop(fs_ptr, rev, \
		    core.SVN_PROP_REVISION_AUTHOR)
	    if author == 'svnadmin':
		continue
	    self.last_author = author
	    self.last_date = core.svn_time_from_cstring(d) / 1000000
	    self.last_rev = rev
	    def authz_cb(root, path, pool):
		return 1
	    editor = SvnDumperEditor(self)
	    e_ptr, e_baton = delta.make_editor(editor)
	    repos.dir_delta(base_root, '', '', root, self.root, e_ptr, e_baton,
		authz_cb, 0, 1, 0, 0)
	    break
Exemple #5
0
    def load(self, repo_path):
        repo_path = core.svn_path_canonicalize(repo_path)
        repos_ptr = repos.open(repo_path)
        fs_ptr = repos.fs(repos_ptr)
        rev = fs.youngest_rev(fs_ptr)
        base_root = fs.revision_root(fs_ptr, 0)
        root = fs.revision_root(fs_ptr, rev)
        hist = fs.node_history(root, self.root)
        while hist is not None:
            hist = fs.history_prev(hist, 0)
            dummy, rev = fs.history_location(hist)
            d = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_DATE)
            author = fs.revision_prop(fs_ptr, rev, \
                    core.SVN_PROP_REVISION_AUTHOR)
            if author == 'svnadmin':
                continue
            self.last_author = author
            self.last_date = core.svn_time_from_cstring(d) / 1000000
            self.last_rev = rev

            def authz_cb(root, path, pool):
                return 1

            editor = SvnDumperEditor(self)
            e_ptr, e_baton = delta.make_editor(editor)
            repos.dir_delta(base_root, '', '', root, self.root, e_ptr, e_baton,
                            authz_cb, 0, 1, 0, 0)
            break
Exemple #6
0
def main(pool, repos_dir, txn):
    # Construct a ChangeCollector to fetch our changes.
    fs_ptr = repos.svn_repos_fs(repos.svn_repos_open(repos_dir, pool))
    root = fs.txn_root(fs.open_txn(fs_ptr, txn, pool), pool)
    cc = repos.ChangeCollector(fs_ptr, root, pool)

    # Call the transaction property validator.  Might as well get the
    # cheap checks outta the way first.
    retval = test_props(cc.get_root_props())
    if retval:
        return retval

    # Generate the path-based changes list.
    e_ptr, e_baton = delta.make_editor(cc, pool)
    repos.svn_repos_replay(root, e_ptr, e_baton, pool)

    # Call the path change validator.
    changes = cc.get_changes()
    paths = changes.keys()
    paths.sort(lambda a, b: core.svn_path_compare_paths(a, b))
    for path in paths:
        change = changes[path]
        retval = test_path_change(path, change)
        if retval:
            return retval

    return 0
  def _print_tree(self, e_factory, rootpath='', base_rev=None, pass_root=0):
    # It no longer prints, it returns the editor made by e_factory which
    # contains the tree in a list.
    if base_rev is None:
      # a specific base rev was not provided. use the transaction base,
      # or the previous revision
      if self.txn_ptr:
        base_rev = fs.txn_base_revision(self.txn_ptr)
      else:
        base_rev = self.rev - 1

    # get the current root
    if self.txn_ptr:
      root = fs.txn_root(self.txn_ptr, self.pool)
    else:
      root = fs.revision_root(self.fs_ptr, self.rev, self.pool)

    # the base of the comparison
    base_root = fs.revision_root(self.fs_ptr, base_rev, self.pool)

    if pass_root:
      editor = e_factory(root, base_root)
    else:
      editor = e_factory()

    # construct the editor for printing these things out
    e_ptr, e_baton = delta.make_editor(editor, self.pool)

    # compute the delta, printing as we go
    def authz_cb(root, path, pool):
      return 1
    repos.dir_delta(base_root, '', '', root, rootpath.encode('utf-8'),
                    e_ptr, e_baton, authz_cb, 0, 1, 0, 0, self.pool)
    return editor
Exemple #8
0
    def _print_tree(self, e_factory, rootpath='', base_rev=None, pass_root=0):
        # It no longer prints, it returns the editor made by e_factory which
        # contains the tree in a list.
        if base_rev is None:
            # a specific base rev was not provided. use the transaction base,
            # or the previous revision
            if self.txn_ptr:
                base_rev = fs.txn_base_revision(self.txn_ptr)
            else:
                base_rev = self.rev - 1

        # get the current root
        if self.txn_ptr:
            root = fs.txn_root(self.txn_ptr, self.pool)
        else:
            root = fs.revision_root(self.fs_ptr, self.rev, self.pool)

        # the base of the comparison
        base_root = fs.revision_root(self.fs_ptr, base_rev, self.pool)

        if pass_root:
            editor = e_factory(root, base_root)
        else:
            editor = e_factory()

        # construct the editor for printing these things out
        e_ptr, e_baton = delta.make_editor(editor, self.pool)

        # compute the delta, printing as we go
        def authz_cb(root, path, pool):
            return 1

        repos.dir_delta(base_root, '', '', root, rootpath.encode('utf-8'),
                        e_ptr, e_baton, authz_cb, 0, 1, 0, 0, self.pool)
        return editor
Exemple #9
0
  def test_do_diff2(self):

    class ChangeReceiver(delta.Editor):
        def __init__(self):
            self.textdeltas = []

        def apply_textdelta(self, file_baton, base_checksum):
            def textdelta_handler(textdelta):
                if textdelta is not None:
                    self.textdeltas.append(textdelta)
            return textdelta_handler

    editor = ChangeReceiver()

    e_ptr, e_baton = delta.make_editor(editor)

    fs_revnum = fs.youngest_rev(self.fs)

    sess_url = ra.get_session_url(self.ra_ctx)
    try:
        ra.reparent(self.ra_ctx, REPOS_URL+"/trunk")
        reporter, reporter_baton = ra.do_diff2(self.ra_ctx, fs_revnum,
                                               "README.txt", 0, 0, 1,
                                               REPOS_URL+"/trunk/README.txt",
                                               e_ptr, e_baton)
        reporter.set_path(reporter_baton, "", 0, True, None)
        reporter.finish_report(reporter_baton)
    finally:
        ra.reparent(self.ra_ctx, sess_url)
      
    self.assertEqual("A test.\n", editor.textdeltas[0].new_data)
    self.assertEqual(1, len(editor.textdeltas))
Exemple #10
0
    def _walk_tree(self, e_factory, base_rev=None, pass_root=0, callback=None):
        if base_rev is None:
            # a specific base rev was not provided. use the transaction base,
            # or the previous revision
            if self.txn_ptr:
                base_rev = fs.txn_base_revision(self.txn_ptr)
            else:
                base_rev = self.rev - 1

        # get the current root
        if self.txn_ptr:
            root = fs.txn_root(self.txn_ptr)
        else:
            root = fs.revision_root(self.fs_ptr, self.rev)

        # the base of the comparison
        base_root = fs.revision_root(self.fs_ptr, base_rev)

        if callback == None:
            callback = lambda msg: None

        if pass_root:
            editor = e_factory(root, base_root, callback)
        else:
            editor = e_factory(callback=callback)

        # construct the editor for printing these things out
        e_ptr, e_baton = delta.make_editor(editor)

        # compute the delta, printing as we go
        def authz_cb(root, path, pool):
            return 1

        repos.dir_delta(base_root, '', '', root, '', e_ptr, e_baton, authz_cb,
                        0, 1, 0, 0)
Exemple #11
0
    def test_do_diff2(self):
        class ChangeReceiver(delta.Editor):
            def __init__(self):
                self.textdeltas = []

            def apply_textdelta(self, file_baton, base_checksum, pool=None):
                def textdelta_handler(textdelta):
                    if textdelta is not None:
                        self.textdeltas.append(textdelta)

                return textdelta_handler

        editor = ChangeReceiver()

        e_ptr, e_baton = delta.make_editor(editor)

        fs_revnum = fs.youngest_rev(self.fs)

        sess_url = ra.get_session_url(self.ra_ctx)
        try:
            ra.reparent(self.ra_ctx, self.repos_uri + "/trunk")
            reporter, reporter_baton = ra.do_diff2(
                self.ra_ctx, fs_revnum, "README.txt", 0, 0, 1,
                self.repos_uri + "/trunk/README.txt", e_ptr, e_baton)
            reporter.set_path(reporter_baton, "", 0, True, None)
            reporter.finish_report(reporter_baton)
        finally:
            ra.reparent(self.ra_ctx, sess_url)

        self.assertEqual("A test.\n", editor.textdeltas[0].new_data)
        self.assertEqual(1, len(editor.textdeltas))
Exemple #12
0
        def _get_changed_paths(fsroot):
            """Return a 3-tuple: found_readable, found_unreadable, changed_paths."""
            editor = repos.ChangeCollector(self.fs_ptr, fsroot)
            e_ptr, e_baton = delta.make_editor(editor)
            repos.svn_repos_replay(fsroot, e_ptr, e_baton)
            changedpaths = {}
            changes = editor.get_changes()

            # Copy the Subversion changes into a new hash, checking
            # authorization and converting them into ChangedPath objects.
            found_readable = found_unreadable = 0
            for path in changes.keys():
                spath = _to_str(path)
                change = changes[path]
                if change.path:
                    change.path = _cleanup_path(change.path)
                if change.base_path:
                    change.base_path = _cleanup_path(change.base_path)
                is_copy = 0
                action = {
                    repos.CHANGE_ACTION_ADD: vclib.ADDED,
                    repos.CHANGE_ACTION_DELETE: vclib.DELETED,
                    repos.CHANGE_ACTION_REPLACE: vclib.REPLACED,
                }.get(change.action, vclib.MODIFIED)
                if ((action == vclib.ADDED or action == vclib.REPLACED)
                        and change.base_path and change.base_rev):
                    is_copy = 1
                pathtype = _kind2type(change.item_kind)
                parts = _path_parts(spath)
                if vclib.check_path_access(self, parts, pathtype, rev):
                    if is_copy and change.base_path and (change.base_path !=
                                                         path):
                        parts = _path_parts(_to_str(change.base_path))
                        if not vclib.check_path_access(self, parts, pathtype,
                                                       change.base_rev):
                            is_copy = 0
                            change.base_path = None
                            change.base_rev = None
                            found_unreadable = 1
                    if change.base_path:
                        base_path = _to_str(change.base_path)
                    else:
                        base_path = None
                    changedpaths[spath] = SVNChangedPath(
                        spath,
                        rev,
                        pathtype,
                        base_path,
                        change.base_rev,
                        action,
                        is_copy,
                        change.text_changed,
                        change.prop_changes,
                    )
                    found_readable = 1
                else:
                    found_unreadable = 1
            return found_readable, found_unreadable, list(
                changedpaths.values())
Exemple #13
0
 def get_changes(self, old_path, old_rev, new_path, new_rev,
                 ignore_ancestry=0):
     old_node = new_node = None
     old_rev = self.normalize_rev(old_rev)
     new_rev = self.normalize_rev(new_rev)
     if self.has_node(old_path, old_rev):
         old_node = self.get_node(old_path, old_rev)
     else:
         raise NoSuchNode(old_path, old_rev, 'The Base for Diff is invalid')
     if self.has_node(new_path, new_rev):
         new_node = self.get_node(new_path, new_rev)
     else:
         raise NoSuchNode(new_path, new_rev, 'The Target for Diff is invalid')
     if new_node.kind != old_node.kind:
         raise TracError('Diff mismatch: Base is a %s (%s in revision %s) '
                         'and Target is a %s (%s in revision %s).' \
                         % (old_node.kind, old_path, old_rev,
                            new_node.kind, new_path, new_rev))
     subpool = Pool(self.pool)
     if new_node.isdir:
         editor = DiffChangeEditor()
         e_ptr, e_baton = delta.make_editor(editor, subpool())
         old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
         new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
         def authz_cb(root, path, pool): return 1
         text_deltas = 0 # as this is anyway re-done in Diff.py...
         entry_props = 0 # "... typically used only for working copy updates"
         repos.svn_repos_dir_delta(old_root,
                                   _to_svn(self.scope + old_path), '',
                                   new_root,
                                   _to_svn(self.scope + new_path),
                                   e_ptr, e_baton, authz_cb,
                                   text_deltas,
                                   1, # directory
                                   entry_props,
                                   ignore_ancestry,
                                   subpool())
         for path, kind, change in editor.deltas:
             path = _from_svn(path)
             old_node = new_node = None
             if change != Changeset.ADD:
                 old_node = self.get_node(posixpath.join(old_path, path),
                                          old_rev)
             if change != Changeset.DELETE:
                 new_node = self.get_node(posixpath.join(new_path, path),
                                          new_rev)
             else:
                 kind = _kindmap[fs.check_path(old_root,
                                               _to_svn(self.scope,
                                                       old_node.path),
                                               subpool())]
             yield  (old_node, new_node, kind, change)
     else:
         old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
         new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
         if fs.contents_changed(old_root, _to_svn(self.scope, old_path),
                                new_root, _to_svn(self.scope, new_path),
                                subpool()):
             yield (old_node, new_node, Node.FILE, Changeset.EDIT)
    def get_revision(self, revision, editor):
        ''' feed the contents of the given revision to the given editor '''

        e_ptr, e_baton = delta.make_editor(editor)

        reporter, reporter_baton = ra.do_update(self.ra, revision, "", True,
                                                e_ptr, e_baton)

        reporter.set_path(reporter_baton, "", revision, True, None)
        reporter.finish_report(reporter_baton)
Exemple #15
0
 def test_unnamed_editor(self):
     """Test editor object without reference from interpreter"""
     # Check that the delta.Editor object has proper lifetime. Without
     # increment of the refcount in make_baton, the object was destroyed
     # immediately because the interpreter does not hold a reference to it.
     this_root = fs.revision_root(self.fs, self.rev)
     prev_root = fs.revision_root(self.fs, self.rev-1)
     e_ptr, e_baton = delta.make_editor(ChangeReceiver(this_root, prev_root))
     repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
             _authz_callback, 1, 1, 0, 0)
Exemple #16
0
    def get_revision(self, revision, editor):
        ''' feed the contents of the given revision to the given editor '''

        e_ptr, e_baton = delta.make_editor(editor)

        reporter, reporter_baton = ra.do_update(self.ra, revision, "", True,
                                                e_ptr, e_baton)

        reporter.set_path(reporter_baton, "", revision, True, None)
        reporter.finish_report(reporter_baton)
Exemple #17
0
 def test_unnamed_editor(self):
     """Test editor object without reference from interpreter"""
     # Check that the delta.Editor object has proper lifetime. Without
     # increment of the refcount in make_baton, the object was destroyed
     # immediately because the interpreter does not hold a reference to it.
     this_root = fs.revision_root(self.fs, self.rev)
     prev_root = fs.revision_root(self.fs, self.rev - 1)
     e_ptr, e_baton = delta.make_editor(ChangeReceiver(
         this_root, prev_root))
     repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
                     _authz_callback, 1, 1, 0, 0)
Exemple #18
0
def check_po(pool, repos_path, txn):
    def authz_cb(root, path, pool):
        return 1

    fs_ptr = repos.fs(repos.open(repos_path, pool))
    txn_ptr = fs.open_txn(fs_ptr, txn, pool)
    txn_root = fs.txn_root(txn_ptr, pool)
    base_root = fs.revision_root(fs_ptr, fs.txn_base_revision(txn_ptr), pool)
    editor = ChangeReceiver(txn_root, base_root, pool)
    e_ptr, e_baton = delta.make_editor(editor, pool)
    repos.dir_delta(base_root, "", "", txn_root, "", e_ptr, e_baton, authz_cb, 0, 1, 0, 0, pool)
Exemple #19
0
    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)
Exemple #20
0
def check_po(pool, repos_path, txn):
    def authz_cb(root, path, pool):
        return 1

    fs_ptr = repos.fs(repos.open(repos_path, pool))
    txn_ptr = fs.open_txn(fs_ptr, txn, pool)
    txn_root = fs.txn_root(txn_ptr, pool)
    base_root = fs.revision_root(fs_ptr, fs.txn_base_revision(txn_ptr), pool)
    editor = ChangeReceiver(txn_root, base_root, pool)
    e_ptr, e_baton = delta.make_editor(editor, pool)
    repos.dir_delta(base_root, '', '', txn_root, '', e_ptr, e_baton, authz_cb,
                    0, 1, 0, 0, pool)
Exemple #21
0
    def get_changes(self):
        pool = Pool(self.pool)
        root = fs.revision_root(self.fs_ptr, self.rev, pool())
        editor = repos.RevisionChangeCollector(self.fs_ptr, self.rev, pool())
        e_ptr, e_baton = delta.make_editor(editor, pool())
        repos.svn_repos_replay(root, e_ptr, e_baton, pool())

        idx = 0
        copies, deletions = {}, {}
        changes = []
        for path, change in 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 not change.path:
                action = Changeset.DELETE
                deletions[change.base_path] = idx
            elif change.added:
                if change.base_path and change.base_rev:
                    action = Changeset.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

        moves = []
        for k, v in copies.items():
            if k in deletions:
                changes[v][2] = Changeset.MOVE
                moves.append(deletions[k])
        offset = 0
        for i in moves:
            del changes[i - offset]
            offset += 1

        changes.sort()
        for change in changes:
            yield tuple(change)
Exemple #22
0
  def test_update(self):
    class TestEditor(delta.Editor):
        pass

    editor = TestEditor()

    e_ptr, e_baton = delta.make_editor(editor)

    reporter, reporter_baton = ra.do_update(self.ra_ctx, 10, "", True, e_ptr, e_baton)

    reporter.set_path(reporter_baton, "", 0, True, None)

    reporter.finish_report(reporter_baton)
Exemple #23
0
  def test_update(self):
    class TestEditor(delta.Editor):
        pass

    editor = TestEditor()

    e_ptr, e_baton = delta.make_editor(editor)

    reporter, reporter_baton = ra.do_update(self.ra_ctx, 10, "", True, e_ptr, e_baton)

    reporter.set_path(reporter_baton, "", 0, True, None)

    reporter.finish_report(reporter_baton)
Exemple #24
0
    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
        copies, deletions = {}, {}
        changes = []
        for path, change in 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 not change.path:
                action = Changeset.DELETE
                deletions[change.base_path] = idx
            elif change.added:
                if change.base_path and change.base_rev:
                    action = Changeset.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

        moves = []
        for k,v in copies.items():
            if k in deletions:
                changes[v][2] = Changeset.MOVE
                moves.append(deletions[k])
        offset = 0
        for i in moves:
            del changes[i - offset]
            offset += 1

        for change in changes:
            yield tuple(change)
Exemple #25
0
  def test_dir_delta(self):
    """Test scope of dir_delta callbacks"""
    # Run dir_delta
    this_root = fs.revision_root(self.fs, self.rev)
    prev_root = fs.revision_root(self.fs, self.rev-1)
    editor = ChangeReceiver(this_root, prev_root)
    e_ptr, e_baton = delta.make_editor(editor)
    repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
                    _authz_callback, 1, 1, 0, 0)

    # Check results
    self.assertEqual(editor.textdeltas[0].new_data, "This is a test.\n")
    self.assertEqual(editor.textdeltas[1].new_data, "A test.\n")
    self.assertEqual(len(editor.textdeltas),2)
Exemple #26
0
 def get_deltas(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=0):
     old_node = new_node = None
     old_rev = self.normalize_rev(old_rev)
     new_rev = self.normalize_rev(new_rev)
     if self.has_node(old_path, old_rev):
         old_node = self.get_node(old_path, old_rev)
     else:
         raise TracError, ('The Base for Diff is invalid: path %s'
                           ' doesn\'t exist in revision %s' \
                           % (old_path, old_rev))
     if self.has_node(new_path, new_rev):
         new_node = self.get_node(new_path, new_rev)
     else:
         raise TracError, ('The Target for Diff is invalid: path %s'
                           ' doesn\'t exist in revision %s' \
                           % (new_path, new_rev))
     if new_node.kind != old_node.kind:
         raise TracError, ('Diff mismatch: Base is a %s (%s in revision %s) '
                           'and Target is a %s (%s in revision %s).' \
                           % (old_node.kind, old_path, old_rev,
                              new_node.kind, new_path, new_rev))
     if new_node.isdir:
         editor = DiffChangeEditor()
         e_ptr, e_baton = delta.make_editor(editor)
         old_root = fs.revision_root(self.fs_ptr, old_rev)
         new_root = fs.revision_root(self.fs_ptr, new_rev)
         def authz_cb(root, path, pool): return 1
         text_deltas = 0 # as this is anyway re-done in Diff.py...
         entry_props = 0 # ("... typically used only for working copy updates")
         repos.svn_repos_dir_delta(old_root, old_path, '',
                                   new_root, new_path,
                                   e_ptr, e_baton, authz_cb,
                                   text_deltas,
                                   1, # directory
                                   entry_props,
                                   ignore_ancestry)
         for path, kind, change in editor.deltas:
             old_node = new_node = None
             if change != Changeset.ADD:
                 old_node = self.get_node(posixpath.join(old_path, path), old_rev)
             if change != Changeset.DELETE:
                 new_node = self.get_node(posixpath.join(new_path, path), new_rev)
             else:
                 kind = _kindmap[fs.check_path(old_root, old_node.path)]
             yield  (old_node, new_node, kind, change)
     else:
         old_root = fs.revision_root(self.fs_ptr, old_rev)
         new_root = fs.revision_root(self.fs_ptr, new_rev)
         if fs.contents_changed(old_root, old_path, new_root, new_path):
             yield (old_node, new_node, Node.FILE, Changeset.EDIT)
Exemple #27
0
    def test_dir_delta(self):
        """Test scope of dir_delta callbacks"""
        # Run dir_delta
        this_root = fs.revision_root(self.fs, self.rev)
        prev_root = fs.revision_root(self.fs, self.rev - 1)
        editor = ChangeReceiver(this_root, prev_root)
        e_ptr, e_baton = delta.make_editor(editor)
        repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
                        _authz_callback, 1, 1, 0, 0)

        # Check results
        self.assertEqual(editor.textdeltas[0].new_data, "This is a test.\n")
        self.assertEqual(editor.textdeltas[1].new_data, "A test.\n")
        self.assertEqual(len(editor.textdeltas), 2)
 def get_replay(self, revision, editor, oldest_rev_i_have=0):
     # this method has a tendency to chew through RAM if you don't re-init
     self.init_ra_and_client()
     e_ptr, e_baton = delta.make_editor(editor)
     try:
         ra.replay(self.ra, revision, oldest_rev_i_have, True, e_ptr,
                   e_baton, self.pool)
     except core.SubversionException, e: #pragma: no cover
         # can I depend on this number being constant?
         if (e.apr_err == core.SVN_ERR_RA_NOT_IMPLEMENTED or
             e.apr_err == core.SVN_ERR_UNSUPPORTED_FEATURE):
             raise SubversionRepoCanNotReplay, ('This Subversion server '
                'is older than 1.4.0, and cannot satisfy replay requests.')
         else:
             raise
Exemple #29
0
def get_revision_info(svnrepos, rev):
  fsroot = svnrepos._getroot(rev)

  # Get the changes for the revision
  cps = ChangedPathSet()
  editor = repos.ChangeCollector(svnrepos.fs_ptr, fsroot,
                                 svnrepos.pool, cps.add_change)
  e_ptr, e_baton = delta.make_editor(editor, svnrepos.pool)
  repos.svn_repos_replay(fsroot, e_ptr, e_baton, svnrepos.pool)

  # Now get the revision property info.  Would use
  # editor.get_root_props(), but something is broken there...
  datestr, author, msg = _fs_rev_props(svnrepos.fs_ptr, rev, svnrepos.pool)
  date = _datestr_to_date(datestr, svnrepos.pool)

  return date, author, msg, cps.get_changes()
Exemple #30
0
  def test_dir_delta(self):
    """Test scope of dir_delta callbacks"""
    # Run dir_delta
    this_root = fs.revision_root(self.fs, self.rev)
    prev_root = fs.revision_root(self.fs, self.rev-1)
    editor = ChangeReceiver(this_root, prev_root)
    e_ptr, e_baton = delta.make_editor(editor)
    repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
                    _authz_callback, 1, 1, 0, 0)

    # Check results.
    # Ignore the order in which the editor delivers the two sibling files.
    self.assertEqual(set([editor.textdeltas[0].new_data,
                          editor.textdeltas[1].new_data]),
                     set(["This is a test.\n", "A test.\n"]))
    self.assertEqual(len(editor.textdeltas), 2)
 def get_replay(self, revision, editor, oldest_rev_i_have=0):
     # this method has a tendency to chew through RAM if you don't re-init
     self.init_ra_and_client()
     e_ptr, e_baton = delta.make_editor(editor)
     try:
         ra.replay(self.ra, revision, oldest_rev_i_have, True, e_ptr,
                   e_baton, self.pool)
     except SubversionException, e:  # pragma: no cover
         # can I depend on this number being constant?
         if (e.apr_err == core.SVN_ERR_RA_NOT_IMPLEMENTED
                 or e.apr_err == core.SVN_ERR_UNSUPPORTED_FEATURE):
             msg = ('This Subversion server is older than 1.4.0, and '
                    'cannot satisfy replay requests.')
             raise common.SubversionRepoCanNotReplay(msg)
         else:
             raise
Exemple #32
0
def get_revision_info(svnrepos, rev):
    fsroot = svnrepos._getroot(rev)

    # Get the changes for the revision
    cps = ChangedPathSet()
    editor = repos.ChangeCollector(svnrepos.fs_ptr, fsroot, svnrepos.pool,
                                   cps.add_change)
    e_ptr, e_baton = delta.make_editor(editor, svnrepos.pool)
    repos.svn_repos_replay(fsroot, e_ptr, e_baton, svnrepos.pool)

    # Now get the revision property info.  Would use
    # editor.get_root_props(), but something is broken there...
    datestr, author, msg = _fs_rev_props(svnrepos.fs_ptr, rev, svnrepos.pool)
    date = _datestr_to_date(datestr, svnrepos.pool)

    return date, author, msg, cps.get_changes()
Exemple #33
0
    def test_dir_delta(self):
        """Test scope of dir_delta callbacks"""
        # Run dir_delta
        this_root = fs.revision_root(self.fs, self.rev)
        prev_root = fs.revision_root(self.fs, self.rev - 1)
        editor = ChangeReceiver(this_root, prev_root)
        e_ptr, e_baton = delta.make_editor(editor)
        repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
                        _authz_callback, 1, 1, 0, 0)

        # Check results.
        # Ignore the order in which the editor delivers the two sibling files.
        self.assertEqual(
            set([editor.textdeltas[0].new_data,
                 editor.textdeltas[1].new_data]),
            set(["This is a test.\n", "A test.\n"]))
        self.assertEqual(len(editor.textdeltas), 2)
Exemple #34
0
def insert_change(pool, fs_ptr, rev, cursor):
    class ChangeEditor(delta.Editor):
        def __init__(self, rev, old_root, new_root, cursor):
            self.rev = rev
            self.cursor = cursor
            self.old_root = old_root
            self.new_root = new_root

        def delete_entry(self, path, revision, parent_baton, pool):
            self.cursor.execute(
                'INSERT INTO node_change (rev, name, change) '
                'VALUES (%s, %s, \'D\')', self.rev, path)

        def add_directory(self, path, parent_baton, copyfrom_path,
                          copyfrom_revision, dir_pool):
            self.cursor.execute(
                'INSERT INTO node_change (rev, name, change) '
                'VALUES (%s, %s, \'A\')', self.rev, path)

        def add_file(self, path, parent_baton, copyfrom_path,
                     copyfrom_revision, file_pool):
            self.cursor.execute(
                'INSERT INTO node_change (rev, name, change) '
                'VALUES (%s, %s, \'A\')', self.rev, path)

        def open_file(self, path, parent_baton, base_revision, file_pool):
            self.cursor.execute(
                'INSERT INTO node_change (rev, name, change) '
                'VALUES (%s, %s, \'M\')', self.rev, path)

    old_root = fs.revision_root(fs_ptr, rev - 1, pool)
    new_root = fs.revision_root(fs_ptr, rev, pool)

    editor = ChangeEditor(rev, old_root, new_root, cursor)
    e_ptr, e_baton = delta.make_editor(editor, pool)

    def authz_cb(root, path, pool):
        return 1

    repos.svn_repos_dir_delta(old_root, '', '', new_root, '', e_ptr, e_baton,
                              authz_cb, 0, 1, 0, 1, pool)
  def __init__(self, path, rev, txn):
    self.repo_root_path = core.svn_path_canonicalize(path)
    
    root_ptr = repos.open(self.repo_root_path)
    self.fs_ptr = repos.fs(root_ptr)

    # Set the revision/transaction
    if txn:
      self.txn_ptr = fs.open_txn(self.fs_ptr, txn)
    else:
      self.txn_ptr = None
      if rev is None:
          rev = fs.youngest_rev(self.fs_ptr)
    self.rev = rev

    # Set the root
    if self.txn_ptr:
      self.root = fs.txn_root(self.txn_ptr)
    else:
      self.root = fs.revision_root(self.fs_ptr, self.rev)

    # Set the base revision/transaction
    if self.txn_ptr:
      self.base_rev = fs.txn_base_revision(self.txn_ptr)
    else:
      self.base_rev = self.rev - 1

    # Set the base root of the comparison
    self.base_root = fs.revision_root(self.fs_ptr, self.base_rev)

    # Get all the changes and sort by path
    editor = repos.ChangeCollector(self.fs_ptr, self.root)
    e_ptr, e_baton = delta.make_editor(editor)
    repos.replay(self.root, e_ptr, e_baton)

    self.changelist = editor.get_changes().items()

    self.changelist.sort()
Exemple #36
0
    def get_changes(self):
        """Retrieve file changes for a given revision.

        (wraps ``repos.svn_repos_replay``)
        """
        pool = Pool(self.pool)
        tmp = Pool(pool)
        root = fs.revision_root(self.fs_ptr, self.rev, pool())
        editor = repos.RevisionChangeCollector(self.fs_ptr, self.rev, pool())
        e_ptr, e_baton = delta.make_editor(editor, pool())
        repos.svn_repos_replay(root, e_ptr, e_baton, pool())

        idx = 0
        copies, deletions = {}, {}
        changes = []
        revroots = {}
        for path_utf8, change in editor.changes.items():
            new_path = _from_svn(path_utf8)

            # Filtering on `path`
            if not _is_path_within_scope(self.scope, new_path):
                continue

            path_utf8 = change.path
            base_path_utf8 = change.base_path
            path = _from_svn(path_utf8)
            base_path = _from_svn(base_path_utf8)
            base_rev = change.base_rev
            change_action = getattr(change, 'action', None)

            # Ensure `base_path` is within the scope
            if not _is_path_within_scope(self.scope, base_path):
                base_path, base_rev = None, -1

            # Determine the action
            if not path and not new_path and self.scope == '/':
                action = Changeset.EDIT # root property change
            elif not path or (change_action is not None
                              and change_action == repos.CHANGE_ACTION_DELETE):
                if new_path:            # deletion
                    action = Changeset.DELETE
                    deletions[new_path.lstrip('/')] = idx
                else:                   # deletion outside of scope, ignore
                    continue
            elif change.added or not base_path: # add or copy
                action = Changeset.ADD
                if base_path and base_rev:
                    action = Changeset.COPY
                    copies[base_path.lstrip('/')] = idx
            else:
                action = Changeset.EDIT
                # identify the most interesting base_path/base_rev
                # in terms of last changed information (see r2562)
                if base_rev in revroots:
                    b_root = revroots[base_rev]
                else:
                    b_root = fs.revision_root(self.fs_ptr, base_rev, pool())
                    revroots[base_rev] = b_root
                tmp.clear()
                cbase_path_utf8 = fs.node_created_path(b_root, base_path_utf8,
                                                       tmp())
                cbase_path = _from_svn(cbase_path_utf8)
                cbase_rev = fs.node_created_rev(b_root, base_path_utf8, tmp())
                # give up if the created path is outside the scope
                if _is_path_within_scope(self.scope, cbase_path):
                    base_path, base_rev = cbase_path, cbase_rev

            kind = _kindmap[change.item_kind]
            path = _path_within_scope(self.scope, new_path or base_path)
            base_path = _path_within_scope(self.scope, base_path)
            changes.append([path, kind, action, base_path, base_rev])
            idx += 1

        moves = []
        # a MOVE is a COPY whose `base_path` corresponds to a `new_path`
        # which has been deleted
        for k, v in copies.items():
            if k in deletions:
                changes[v][2] = Changeset.MOVE
                moves.append(deletions[k])
        offset = 0
        moves.sort()
        for i in moves:
            del changes[i - offset]
            offset += 1

        changes.sort()
        for change in changes:
            yield tuple(change)
Exemple #37
0
    def get_changes(self, old_path, old_rev, new_path, new_rev,
                    ignore_ancestry=0):
        """Determine differences between two arbitrary pairs of paths
        and revisions.

        (wraps ``repos.svn_repos_dir_delta``)
        """
        old_node = new_node = None
        old_rev = self.normalize_rev(old_rev)
        new_rev = self.normalize_rev(new_rev)
        if self.has_node(old_path, old_rev):
            old_node = self.get_node(old_path, old_rev)
        else:
            raise NoSuchNode(old_path, old_rev, 'The Base for Diff is invalid')
        if self.has_node(new_path, new_rev):
            new_node = self.get_node(new_path, new_rev)
        else:
            raise NoSuchNode(new_path, new_rev,
                             'The Target for Diff is invalid')
        if new_node.kind != old_node.kind:
            raise TracError(_('Diff mismatch: Base is a %(oldnode)s '
                              '(%(oldpath)s in revision %(oldrev)s) and '
                              'Target is a %(newnode)s (%(newpath)s in '
                              'revision %(newrev)s).', oldnode=old_node.kind,
                              oldpath=old_path, oldrev=old_rev,
                              newnode=new_node.kind, newpath=new_path,
                              newrev=new_rev))
        subpool = Pool(self.pool)
        if new_node.isdir:
            editor = DiffChangeEditor()
            e_ptr, e_baton = delta.make_editor(editor, subpool())
            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
            def authz_cb(root, path, pool):
                return 1
            text_deltas = 0 # as this is anyway re-done in Diff.py...
            entry_props = 0 # "... typically used only for working copy updates"
            repos.svn_repos_dir_delta(old_root,
                                      _to_svn(subpool(), self.scope, old_path),
                                      '', new_root,
                                      _to_svn(subpool(), self.scope, new_path),
                                      e_ptr, e_baton, authz_cb,
                                      text_deltas,
                                      1, # directory
                                      entry_props,
                                      ignore_ancestry,
                                      subpool())
            # sort deltas by path before creating `SubversionNode`s to reduce
            # memory usage (#10978)
            deltas = sorted(((_from_svn(path), kind, change)
                             for path, kind, change in editor.deltas),
                            key=lambda entry: entry[0])
            for path, kind, change in deltas:
                old_node = new_node = None
                if change != Changeset.ADD:
                    old_node = self.get_node(posixpath.join(old_path, path),
                                             old_rev)
                if change != Changeset.DELETE:
                    new_node = self.get_node(posixpath.join(new_path, path),
                                             new_rev)
                else:
                    kind = _kindmap[fs.check_path(old_root,
                                                  _to_svn(subpool(),
                                                          self.scope,
                                                          old_node.path),
                                                  subpool())]
                yield  (old_node, new_node, kind, change)
        else:
            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
            if fs.contents_changed(old_root,
                                   _to_svn(subpool(), self.scope, old_path),
                                   new_root,
                                   _to_svn(subpool(), self.scope, new_path),
                                   subpool()):
                yield (old_node, new_node, Node.FILE, Changeset.EDIT)
Exemple #38
0
 def get_changes(self):
     pool = Pool(self.pool)
     tmp = Pool(pool)
     root = fs.revision_root(self.fs_ptr, self.rev, pool())
     editor = repos.RevisionChangeCollector(self.fs_ptr, self.rev, pool())
     e_ptr, e_baton = delta.make_editor(editor, pool())
     repos.svn_repos_replay(root, e_ptr, e_baton, pool())
     
     idx = 0
     copies, deletions = {}, {}
     changes = []
     revroots = {}
     for path, change in editor.changes.items():
         if not (_is_path_within_scope(self.scope, path)):
             continue
         
         path = change.path
         base_path = change.base_path
         base_rev = change.base_rev
         
         if not (_is_path_within_scope(self.scope, base_path)):
             base_path, base_rev = None, -1
         
         if not path:
             if base_path:
                 if base_path in deletions:
                     continue
                 action = 'D'
                 deletions[base_path] = idx
             elif self.scope == '/':
                 action = 'E'
             else:
                 continue
         elif change.added or not base_path:
             action = 'A'
             if base_path and base_rev:
                 action = 'C'
                 copies[base_path] = idx
         else:
             action = 'E'
             if revroots.has_key(base_rev):
                 b_root = revroots[base_rev]
             else:
                 b_root = fs.revision_root(self.fs_ptr, base_rev, pool())
                 revroots[base_rev] = b_root
             tmp.clear()
             cbase_path = fs.node_created_path(b_root, base_path, tmp())
             cbase_rev = fs.node_created_rev(b_root, base_path, tmp())
             
             if _is_path_within_scope(self.scope, cbase_path):
                 base_path, base_rev = cbase_path, cbase_rev
         kind = _kindmap[change.item_kind]
         path = _path_within_scope(self.scope, _from_svn(path or base_path))
         base_path = _path_within_scope(self.scope, _from_svn(base_path))
         changes.append({'path': path, 'kind': kind, 'action': action,
                         'base_path': base_path, 'base_rev': base_rev})
         idx += 1
     
     moves = []
     for k,v in copies.items():
         if k in deletions:
             changes[v]['action'] = 'M'
             moves.append(deletions[k])
     offset = 0
     moves.sort()
     for i in moves:
         del changes[i - offset]
         offset += 1
     
     changes.sort()
     for change in changes:
         yield change
Exemple #39
0
    def get_changes(self, old_path, old_rev, new_path, new_rev,
                    ignore_ancestry=0):
        """Determine differences between two arbitrary pairs of paths
        and revisions.

        (wraps ``repos.svn_repos_dir_delta``)
        """
        old_node = new_node = None
        old_rev = self.normalize_rev(old_rev)
        new_rev = self.normalize_rev(new_rev)
        if self.has_node(old_path, old_rev):
            old_node = self.get_node(old_path, old_rev)
        else:
            raise NoSuchNode(old_path, old_rev, 'The Base for Diff is invalid')
        if self.has_node(new_path, new_rev):
            new_node = self.get_node(new_path, new_rev)
        else:
            raise NoSuchNode(new_path, new_rev,
                             'The Target for Diff is invalid')
        if new_node.kind != old_node.kind:
            raise TracError(_('Diff mismatch: Base is a %(oldnode)s '
                              '(%(oldpath)s in revision %(oldrev)s) and '
                              'Target is a %(newnode)s (%(newpath)s in '
                              'revision %(newrev)s).', oldnode=old_node.kind,
                              oldpath=old_path, oldrev=old_rev,
                              newnode=new_node.kind, newpath=new_path,
                              newrev=new_rev))
        subpool = Pool(self.pool)
        if new_node.isdir:
            editor = DiffChangeEditor()
            e_ptr, e_baton = delta.make_editor(editor, subpool())
            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
            def authz_cb(root, path, pool):
                return 1
            text_deltas = 0 # as this is anyway re-done in Diff.py...
            entry_props = 0 # "... typically used only for working copy updates"
            repos.svn_repos_dir_delta(old_root,
                                      _to_svn(subpool(), self.scope, old_path),
                                      '', new_root,
                                      _to_svn(subpool(), self.scope, new_path),
                                      e_ptr, e_baton, authz_cb,
                                      text_deltas,
                                      1, # directory
                                      entry_props,
                                      ignore_ancestry,
                                      subpool())
            # sort deltas by path before creating `SubversionNode`s to reduce
            # memory usage (#10978)
            deltas = sorted(((_from_svn(path), kind, change)
                             for path, kind, change in editor.deltas),
                            key=lambda entry: entry[0])
            for path, kind, change in deltas:
                old_node = new_node = None
                if change != Changeset.ADD:
                    old_node = self.get_node(posixpath.join(old_path, path),
                                             old_rev)
                if change != Changeset.DELETE:
                    new_node = self.get_node(posixpath.join(new_path, path),
                                             new_rev)
                else:
                    kind = _kindmap[fs.check_path(old_root,
                                                  _to_svn(subpool(),
                                                          self.scope,
                                                          old_node.path),
                                                  subpool())]
                yield  (old_node, new_node, kind, change)
        else:
            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
            if fs.contents_changed(old_root,
                                   _to_svn(subpool(), self.scope, old_path),
                                   new_root,
                                   _to_svn(subpool(), self.scope, new_path),
                                   subpool()):
                yield (old_node, new_node, Node.FILE, Changeset.EDIT)
Exemple #40
0
        def _get_changed_paths(fsroot):
            """Return a 3-tuple: found_readable, found_unreadable, changed_paths."""
            editor = repos.ChangeCollector(self.fs_ptr, fsroot)
            e_ptr, e_baton = delta.make_editor(editor)
            repos.svn_repos_replay(fsroot, e_ptr, e_baton)
            changedpaths = {}
            changes = editor.get_changes()

            # Copy the Subversion changes into a new hash, checking
            # authorization and converting them into ChangedPath objects.
            found_readable = found_unreadable = 0
            for path in changes.keys():
                change = changes[path]
                if change.path:
                    change.path = _cleanup_path(change.path)
                if change.base_path:
                    change.base_path = _cleanup_path(change.base_path)
                is_copy = 0
                if not hasattr(change, 'action'):  # new to subversion 1.4.0
                    action = vclib.MODIFIED
                    if not change.path:
                        action = vclib.DELETED
                    elif change.added:
                        action = vclib.ADDED
                        replace_check_path = path
                        if change.base_path and change.base_rev:
                            replace_check_path = change.base_path
                        if changedpaths.has_key(replace_check_path) \
                           and changedpaths[replace_check_path].action == vclib.DELETED:
                            action = vclib.REPLACED
                else:
                    if change.action == repos.CHANGE_ACTION_ADD:
                        action = vclib.ADDED
                    elif change.action == repos.CHANGE_ACTION_DELETE:
                        action = vclib.DELETED
                    elif change.action == repos.CHANGE_ACTION_REPLACE:
                        action = vclib.REPLACED
                    else:
                        action = vclib.MODIFIED
                if (action == vclib.ADDED or action == vclib.REPLACED) \
                   and change.base_path \
                   and change.base_rev:
                    is_copy = 1
                if change.item_kind == core.svn_node_dir:
                    pathtype = vclib.DIR
                elif change.item_kind == core.svn_node_file:
                    pathtype = vclib.FILE
                else:
                    pathtype = None

                parts = _path_parts(path)
                if vclib.check_path_access(self, parts, pathtype, rev):
                    if is_copy and change.base_path and (change.base_path !=
                                                         path):
                        parts = _path_parts(change.base_path)
                        if not vclib.check_path_access(self, parts, pathtype,
                                                       change.base_rev):
                            is_copy = 0
                            change.base_path = None
                            change.base_rev = None
                            found_unreadable = 1
                    changedpaths[path] = SVNChangedPath(
                        path, rev, pathtype, change.base_path, change.base_rev,
                        action, is_copy, change.text_changed,
                        change.prop_changes)
                    found_readable = 1
                else:
                    found_unreadable = 1
            return found_readable, found_unreadable, changedpaths.values()
Exemple #41
0
   def _get_changed_paths(fsroot):
     """Return a 3-tuple: found_readable, found_unreadable, changed_paths."""
     editor = repos.ChangeCollector(self.fs_ptr, fsroot)
     e_ptr, e_baton = delta.make_editor(editor)
     repos.svn_repos_replay(fsroot, e_ptr, e_baton)
     changedpaths = {}
     changes = editor.get_changes()
   
     # Copy the Subversion changes into a new hash, checking
     # authorization and converting them into ChangedPath objects.
     found_readable = found_unreadable = 0
     for path in changes.keys():
       change = changes[path]
       if change.path:
         change.path = _cleanup_path(change.path)
       if change.base_path:
         change.base_path = _cleanup_path(change.base_path)
       is_copy = 0
       if not hasattr(change, 'action'): # new to subversion 1.4.0
         action = vclib.MODIFIED
         if not change.path:
           action = vclib.DELETED
         elif change.added:
           action = vclib.ADDED
           replace_check_path = path
           if change.base_path and change.base_rev:
             replace_check_path = change.base_path
           if changedpaths.has_key(replace_check_path) \
              and changedpaths[replace_check_path].action == vclib.DELETED:
             action = vclib.REPLACED
       else:
         if change.action == repos.CHANGE_ACTION_ADD:
           action = vclib.ADDED
         elif change.action == repos.CHANGE_ACTION_DELETE:
           action = vclib.DELETED
         elif change.action == repos.CHANGE_ACTION_REPLACE:
           action = vclib.REPLACED
         else:
           action = vclib.MODIFIED
       if (action == vclib.ADDED or action == vclib.REPLACED) \
          and change.base_path \
          and change.base_rev:
         is_copy = 1
       if change.item_kind == core.svn_node_dir:
         pathtype = vclib.DIR
       elif change.item_kind == core.svn_node_file:
         pathtype = vclib.FILE
       else:
         pathtype = None
 
       parts = _path_parts(path)
       if vclib.check_path_access(self, parts, pathtype, rev):
         if is_copy and change.base_path and (change.base_path != path):
           parts = _path_parts(change.base_path)
           if not vclib.check_path_access(self, parts, pathtype,
                                          change.base_rev):
             is_copy = 0
             change.base_path = None
             change.base_rev = None
             found_unreadable = 1
         changedpaths[path] = SVNChangedPath(path, rev, pathtype,
                                             change.base_path,
                                             change.base_rev, action,
                                             is_copy, change.text_changed,
                                             change.prop_changes)
         found_readable = 1
       else:
         found_unreadable = 1
     return found_readable, found_unreadable, changedpaths.values()
Exemple #42
0
    def _get_changes(self, old_path, old_rev, new_path, new_rev,
                     ignore_ancestry):
        old_node = new_node = None
        old_rev = self.normalize_rev(old_rev)
        new_rev = self.normalize_rev(new_rev)
        if self.has_node(old_path, old_rev):
            old_node = self.get_node(old_path, old_rev)
        else:
            raise NoSuchNode(old_path, old_rev, 'The Base for Diff is invalid')
        if self.has_node(new_path, new_rev):
            new_node = self.get_node(new_path, new_rev)
        else:
            raise NoSuchNode(new_path, new_rev,
                             'The Target for Diff is invalid')
        if new_node.kind != old_node.kind:
            raise TracError(
                _(
                    'Diff mismatch: Base is a %(oldnode)s '
                    '(%(oldpath)s in revision %(oldrev)s) and '
                    'Target is a %(newnode)s (%(newpath)s in '
                    'revision %(newrev)s).',
                    oldnode=old_node.kind,
                    oldpath=old_path,
                    oldrev=old_rev,
                    newnode=new_node.kind,
                    newpath=new_path,
                    newrev=new_rev))
        subpool = Pool(self.pool)
        if new_node.isdir:
            editor = DiffChangeEditor()
            e_ptr, e_baton = delta.make_editor(editor, subpool())
            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())

            def authz_cb(root, path, pool):
                return 1

            text_deltas = 0  # as this is anyway re-done in Diff.py...
            entry_props = 0  # "... typically used only for working copy updates"
            repos.svn_repos_dir_delta(
                old_root,
                _to_svn(subpool(), self.scope, old_path),
                '',
                new_root,
                _to_svn(subpool(), self.scope, new_path),
                e_ptr,
                e_baton,
                authz_cb,
                text_deltas,
                1,  # directory
                entry_props,
                ignore_ancestry,
                subpool())
            for path, kind, change in editor.deltas:
                path = _from_svn(path)
                old_node = new_node = None
                if change != Changeset.ADD:
                    old_node = self.get_node(posixpath.join(old_path, path),
                                             old_rev)
                if change != Changeset.DELETE:
                    new_node = self.get_node(posixpath.join(new_path, path),
                                             new_rev)
                else:
                    kind = _kindmap[fs.check_path(
                        old_root, _to_svn(subpool(), self.scope,
                                          old_node.path), subpool())]
                yield (old_node, new_node, kind, change)
        else:
            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
            if fs.contents_changed(old_root,
                                   _to_svn(subpool(), self.scope, old_path),
                                   new_root,
                                   _to_svn(subpool(), self.scope, new_path),
                                   subpool()):
                yield (old_node, new_node, Node.FILE, Changeset.EDIT)
Exemple #43
0
    def get_changes(self):
        pool = Pool(self.pool)
        tmp = Pool(pool)
        root = fs.revision_root(self.fs_ptr, self.rev, pool())
        editor = repos.RevisionChangeCollector(self.fs_ptr, self.rev, pool())
        e_ptr, e_baton = delta.make_editor(editor, pool())
        repos.svn_repos_replay(root, e_ptr, e_baton, pool())

        idx = 0
        copies, deletions = {}, {}
        changes = []
        revroots = {}
        for path, change in editor.changes.items():

            # Filtering on `path`
            if not (_is_path_within_scope(self.scope, path) and \
                    self.authz.has_permission(path)):
                continue

            path = change.path
            base_path = change.base_path
            base_rev = change.base_rev

            # Ensure `base_path` is within the scope
            if not (_is_path_within_scope(self.scope, base_path) and \
                    self.authz.has_permission(base_path)):
                base_path, base_rev = None, -1

            # Determine the action
            if not path:  # deletion
                if base_path:
                    if base_path in deletions:
                        continue  # duplicates on base_path are possible (#3778)
                    action = Changeset.DELETE
                    deletions[base_path] = idx
                elif self.scope:  # root property change
                    action = Changeset.EDIT
                else:  # deletion outside of scope, ignore
                    continue
            elif change.added or not base_path:  # add or copy
                action = Changeset.ADD
                if base_path and base_rev:
                    action = Changeset.COPY
                    copies[base_path] = idx
            else:
                action = Changeset.EDIT
                # identify the most interesting base_path/base_rev
                # in terms of last changed information (see r2562)
                if revroots.has_key(base_rev):
                    b_root = revroots[base_rev]
                else:
                    b_root = fs.revision_root(self.fs_ptr, base_rev, pool())
                    revroots[base_rev] = b_root
                tmp.clear()
                cbase_path = fs.node_created_path(b_root, base_path, tmp())
                cbase_rev = fs.node_created_rev(b_root, base_path, tmp())
                # give up if the created path is outside the scope
                if _is_path_within_scope(self.scope, cbase_path):
                    base_path, base_rev = cbase_path, cbase_rev

            kind = _kindmap[change.item_kind]
            path = _path_within_scope(self.scope, _from_svn(path or base_path))
            base_path = _path_within_scope(self.scope, _from_svn(base_path))
            changes.append([path, kind, action, base_path, base_rev])
            idx += 1

        moves = []
        for k, v in copies.items():
            if k in deletions:
                changes[v][2] = Changeset.MOVE
                moves.append(deletions[k])
        offset = 0
        moves.sort()
        for i in moves:
            del changes[i - offset]
            offset += 1

        changes.sort()
        for change in changes:
            yield tuple(change)
Exemple #44
0
    def get_changes(self,
                    old_path,
                    old_rev,
                    new_path,
                    new_rev,
                    ignore_ancestry=0):
        old_node = new_node = None
        old_rev = self.normalize_rev(old_rev)
        new_rev = self.normalize_rev(new_rev)
        if self.has_node(old_path, old_rev):
            old_node = self.get_node(old_path, old_rev)
        else:
            raise NoSuchNode(
                old_path, old_rev, u'La base pour le calcul des '
                u'différences est invalide')
        if self.has_node(new_path, new_rev):
            new_node = self.get_node(new_path, new_rev)
        else:
            raise NoSuchNode(
                new_path, new_rev, u'La cible pour le calcul des '
                u'différences est invalide')
        if new_node.kind != old_node.kind:
            raise TracError(u'Erreur de calcul des différences: La base est un'
                            u'%s (%s en révision %s) '
                            u'et la cible est un %s (%s en révision %s).' \
                              % (old_node.kind, old_path, old_rev,
                                 new_node.kind, new_path, new_rev))
        subpool = Pool(self.pool)
        if new_node.isdir:
            editor = DiffChangeEditor()
            e_ptr, e_baton = delta.make_editor(editor, subpool())
            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())

            def authz_cb(root, path, pool):
                return 1

            text_deltas = 0  # as this is anyway re-done in Diff.py...
            entry_props = 0  # "... typically used only for working copy updates"
            repos.svn_repos_dir_delta(
                old_root,
                _to_svn(self.scope + old_path),
                '',
                new_root,
                _to_svn(self.scope + new_path),
                e_ptr,
                e_baton,
                authz_cb,
                text_deltas,
                1,  # directory
                entry_props,
                ignore_ancestry,
                subpool())
            for path, kind, change in editor.deltas:
                path = _from_svn(path)
                old_node = new_node = None
                if change != Changeset.ADD:
                    old_node = self.get_node(posixpath.join(old_path, path),
                                             old_rev)
                if change != Changeset.DELETE:
                    new_node = self.get_node(posixpath.join(new_path, path),
                                             new_rev)
                else:
                    kind = _kindmap[fs.check_path(
                        old_root, _to_svn(self.scope, old_node.path),
                        subpool())]
                yield (old_node, new_node, kind, change)
        else:
            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
            if fs.contents_changed(old_root, _to_svn(self.scope, old_path),
                                   new_root, _to_svn(self.scope, new_path),
                                   subpool()):
                yield (old_node, new_node, Node.FILE, Changeset.EDIT)
Exemple #45
0
    def get_changes(self):
        """Retrieve file changes for a given revision.

        (wraps ``repos.svn_repos_replay``)
        """
        pool = Pool(self.pool)
        tmp = Pool(pool)
        root = fs.revision_root(self.fs_ptr, self.rev, pool())
        editor = repos.RevisionChangeCollector(self.fs_ptr, self.rev, pool())
        e_ptr, e_baton = delta.make_editor(editor, pool())
        repos.svn_repos_replay(root, e_ptr, e_baton, pool())

        idx = 0
        copies, deletions = {}, {}
        changes = []
        revroots = {}
        for path_utf8, change in editor.changes.items():
            new_path = _from_svn(path_utf8)

            # Filtering on `path`
            if not _is_path_within_scope(self.scope, new_path):
                continue

            path_utf8 = change.path
            base_path_utf8 = change.base_path
            path = _from_svn(path_utf8)
            base_path = _from_svn(base_path_utf8)
            base_rev = change.base_rev
            change_action = getattr(change, 'action', None)

            # Ensure `base_path` is within the scope
            if not _is_path_within_scope(self.scope, base_path):
                base_path, base_rev = None, -1

            # Determine the action
            if not path and not new_path and self.scope == '/':
                action = Changeset.EDIT # root property change
            elif not path or (change_action is not None
                              and change_action == repos.CHANGE_ACTION_DELETE):
                if new_path:            # deletion
                    action = Changeset.DELETE
                    deletions[new_path.lstrip('/')] = idx
                else:                   # deletion outside of scope, ignore
                    continue
            elif change.added or not base_path: # add or copy
                action = Changeset.ADD
                if base_path and base_rev:
                    action = Changeset.COPY
                    copies[base_path.lstrip('/')] = idx
            else:
                action = Changeset.EDIT
                # identify the most interesting base_path/base_rev
                # in terms of last changed information (see r2562)
                if revroots.has_key(base_rev):
                    b_root = revroots[base_rev]
                else:
                    b_root = fs.revision_root(self.fs_ptr, base_rev, pool())
                    revroots[base_rev] = b_root
                tmp.clear()
                cbase_path_utf8 = fs.node_created_path(b_root, base_path_utf8,
                                                       tmp())
                cbase_path = _from_svn(cbase_path_utf8)
                cbase_rev = fs.node_created_rev(b_root, base_path_utf8, tmp())
                # give up if the created path is outside the scope
                if _is_path_within_scope(self.scope, cbase_path):
                    base_path, base_rev = cbase_path, cbase_rev

            kind = _kindmap[change.item_kind]
            path = _path_within_scope(self.scope, new_path or base_path)
            base_path = _path_within_scope(self.scope, base_path)
            changes.append([path, kind, action, base_path, base_rev])
            idx += 1

        moves = []
        # a MOVE is a COPY whose `base_path` corresponds to a `new_path`
        # which has been deleted
        for k, v in copies.items():
            if k in deletions:
                changes[v][2] = Changeset.MOVE
                moves.append(deletions[k])
        offset = 0
        moves.sort()
        for i in moves:
            del changes[i - offset]
            offset += 1

        changes.sort()
        for change in changes:
            yield tuple(change)
Exemple #46
0
def main(pool, repos_dir, rev, config_fp):
    # Construct a ChangeCollector to fetch our changes.
    fs_ptr = repos.svn_repos_fs(repos.svn_repos_open(repos_dir, pool))
    root = fs.revision_root(fs_ptr, rev, pool)

    # Get revision properties we need. (Subversion 1.2)
    # cc = repos.ChangeCollector(fs_ptr, root, pool)
    # props = cc.get_root_props()
    # author = str(props.get(core.SVN_PROP_REVISION_AUTHOR, ''))
    # log = str(props.get(core.SVN_PROP_REVISION_LOG, ''))

    # Get revision properties we need. (Subversion 1.1)
    cc = repos.RevisionChangeCollector(fs_ptr, rev, pool)
    author = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_AUTHOR, pool)
    log = fs.revision_prop(fs_ptr, rev, core.SVN_PROP_REVISION_LOG, pool)

    ### Do any Subversion-to-Scarab author mappings here ###

    # Example:
    #  _authors = {
    #   'miw':'mick',
    #   'lif':'lisefr',
    #   'gni':'gunleik',
    #   'man':'Maja',
    #   'ako':'konand',
    #   'hho':'helga',
    #   'hba':'hilde',
    #   'rgc':'rgc'
    #   }
    #  author = _authors.get(author, author)

    # Now build the comment.  First we start with some header
    # information about the revision, and a link to the ViewCVS revision
    # view.
    e_ptr, e_baton = delta.make_editor(cc, pool)
    repos.svn_repos_replay(root, e_ptr, e_baton, pool)
    comment = "%s %d:    %s/?view=rev&rev=%d\n" \
              % (MSG_SUBVERSION_COMMIT, rev, VIEWCVS_URL, rev)
    comment = comment + \
  """-------------------------------------------------------------------------
%s
-------------------------------------------------------------------------
""" % log

    # Next, we'll figure out which paths changed and use that
    # information to generate ViewCVS links.
    #  changes = cc.get_changes() # Subversion 1.2
    changes = cc.changes  # Subversion 1.1
    paths = changes.keys()
    #  paths.sort(lambda a, b: core.svn_path_compare_paths(a, b)) # Subversion 1.2
    for path in paths:
        change = changes[path]
        diff_url = ''
        action = None
        if not change.path:  ### Deleted (show the last living version)
            action = MSG_ACTION_DELETED
            diff_url = '%s/%s?view=auto&rev=%d' \
                       % (VIEWCVS_URL,
                          urllib.quote(change.base_path[1:]), change.base_rev)
        elif change.added:  ### Added
            if change.base_path and (change.base_rev !=
                                     -1):  ### (with history)
                action = MSG_ACTION_COPIED
                diff_url = '%s/%s?view=diff&rev=%d&p1=%s&r1=%d&p2=%s&r2=%d' \
                           % (VIEWCVS_URL,
                              urllib.quote(change.path), rev,
                              urllib.quote(change.base_path[1:]), change.base_rev,
                              urllib.quote(change.path), rev)
            else:  ### (without history, show new file)
                action = MSG_ACTION_ADDED
                diff_url = '%s/%s?view=auto&rev=%d' \
                           % (VIEWCVS_URL,
                              urllib.quote(change.path), rev)
        elif change.text_changed:  ### Modified
            action = MSG_ACTION_MODIFIED
            diff_url = '%s/%s?view=diff&rev=%d&p1=%s&r1=%d&p2=%s&r2=%d' \
                       % (VIEWCVS_URL,
                          urllib.quote(change.path), rev,
                          urllib.quote(change.base_path[1:]), change.base_rev,
                          urllib.quote(change.path), rev)
        if action:
            comment = comment + "%s: %s\n    %s\n" % (action, path, diff_url)

    # Connect to the xmlrpc server, and transmit our data.
    Server(SCARAB_XMLRPC_URL).simple.addComment(log, author, comment,
                                                DISABLE_EMAILS)
Exemple #47
0
def get_revision_info(svnrepos, rev):
  fsroot = svnrepos._getroot(rev)

  # Get the changes for the revision
  editor = repos.ChangeCollector(svnrepos.fs_ptr, fsroot, svnrepos.pool)
  e_ptr, e_baton = delta.make_editor(editor, svnrepos.pool)
  repos.svn_repos_replay(fsroot, e_ptr, e_baton, svnrepos.pool)
  changes = editor.get_changes()
  changedpaths = {}
  
  # Copy the Subversion changes into a new hash, converting them into
  # ChangedPath objects.
  for path in changes.keys():
    change = changes[path]
    if change.path:
      change.path = _cleanup_path(change.path)
    if change.base_path:
      change.base_path = _cleanup_path(change.base_path)
    is_copy = 0
    if not hasattr(change, 'action'): # new to subversion 1.4.0
      action = 'modified'
      if not change.path:
        action = 'deleted'
      elif change.added:
        action = 'added'
        replace_check_path = path
        if change.base_path and change.base_rev:
          replace_check_path = change.base_path
        if changedpaths.has_key(replace_check_path) \
           and changedpaths[replace_check_path].action == 'deleted':
          action = 'replaced'
    else:
      if change.action == repos.CHANGE_ACTION_ADD:
        action = 'added'
      elif change.action == repos.CHANGE_ACTION_DELETE:
        action = 'deleted'
      elif change.action == repos.CHANGE_ACTION_REPLACE:
        action = 'replaced'
      else:
        action = 'modified'
    if (action == 'added' or action == 'replaced') \
       and change.base_path \
       and change.base_rev:
      is_copy = 1
    if change.item_kind == core.svn_node_dir:
      pathtype = vclib.DIR
    elif change.item_kind == core.svn_node_file:
      pathtype = vclib.FILE
    else:
      pathtype = None
    changedpaths[path] = ChangedPath(path, pathtype, change.prop_changes,
                                     change.text_changed, change.base_path,
                                     change.base_rev, action, is_copy)

  # Actually, what we want is a sorted list of ChangedPath objects.
  change_items = changedpaths.values()
  change_items.sort(lambda a, b: _compare_paths(a.filename, b.filename))
  
  # Now get the revision property info.  Would use
  # editor.get_root_props(), but something is broken there...
  datestr, author, msg = _fs_rev_props(svnrepos.fs_ptr, rev, svnrepos.pool)
  date = _datestr_to_date(datestr, svnrepos.pool)

  return date, author, msg, change_items
Exemple #48
0
    def get_changes(self):
        pool = Pool(self.pool)
        tmp = Pool(pool)
        root = fs.revision_root(self.fs_ptr, self.rev, pool())
        editor = repos.RevisionChangeCollector(self.fs_ptr, self.rev, pool())
        e_ptr, e_baton = delta.make_editor(editor, pool())
        repos.svn_repos_replay(root, e_ptr, e_baton, pool())

        idx = 0
        copies, deletions = {}, {}
        changes = []
        revroots = {}
        for path, change in editor.changes.items():
            
            # Filtering on `path`
            if not (_is_path_within_scope(self.scope, path) and \
                    self.authz.has_permission(path)):
                continue

            path = change.path
            base_path = change.base_path
            base_rev = change.base_rev

            # Ensure `base_path` is within the scope
            if not (_is_path_within_scope(self.scope, base_path) and \
                    self.authz.has_permission(base_path)):
                base_path, base_rev = None, -1

            # Determine the action
            if not path:                # deletion
                if base_path:
                    if base_path in deletions:
                        continue # duplicates on base_path are possible (#3778)
                    action = Changeset.DELETE
                    deletions[base_path] = idx
                elif self.scope:        # root property change
                    action = Changeset.EDIT
                else:                   # deletion outside of scope, ignore
                    continue
            elif change.added or not base_path: # add or copy
                action = Changeset.ADD
                if base_path and base_rev:
                    action = Changeset.COPY
                    copies[base_path] = idx
            else:
                action = Changeset.EDIT
                # identify the most interesting base_path/base_rev
                # in terms of last changed information (see r2562)
                if revroots.has_key(base_rev):
                    b_root = revroots[base_rev]
                else:
                    b_root = fs.revision_root(self.fs_ptr, base_rev, pool())
                    revroots[base_rev] = b_root
                tmp.clear()
                cbase_path = fs.node_created_path(b_root, base_path, tmp())
                cbase_rev = fs.node_created_rev(b_root, base_path, tmp()) 
                # give up if the created path is outside the scope
                if _is_path_within_scope(self.scope, cbase_path):
                    base_path, base_rev = cbase_path, cbase_rev

            kind = _kindmap[change.item_kind]
            path = _path_within_scope(self.scope, _from_svn(path or base_path))
            base_path = _path_within_scope(self.scope, _from_svn(base_path))
            changes.append([path, kind, action, base_path, base_rev])
            idx += 1

        moves = []
        for k,v in copies.items():
            if k in deletions:
                changes[v][2] = Changeset.MOVE
                moves.append(deletions[k])
        offset = 0
        moves.sort()
        for i in moves:
            del changes[i - offset]
            offset += 1

        changes.sort()
        for change in changes:
            yield tuple(change)