diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py
--- a/trac/versioncontrol/api.py
+++ b/trac/versioncontrol/api.py
@@ -230,7 +230,7 @@
         return []
     
     def get_changeset(self, rev):
-        """Retrieve a Changeset corresponding to the  given revision `rev`."""
+        """Retrieve a Changeset corresponding to the given revision `rev`."""
         raise NotImplementedError
 
     def get_changesets(self, start, stop):
diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
--- a/trac/versioncontrol/cache.py
+++ b/trac/versioncontrol/cache.py
@@ -19,6 +19,7 @@
 from datetime import datetime
 
 from trac.core import TracError
+from trac.util.compat import any
 from trac.util.datefmt import utc, to_timestamp
 from trac.util.translation import _
 from trac.versioncontrol import Changeset, Node, Repository, Authorizer, \
@@ -56,8 +57,11 @@
             yield category, name, path, rev
 
     def get_changeset(self, rev):
-        return CachedChangeset(self.repos, self.repos.normalize_rev(rev),
+        cset = CachedChangeset(self.repos, self.repos.normalize_rev(rev),
                                self.getdb, self.authz)
+        if rev == 0 or any(cset.get_changes()):
+            return cset
+        raise NoSuchChangeset(rev)
 
     def get_changesets(self, start, stop):
         db = self.getdb()
@@ -68,10 +72,11 @@
                        (to_timestamp(start), to_timestamp(stop)))
         for rev, in cursor:
             try:
-                if self.authz.has_permission_for_changeset(rev):
-                    yield self.get_changeset(rev)
+                yield self.get_changeset(rev)
             except NoSuchChangeset:
-                pass # skip changesets currently being resync'ed
+                # Skip unauthorized changesets and changesets currently being
+                # resync'ed
+                pass
 
     def sync_changeset(self, rev):
         cset = self.repos.get_changeset(rev)
@@ -270,7 +275,7 @@
             args.append(path)
             sql += " OR "
             # changes on path children
-            sql += "path "+db.like()
+            sql += "path " + db.like()
             args.append(db.like_escape(path+'/') + '%')
             sql += " OR "
             # deletion of path ancestors
@@ -282,12 +287,13 @@
             sql += ")"
 
         sql += " ORDER BY " + db.cast('rev', 'int') + \
-                (direction == '<' and " DESC" or "") + " LIMIT 1"
+                (direction == '<' and " DESC" or "")
         
         cursor = db.cursor()
         cursor.execute(sql, args)
         for rev, in cursor:
-            return rev
+            if self.authz.has_permission_for_changeset(rev):
+                return rev
 
     def rev_older_than(self, rev1, rev2):
         return self.repos.rev_older_than(rev1, rev2)
diff --git a/trac/versioncontrol/svn_authz.py b/trac/versioncontrol/svn_authz.py
--- a/trac/versioncontrol/svn_authz.py
+++ b/trac/versioncontrol/svn_authz.py
@@ -20,7 +20,7 @@
 
 from trac.config import Option
 from trac.core import *
-from trac.versioncontrol import Authorizer
+from trac.versioncontrol import Authorizer, NoSuchChangeset
 
 
 class SvnAuthzOptions(Component):
@@ -105,12 +105,11 @@
         return 0
 
     def has_permission_for_changeset(self, rev):
-        changeset = self.repos.get_changeset(rev)
-        for change in changeset.get_changes():
-            # the repository checks permissions for each change, so just check
-            # if any changes can be accessed
+        try:
+            changeset = self.repos.get_changeset(rev)
             return 1
-        return 0
+        except NoSuchChangeset:
+            return 0
 
     # Internal API
 
diff --git a/trac/versioncontrol/svn_fs.py b/trac/versioncontrol/svn_fs.py
--- a/trac/versioncontrol/svn_fs.py
+++ b/trac/versioncontrol/svn_fs.py
@@ -61,6 +61,7 @@
 from trac.versioncontrol.svn_authz import SubversionAuthorizer
 from trac.versioncontrol.web_ui.browser import IPropertyRenderer
 from trac.util import sorted, embedded_numbers, reversed
+from trac.util.compat import any
 from trac.util.text import exception_to_unicode, to_unicode
 from trac.util.translation import _
 from trac.util.datefmt import utc
@@ -506,8 +507,26 @@
 
     def get_changeset(self, rev):
         rev = self.normalize_rev(rev)
-        return SubversionChangeset(rev, self.authz, self.scope,
+        cset = SubversionChangeset(rev, self.authz, self.scope,
                                    self.fs_ptr, self.pool)
+        if rev == 0 or any(cset.get_changes()):
+            return cset
+        raise NoSuchChangeset(rev)
+
+    def get_changesets(self, start, stop):
+        # Overrides Repository.get_changesets() to avoid calling
+        # get_changeset() twice for every changeset
+        rev = self.youngest_rev
+        while rev:
+            try:
+                chgset = self.get_changeset(rev)
+                if chgset.date < start:
+                    return
+                if chgset.date < stop:
+                    yield chgset
+            except NoSuchChangeset:
+                pass
+            rev = self.previous_rev(rev)
 
     def get_node(self, path, rev=None):
         path = path or ''
@@ -544,6 +563,8 @@
             tmp1, tmp2 = tmp2, tmp1
             if history_ptr:
                 path_utf8, rev = fs.history_location(history_ptr, tmp2())
+                if not self.authz.has_permission_for_changeset(rev):
+                    continue
                 tmp2.clear()
                 if rev < end:
                     break
@@ -560,7 +581,6 @@
                 return prev
         return None
     
-
     def get_oldest_rev(self):
         if self.oldest is None:
             self.oldest = 1

