Merge 0.4.
[jelmer/subvertpy.git] / logwalker.py
index 1f4ab1168aac7a4b15df6c34d152092e1ed5c002..5c2e93be2e362c0bba202ea79c9fcd437c6ba0f8 100644 (file)
@@ -20,15 +20,14 @@ from bzrlib.errors import NoSuchRevision
 from bzrlib.trace import mutter
 import bzrlib.ui as ui
 
-from svn.core import SubversionException, Pool
-from transport import SvnRaTransport
-import svn.core
+from bzrlib.plugins.svn.core import SubversionException
+from bzrlib.plugins.svn.transport import SvnRaTransport
+from bzrlib.plugins.svn import core
 
 from bzrlib.plugins.svn import changes, core
 from bzrlib.plugins.svn.cache import CacheTable
 from bzrlib.plugins.svn.errors import ERR_FS_NO_SUCH_REVISION, ERR_FS_NOT_FOUND
-
-LOG_CHUNK_LIMIT = 0
+from bzrlib.plugins.svn.ra import DIRENT_KIND
 
 class lazy_dict(object):
     def __init__(self, initial, create_fn, *args):
@@ -189,6 +188,7 @@ class CachingLogWalker(CacheTable):
             revprops = lazy_dict({}, self._transport.revprop_list, revnum)
 
             if changes.changes_path(revpaths, path, True):
+                assert isinstance(revnum, int)
                 yield (revpaths, revnum, revprops)
                 i += 1
                 if limit != 0 and i == limit:
@@ -197,7 +197,11 @@ class CachingLogWalker(CacheTable):
             if next is None:
                 break
 
+            assert (ascending and next[1] > revnum) or \
+                   (not ascending and next[1] < revnum)
             (path, revnum) = next
+            assert isinstance(path, str)
+            assert isinstance(revnum, int)
 
     def get_previous(self, path, revnum):
         """Return path,revnum pair specified pair was derived from.
@@ -251,36 +255,40 @@ class CachingLogWalker(CacheTable):
 
         :param to_revnum: End of range to fetch information for
         """
+        assert isinstance(self.saved_revnum, int)
         if to_revnum <= self.saved_revnum:
             return
         latest_revnum = self.actual._transport.get_latest_revnum()
+        assert isinstance(latest_revnum, int)
         to_revnum = max(latest_revnum, to_revnum)
 
         pb = ui.ui_factory.nested_progress_bar()
 
         try:
+            def update_db(orig_paths, revision, revprops):
+                assert isinstance(orig_paths, dict) or orig_paths is None
+                assert isinstance(revision, int)
+                assert isinstance(revprops, dict)
+                pb.update('fetching svn revision info', revision, to_revnum)
+                if orig_paths is None:
+                    orig_paths = {}
+                for p in orig_paths:
+                    (action, copyfrom_path, copyfrom_rev) = orig_paths[p]
+
+                    if copyfrom_path:
+                        copyfrom_path = copyfrom_path.strip("/")
+                    self.cachedb.execute(
+                         "replace into changed_path (rev, path, action, copyfrom_path, copyfrom_rev) values (?, ?, ?, ?, ?)", 
+                         (revision, p.strip("/"), action, copyfrom_path, copyfrom_rev))
+
+                self.saved_revnum = revision
+                if self.saved_revnum % 1000 == 0:
+                    self.cachedb.commit()
+
             try:
-                while self.saved_revnum < to_revnum:
-                    for (orig_paths, revision, revprops) in self.actual._transport.iter_log(None, self.saved_revnum, 
-                                             to_revnum, self.actual._limit, True, 
-                                             True, []):
-                        pb.update('fetching svn revision info', revision, to_revnum)
-                        if orig_paths is None:
-                            orig_paths = {}
-                        for p in orig_paths:
-                            copyfrom_path = orig_paths[p].copyfrom_path
-                            if copyfrom_path is not None:
-                                copyfrom_path = copyfrom_path.strip("/")
-
-                            self.cachedb.execute(
-                                 "replace into changed_path (rev, path, action, copyfrom_path, copyfrom_rev) values (?, ?, ?, ?, ?)", 
-                                 (revision, p.strip("/"), orig_paths[p].action, copyfrom_path, orig_paths[p].copyfrom_rev))
-                            # Work around nasty memory leak in Subversion
-                            orig_paths[p]._parent_pool.destroy()
-
-                        self.saved_revnum = revision
-                        if self.saved_revnum % 1000 == 0:
-                            self.cachedb.commit()
+                assert isinstance(self.saved_revnum, int)
+                assert isinstance(to_revnum, int)
+                self.actual._transport.get_log(update_db, None, self.saved_revnum, to_revnum, 0, True, True, [])
             finally:
                 pb.finished()
         except SubversionException, (_, num):
@@ -294,12 +302,12 @@ class CachingLogWalker(CacheTable):
 def struct_revpaths_to_tuples(changed_paths):
     assert isinstance(changed_paths, dict)
     revpaths = {}
-    for k,v in changed_paths.items():
-        if v.copyfrom_path is None:
+    for k,(action, copyfrom_path, copyfrom_rev) in changed_paths.items():
+        if copyfrom_path is None:
             copyfrom_path = None
         else:
-            copyfrom_path = v.copyfrom_path.strip("/")
-        revpaths[k.strip("/")] = (v.action, copyfrom_path, v.copyfrom_rev)
+            copyfrom_path = copyfrom_path.strip("/")
+        revpaths[k.strip("/")] = (action, copyfrom_path, copyfrom_rev)
     return revpaths
 
 
@@ -314,11 +322,6 @@ class LogWalker(object):
 
         self._transport = transport
 
-        if limit is not None:
-            self._limit = limit
-        else:
-            self._limit = LOG_CHUNK_LIMIT
-
     def find_latest_change(self, path, revnum):
         """Find latest revision that touched path.
 
@@ -395,67 +398,20 @@ class LogWalker(object):
         assert isinstance(path, str), "invalid path"
         path = path.strip("/")
         conn = self._transport.connections.get(self._transport.get_svn_repos_root())
+        results = []
+        unchecked_dirs = set([path])
         try:
-            ft = conn.check_path(path, revnum)
-            if ft == core.NODE_FILE:
-                return []
-            assert ft == core.NODE_DIR
-        finally:
-            self._transport.connections.add(conn)
-
-        class TreeLister(svn.delta.Editor):
-            def __init__(self, base):
-                self.files = []
-                self.base = base
-
-            def set_target_revision(self, revnum):
-                """See Editor.set_target_revision()."""
-                pass
-
-            def open_root(self, revnum, baton):
-                """See Editor.open_root()."""
-                return path
-
-            def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revnum, pool):
-                """See Editor.add_directory()."""
-                self.files.append(urlutils.join(self.base, path))
-                return path
-
-            def change_dir_prop(self, id, name, value, pool):
-                pass
-
-            def change_file_prop(self, id, name, value, pool):
-                pass
-
-            def add_file(self, path, parent_id, copyfrom_path, copyfrom_revnum, baton):
-                self.files.append(urlutils.join(self.base, path))
-                return path
-
-            def close_dir(self, id):
-                pass
-
-            def close_file(self, path, checksum):
-                pass
-
-            def close_edit(self):
-                pass
-
-            def abort_edit(self):
-                pass
-
-            def apply_textdelta(self, file_id, base_checksum):
-                pass
-
-        pool = Pool()
-        editor = TreeLister(path)
-        try:
-            conn = self._transport.connections.get(urlutils.join(self._transport.get_svn_repos_root(), path))
-            reporter = conn.do_update(revnum, True, editor, pool)
-            reporter.set_path("", revnum, True, None, pool)
-            reporter.finish_report(pool)
+            while len(unchecked_dirs) > 0:
+                nextp = unchecked_dirs.pop()
+                dirents = conn.get_dir(nextp, revnum, DIRENT_KIND)[0]
+                for k,v in dirents.items():
+                    childp = urlutils.join(nextp, k)
+                    if v['kind'] == core.NODE_DIR:
+                        unchecked_dirs.add(childp)
+                    results.append(childp)
         finally:
             self._transport.connections.add(conn)
-        return editor.files
+        return results
 
     def get_previous(self, path, revnum):
         """Return path,revnum pair specified pair was derived from.