Move cache message to directory creation.
[jelmer/subvertpy.git] / repository.py
index 8d7d2963a264894d0d6402b626bd2c8f99655468..2b0af5471abecf1c9d1a7e619bce4b8c917584fd 100644 (file)
@@ -16,8 +16,8 @@
 """Subversion repository access."""
 
 import bzrlib
-from bzrlib import osutils, ui
-from bzrlib.branch import BranchCheckResult
+from bzrlib import osutils, ui, urlutils
+from bzrlib.branch import Branch, BranchCheckResult
 from bzrlib.errors import (InvalidRevisionId, NoSuchRevision, NotBranchError, 
                            UninitializableFormat, UnrelatedBranches)
 from bzrlib.inventory import Inventory
@@ -26,7 +26,7 @@ from bzrlib.repository import Repository, RepositoryFormat
 from bzrlib.revisiontree import RevisionTree
 from bzrlib.revision import Revision, NULL_REVISION
 from bzrlib.transport import Transport, get_transport
-from bzrlib.trace import mutter
+from bzrlib.trace import info, mutter
 
 from svn.core import SubversionException, Pool
 import svn.core
@@ -39,176 +39,18 @@ import calendar
 from config import SvnRepositoryConfig
 import errors
 import logwalker
-from revids import (generate_svn_revision_id, parse_svn_revision_id, 
-                    MAPPING_VERSION, RevidMap)
+from mapping import (default_mapping, SVN_PROP_BZR_REVISION_ID, 
+                     SVN_PROP_BZR_REVISION_INFO, SVN_PROP_BZR_BRANCHING_SCHEME,
+                     SVN_PROP_BZR_ANCESTRY, SVN_PROP_BZR_FILEIDS,
+                     parse_revision_metadata, parse_revid_property)
+                      
+from revids import RevidMap
 from scheme import (BranchingScheme, ListBranchingScheme, 
                     parse_list_scheme_text, guess_scheme_from_history)
 from tree import SvnRevisionTree
-import time
 import urllib
 
-SVN_PROP_BZR_PREFIX = 'bzr:'
-SVN_PROP_BZR_ANCESTRY = 'bzr:ancestry:v%d-' % MAPPING_VERSION
-SVN_PROP_BZR_FILEIDS = 'bzr:file-ids'
-SVN_PROP_BZR_MERGE = 'bzr:merge'
 SVN_PROP_SVK_MERGE = 'svk:merge'
-SVN_PROP_BZR_REVISION_INFO = 'bzr:revision-info'
-SVN_PROP_BZR_REVISION_ID = 'bzr:revision-id:v%d-' % MAPPING_VERSION
-SVN_PROP_BZR_BRANCHING_SCHEME = 'bzr:branching-scheme'
-
-SVN_REVPROP_BZR_COMMITTER = 'bzr:committer'
-SVN_REVPROP_BZR_FILEIDS = 'bzr:file-ids'
-SVN_REVPROP_BZR_MERGE = 'bzr:merge'
-SVN_REVPROP_BZR_REVISION_ID = 'bzr:revision-id'
-SVN_REVPROP_BZR_REVPROP_PREFIX = 'bzr:revprop:'
-SVN_REVPROP_BZR_ROOT = 'bzr:root'
-SVN_REVPROP_BZR_SCHEME = 'bzr:scheme'
-SVN_REVPROP_BZR_SIGNATURE = 'bzr:gpg-signature'
-
-# The following two functions don't use day names (which can vary by 
-# locale) unlike the alternatives in bzrlib.timestamp
-
-def format_highres_date(t, offset=0):
-    """Format a date, such that it includes higher precision in the
-    seconds field.
-
-    :param t:   The local time in fractional seconds since the epoch
-    :type t: float
-    :param offset:  The timezone offset in integer seconds
-    :type offset: int
-    """
-    assert isinstance(t, float)
-
-    # This has to be formatted for "original" date, so that the
-    # revision XML entry will be reproduced faithfully.
-    if offset is None:
-        offset = 0
-    tt = time.gmtime(t + offset)
-
-    return (time.strftime("%Y-%m-%d %H:%M:%S", tt)
-            # Get the high-res seconds, but ignore the 0
-            + ('%.9f' % (t - int(t)))[1:]
-            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
-
-
-def unpack_highres_date(date):
-    """This takes the high-resolution date stamp, and
-    converts it back into the tuple (timestamp, timezone)
-    Where timestamp is in real UTC since epoch seconds, and timezone is an
-    integer number of seconds offset.
-
-    :param date: A date formated by format_highres_date
-    :type date: string
-    """
-    # skip day if applicable
-    if not date[0].isdigit():
-        space_loc = date.find(' ')
-        if space_loc == -1:
-            raise ValueError("No valid date: %r" % date)
-        date = date[space_loc+1:]
-    # Up until the first period is a datestamp that is generated
-    # as normal from time.strftime, so use time.strptime to
-    # parse it
-    dot_loc = date.find('.')
-    if dot_loc == -1:
-        raise ValueError(
-            'Date string does not contain high-precision seconds: %r' % date)
-    base_time = time.strptime(date[:dot_loc], "%Y-%m-%d %H:%M:%S")
-    fract_seconds, offset = date[dot_loc:].split()
-    fract_seconds = float(fract_seconds)
-
-    offset = int(offset)
-
-    hours = int(offset / 100)
-    minutes = (offset % 100)
-    seconds_offset = (hours * 3600) + (minutes * 60)
-
-    # time.mktime returns localtime, but calendar.timegm returns UTC time
-    timestamp = calendar.timegm(base_time)
-    timestamp -= seconds_offset
-    # Add back in the fractional seconds
-    timestamp += fract_seconds
-    return (timestamp, seconds_offset)
-
-
-def parse_merge_property(line):
-    """Parse a bzr:merge property value.
-
-    :param line: Line to parse
-    :return: List of revisions merged
-    """
-    if ' ' in line:
-        mutter('invalid revision id %r in merged property, skipping' % line)
-        return []
-
-    return filter(lambda x: x != "", line.split("\t"))
-
-
-def parse_revid_property(line):
-    """Parse a (revnum, revid) tuple as set in revision id properties.
-    :param line: line to parse
-    :return: tuple with (bzr_revno, revid)
-    """
-    assert not '\n' in line
-    try:
-        (revno, revid) = line.split(' ', 1)
-    except ValueError:
-        raise errors.InvalidPropertyValue(SVN_PROP_BZR_REVISION_ID, 
-                "missing space")
-    if revid == "":
-        raise errors.InvalidPropertyValue(SVN_PROP_BZR_REVISION_ID,
-                "empty revision id")
-    return (int(revno), revid)
-
-
-def parse_revision_metadata(text, rev):
-    """Parse a revision info text (as set in bzr:revision-info).
-
-    :param text: text to parse
-    :param rev: Revision object to apply read parameters to
-    """
-    in_properties = False
-    for l in text.splitlines():
-        try:
-            key, value = l.split(": ", 2)
-        except ValueError:
-            raise errors.InvalidPropertyValue(SVN_PROP_BZR_REVISION_INFO, 
-                    "Missing : in revision metadata")
-        if key == "committer":
-            rev.committer = str(value)
-        elif key == "timestamp":
-            (rev.timestamp, rev.timezone) = unpack_highres_date(value)
-        elif key == "properties":
-            in_properties = True
-        elif key[0] == "\t" and in_properties:
-            rev.properties[str(key[1:])] = str(value)
-        else:
-            raise errors.InvalidPropertyValue(SVN_PROP_BZR_REVISION_INFO, 
-                    "Invalid key %r" % key)
-
-
-def generate_revision_metadata(timestamp, timezone, committer, revprops):
-    """Generate revision metadata text for the specified revision 
-    properties.
-
-    :param timestamp: timestamp of the revision, in seconds since epoch
-    :param timezone: timezone, specified by offset from GMT in seconds
-    :param committer: name/email of the committer
-    :param revprops: dictionary with custom revision properties
-    :return: text with data to set bzr:revision-info to.
-    """
-    assert timestamp is None or isinstance(timestamp, float)
-    text = ""
-    if timestamp is not None:
-        text += "timestamp: %s\n" % format_highres_date(timestamp, timezone) 
-    if committer is not None:
-        text += "committer: %s\n" % committer
-    if revprops is not None and revprops != {}:
-        text += "properties: \n"
-        for k, v in sorted(revprops.items()):
-            text += "\t%s: %s\n" % (k, v)
-    return text
-
 
 def parse_svk_feature(feature):
     """Parse a svk feature identifier.
@@ -230,7 +72,8 @@ def revision_id_to_svk_feature(revid):
     :param revid: Revision id to convert.
     :return: Matching SVK feature identifier.
     """
-    (uuid, branch, revnum, _) = parse_svn_revision_id(revid)
+    assert isinstance(revid, str)
+    (uuid, branch, revnum, _) = default_mapping.parse_revision_id(revid)
     # TODO: What about renamed revisions? Should use 
     # repository.lookup_revision_id here.
     return "%s:/%s:%d" % (uuid, branch, revnum)
@@ -242,8 +85,8 @@ class SvnRepositoryFormat(RepositoryFormat):
     rich_root_data = True
 
     def __get_matchingbzrdir(self):
-        from format import SvnFormat
-        return SvnFormat()
+        from remote import SvnRemoteFormat
+        return SvnRemoteFormat()
 
     _matchingbzrdir = property(__get_matchingbzrdir)
 
@@ -260,6 +103,8 @@ class SvnRepositoryFormat(RepositoryFormat):
     def check_conversion_target(self, target_repo_format):
         return target_repo_format.rich_root_data
 
+CACHE_DB_VERSION = 3
+
 cachedbs = {}
 
 class SvnRepository(Repository):
@@ -287,12 +132,10 @@ class SvnRepository(Repository):
         self._serializer = None
         self.dir_cache = {}
         self.pool = Pool()
-        self.config = SvnRepositoryConfig(self.uuid)
-        self.config.add_location(self.base)
-        self._revids_seen = {}
+        self.get_config().add_location(self.base)
         cache_dir = self.create_cache_dir()
         cachedir_transport = get_transport(cache_dir)
-        cache_file = os.path.join(cache_dir, 'cache-v%d' % MAPPING_VERSION)
+        cache_file = os.path.join(cache_dir, 'cache-v%d' % CACHE_DB_VERSION)
         if not cachedbs.has_key(cache_file):
             cachedbs[cache_file] = sqlite3.connect(cache_file)
         self.cachedb = cachedbs[cache_file]
@@ -319,28 +162,46 @@ class SvnRepository(Repository):
     def get_transaction(self):
         raise NotImplementedError(self.get_transaction)
 
+    def get_stored_scheme(self):
+        """Retrieve the stored branching scheme, either in the repository 
+        or in the configuration file.
+        """
+        scheme = self.get_config().get_branching_scheme()
+        if scheme is not None:
+            return (scheme, self.get_config().branching_scheme_is_mandatory())
+
+        last_revnum = self.transport.get_latest_revnum()
+        scheme = self._get_property_scheme(last_revnum)
+        if scheme is not None:
+            return (scheme, True)
+
+        return (None, False)
+
     def get_scheme(self):
         """Determine the branching scheme to use for this repository.
 
         :return: Branching scheme.
         """
+        # First, try to use the branching scheme we already know
         if self._scheme is not None:
             return self._scheme
 
-        scheme = self.config.get_branching_scheme()
-        if scheme is not None:
+        (scheme, mandatory) = self.get_stored_scheme()
+        if mandatory:
             self._scheme = scheme
             return scheme
 
-        last_revnum = self.transport.get_latest_revnum()
-        scheme = self._get_property_scheme(last_revnum)
         if scheme is not None:
-            self.set_branching_scheme(scheme)
-            return scheme
+            if (self._hinted_branch_path is None or 
+                scheme.is_branch(self._hinted_branch_path)):
+                self._scheme = scheme
+                return scheme
 
+        last_revnum = self.transport.get_latest_revnum()
         self.set_branching_scheme(
             self._guess_scheme(last_revnum, self._hinted_branch_path),
-            store=(last_revnum > 20))
+            store=(last_revnum > 20),
+            mandatory=False)
 
         return self._scheme
 
@@ -357,7 +218,7 @@ class SvnRepository(Repository):
         def done(revision, date, author):
             pass
         editor = self.transport.get_commit_editor(
-                "Updating branching scheme for Bazaar.",
+                {svn.core.SVN_PROP_REVISION_LOG: "Updating branching scheme for Bazaar."},
                 done, None, False)
         root = editor.open_root(-1)
         editor.change_dir_prop(root, SVN_PROP_BZR_BRANCHING_SCHEME, 
@@ -372,10 +233,11 @@ class SvnRepository(Repository):
         mutter("Guessed branching scheme: %r" % scheme)
         return scheme
 
-    def set_branching_scheme(self, scheme, store=True):
+    def set_branching_scheme(self, scheme, store=True, mandatory=False):
         self._scheme = scheme
         if store:
-            self.config.set_branching_scheme(str(scheme))
+            self.get_config().set_branching_scheme(str(scheme), 
+                                                   mandatory=mandatory)
 
     def _warn_if_deprecated(self):
         # This class isn't deprecated
@@ -389,6 +251,7 @@ class SvnRepository(Repository):
         cache_dir = create_cache_dir()
         dir = os.path.join(cache_dir, self.uuid)
         if not os.path.exists(dir):
+            info("Initialising Subversion metadata cache in %s" % dir)
             os.mkdir(dir)
         return dir
 
@@ -521,7 +384,7 @@ class SvnRepository(Repository):
         :return: Revision id of the left-hand-side parent or None if 
                   this is the first revision
         """
-        assert isinstance(path, basestring)
+        assert isinstance(path, str)
         assert isinstance(revnum, int)
 
         if not scheme.is_branch(path) and \
@@ -595,6 +458,22 @@ class SvnRepository(Repository):
             if revid is not None:
                 yield revid
 
+    def get_parents(self, revids):
+        parents_list = []
+        for revision_id in revids:
+            if revision_id == NULL_REVISION:
+                parents = []
+            else:
+                try:
+                    parents = self.revision_parents(revision_id)
+                except NoSuchRevision:
+                    parents = None
+                else:
+                    if len(parents) == 0:
+                        parents = [NULL_REVISION]
+            parents_list.append(parents)
+        return parents_list
+
     def revision_parents(self, revision_id, bzr_merges=None, svk_merges=None):
         """See Repository.revision_parents()."""
         parent_ids = []
@@ -622,7 +501,7 @@ class SvnRepository(Repository):
 
     def get_revision(self, revision_id):
         """See Repository.get_revision."""
-        if not revision_id or not isinstance(revision_id, basestring):
+        if not revision_id or not isinstance(revision_id, str):
             raise InvalidRevisionId(revision_id=revision_id, branch=self)
 
         (path, revnum, _) = self.lookup_revision_id(revision_id)
@@ -630,14 +509,28 @@ class SvnRepository(Repository):
         parent_ids = self.revision_parents(revision_id)
 
         # Commit SVN revision properties to a Revision object
-        rev = Revision(revision_id=revision_id, parent_ids=parent_ids)
+        class LazySvnRevision(Revision):
+            inventory_sha1 = property(lambda rev: self.get_inventory_sha1(rev.revision_id))
+
+        rev = LazySvnRevision(revision_id=revision_id, parent_ids=parent_ids)
+
+        svn_revprops = self.transport.revprop_list(revnum)
 
-        (rev.committer, rev.message, date) = self._log.get_revision_info(revnum)
-        if rev.committer is None:
+        if svn_revprops.has_key(svn.core.SVN_PROP_REVISION_AUTHOR):
+            rev.committer = svn_revprops[svn.core.SVN_PROP_REVISION_AUTHOR]
+        else:
             rev.committer = ""
 
-        if date is not None:
-            rev.timestamp = 1.0 * svn.core.secs_from_timestr(date, None)
+        rev.message = svn_revprops.get(svn.core.SVN_PROP_REVISION_LOG)
+
+        if rev.message:
+            try:
+                rev.message = rev.message.decode("utf-8")
+            except UnicodeDecodeError:
+                pass
+
+        if svn_revprops.has_key(svn.core.SVN_PROP_REVISION_DATE):
+            rev.timestamp = 1.0 * svn.core.secs_from_timestr(svn_revprops[svn.core.SVN_PROP_REVISION_DATE], None)
         else:
             rev.timestamp = 0.0 # FIXME: Obtain repository creation time
         rev.timezone = None
@@ -646,9 +539,6 @@ class SvnRepository(Repository):
                 self.branchprop_list.get_property(path, revnum, 
                      SVN_PROP_BZR_REVISION_INFO, ""), rev)
 
-        rev.inventory_sha1 = property(
-            lambda: self.get_inventory_sha1(revision_id))
-
         return rev
 
     def get_revisions(self, revision_ids):
@@ -681,8 +571,8 @@ class SvnRepository(Repository):
                 SVN_PROP_BZR_REVISION_ID+str(scheme)).strip("\n")
         # Or generate it
         if line == "":
-            revid = generate_svn_revision_id(self.uuid, revnum, path, 
-                                             scheme)
+            revid = default_mapping.generate_revision_id(
+                        self.uuid, revnum, path, scheme)
         else:
             try:
                 (bzr_revno, revid) = parse_revid_property(line)
@@ -690,8 +580,8 @@ class SvnRepository(Repository):
                         scheme, bzr_revno)
             except errors.InvalidPropertyValue, e:
                 mutter(str(e))
-                revid = generate_svn_revision_id(self.uuid, revnum, path
-                                                 scheme)
+                revid = default_mapping.generate_revision_id(self.uuid
+                            revnum, path, scheme)
                 self.revmap.insert_revid(revid, path, revnum, revnum, 
                         scheme)
 
@@ -707,13 +597,14 @@ class SvnRepository(Repository):
         :return: Tuple with branch path, revision number and scheme.
         """
         def get_scheme(name):
-            assert isinstance(name, basestring)
+            assert isinstance(name, str)
             return BranchingScheme.find_scheme(name)
 
         # Try a simple parse
         try:
-            (uuid, branch_path, revnum, schemen) = parse_svn_revision_id(revid)
+            (uuid, branch_path, revnum, schemen) = default_mapping.parse_revision_id(revid)
             assert isinstance(branch_path, str)
+            assert isinstance(schemen, str)
             if uuid == self.uuid:
                 return (branch_path, revnum, get_scheme(schemen))
             # If the UUID doesn't match, this may still be a valid revision
@@ -727,6 +618,7 @@ class SvnRepository(Repository):
             (branch_path, min_revnum, max_revnum, \
                     scheme) = self.revmap.lookup_revid(revid)
             assert isinstance(branch_path, str)
+            assert isinstance(scheme, str)
             # Entry already complete?
             if min_revnum == max_revnum:
                 return (branch_path, min_revnum, get_scheme(scheme))
@@ -735,22 +627,28 @@ class SvnRepository(Repository):
             if scheme is None:
                 scheme = self.get_scheme()
             last_revnum = self.transport.get_latest_revnum()
-            if (self._revids_seen.has_key(str(scheme)) and 
-                last_revnum <= self._revids_seen[str(scheme)]):
+            if (last_revnum <= self.revmap.last_revnum_checked(str(scheme))):
                 # All revision ids in this repository for the current 
                 # scheme have already been discovered. No need to 
                 # check again.
                 raise e
             found = False
-            for (branch, revno, _) in self.find_branches(scheme, last_revnum):
+            for (branch, revno, _) in self.find_branchpaths(scheme, 
+                    self.revmap.last_revnum_checked(str(scheme)),
+                    last_revnum):
+                assert isinstance(branch, str)
+                assert isinstance(revno, int)
                 # Look at their bzr:revision-id-vX
                 revids = []
-                for line in self.branchprop_list.get_property(branch, revno, 
-                        SVN_PROP_BZR_REVISION_ID+str(scheme), "").splitlines():
-                    try:
-                        revids.append(parse_revid_property(line))
-                    except errors.InvalidPropertyValue, ie:
-                        mutter(str(ie))
+                try:
+                    for line in self.branchprop_list.get_property(branch, revno, 
+                            SVN_PROP_BZR_REVISION_ID+str(scheme), "").splitlines():
+                        try:
+                            revids.append(parse_revid_property(line))
+                        except errors.InvalidPropertyValue, ie:
+                            mutter(str(ie))
+                except SubversionException, (_, svn.core.SVN_ERR_FS_NOT_DIRECTORY):
+                    continue
 
                 # If there are any new entries that are not yet in the cache, 
                 # add them
@@ -759,14 +657,11 @@ class SvnRepository(Repository):
                         found = True
                     self.revmap.insert_revid(entry_revid, branch, 0, revno, 
                             str(scheme), entry_revno)
-
-                if found:
-                    break
                 
+            # We've added all the revision ids for this scheme in the repository,
+            # so no need to check again unless new revisions got added
+            self.revmap.set_last_revnum_checked(str(scheme), last_revnum)
             if not found:
-                # We've added all the revision ids for this scheme in the repository,
-                # so no need to check again unless new revisions got added
-                self._revids_seen[str(scheme)] = last_revnum
                 raise e
             (branch_path, min_revnum, max_revnum, scheme) = self.revmap.lookup_revid(revid)
             assert isinstance(branch_path, str)
@@ -774,7 +669,7 @@ class SvnRepository(Repository):
         # Find the branch property between min_revnum and max_revnum that 
         # added revid
         for (bp, rev) in self.follow_branch(branch_path, max_revnum, 
-                                            get_scheme(scheme)):
+                                            get_scheme(str(scheme))):
             try:
                 (entry_revno, entry_revid) = parse_revid_property(
                  self.branchprop_list.get_property_diff(bp, rev, 
@@ -920,7 +815,7 @@ class SvnRepository(Repository):
 
         for (bp, paths, revnum) in self._log.follow_path(branch_path, revnum):
             assert revnum > 0 or bp == ""
-            assert scheme.is_branch(bp) or schee.is_tag(bp)
+            assert scheme.is_branch(bp) or scheme.is_tag(bp)
             # Remove non-bp paths from paths
             for p in paths.keys():
                 if not p.startswith(bp+"/") and bp != p and bp != "":
@@ -945,6 +840,9 @@ class SvnRepository(Repository):
                      
             yield (bp, paths, revnum)
 
+    def get_config(self):
+        return SvnRepositoryConfig(self.uuid)
+
     def has_signature_for_revision_id(self, revision_id):
         """Check whether a signature exists for a particular revision id.
 
@@ -1002,15 +900,39 @@ class SvnRepository(Repository):
 
         return self._ancestry
 
-    def find_branches(self, scheme, revnum=None):
-        """Find all branches that were changed in the specified revision number.
+    def find_branches(self, using=False):
+        """Find branches underneath this repository.
+
+        This will include branches inside other branches.
+
+        :param using: If True, list only branches using this repository.
+        """
+        # All branches use this repository, so the using argument can be 
+        # ignored.
+        scheme = self.get_scheme()
+
+        existing_branches = [bp for (bp, revnum, _) in 
+                filter(lambda (bp, rev, exists): exists,
+                       self.find_branchpaths(scheme))]
+
+        branches = []
+        for bp in existing_branches:
+            try:
+                branches.append(Branch.open(urlutils.join(self.base, bp)))
+            except NotBranchError: # Skip non-directories
+                pass
+        return branches
+
+    def find_branchpaths(self, scheme, from_revnum=0, to_revnum=None):
+        """Find all branch paths that were changed in the specified revision 
+        range.
 
         :param revnum: Revision to search for branches.
         :return: iterator that returns tuples with (path, revision number, still exists). The revision number is the revision in which the branch last existed.
         """
         assert scheme is not None
-        if revnum is None:
-            revnum = self.transport.get_latest_revnum()
+        if to_revnum is None:
+            to_revnum = self.transport.get_latest_revnum()
 
         created_branches = {}
 
@@ -1018,16 +940,23 @@ class SvnRepository(Repository):
 
         pb = ui.ui_factory.nested_progress_bar()
         try:
-            for i in range(revnum+1):
-                pb.update("finding branches", i, revnum+1)
+            for i in range(from_revnum, to_revnum+1):
+                pb.update("finding branches", i, to_revnum+1)
                 paths = self._log.get_revision_paths(i)
                 for p in sorted(paths.keys()):
                     if scheme.is_branch(p) or scheme.is_tag(p):
-                        if paths[p][0] in ('R', 'D'):
+                        if paths[p][0] in ('R', 'D') and p in created_branches:
                             del created_branches[p]
-                            j = self._log.find_latest_change(p, i-1, 
-                                include_parents=True, include_children=True)
-                            ret.append((p, j, False))
+                            if paths[p][1]:
+                                prev_path = paths[p][1]
+                                prev_rev = paths[p][2]
+                            else:
+                                prev_path = p
+                                prev_rev = self._log.find_latest_change(p, 
+                                    i-1, include_parents=True, 
+                                    include_children=True)
+                            assert isinstance(prev_rev, int)
+                            ret.append((prev_path, prev_rev, False))
 
                         if paths[p][0] in ('A', 'R'): 
                             created_branches[p] = i
@@ -1036,32 +965,37 @@ class SvnRepository(Repository):
                         if paths[p][0] in ('R', 'D'):
                             k = created_branches.keys()
                             for c in k:
-                                if c.startswith(p+"/"):
+                                if c.startswith(p+"/") and c in created_branches:
                                     del created_branches[c] 
                                     j = self._log.find_latest_change(c, i-1, 
                                             include_parents=True, 
                                             include_children=True)
+                                    assert isinstance(j, int)
                                     ret.append((c, j, False))
                         if paths[p][0] in ('A', 'R'):
                             parents = [p]
                             while parents:
                                 p = parents.pop()
-                                for c in self.transport.get_dir(p, i)[0].keys():
-                                    n = p+"/"+c
-                                    if scheme.is_branch(n) or scheme.is_tag(n):
-                                        created_branches[n] = i
-                                    elif (scheme.is_branch_parent(n) or 
-                                          scheme.is_tag_parent(n)):
-                                        parents.append(n)
+                                try:
+                                    for c in self.transport.get_dir(p, i)[0].keys():
+                                        n = p+"/"+c
+                                        if scheme.is_branch(n) or scheme.is_tag(n):
+                                            created_branches[n] = i
+                                        elif (scheme.is_branch_parent(n) or 
+                                              scheme.is_tag_parent(n)):
+                                            parents.append(n)
+                                except SubversionException, (_, svn.core.SVN_ERR_FS_NOT_DIRECTORY):
+                                    pass
         finally:
             pb.finished()
 
         for p in created_branches:
-            j = self._log.find_latest_change(p, revnum, 
+            j = self._log.find_latest_change(p, to_revnum, 
                                              include_parents=True,
                                              include_children=True)
             if j is None:
                 j = created_branches[p]
+            assert isinstance(j, int)
             ret.append((p, j, True))
 
         return ret