diff --git a/trac/admin/console.py b/trac/admin/console.py
--- a/trac/admin/console.py
+++ b/trac/admin/console.py
@@ -50,7 +50,7 @@
         try:
             import readline
             delims = readline.get_completer_delims()
-            for c in '-/':
+            for c in '-/()':
                 delims = delims.replace(c, '')
             readline.set_completer_delims(delims)
         except ImportError:
diff --git a/trac/admin/tests/console-tests.txt b/trac/admin/tests/console-tests.txt
--- a/trac/admin/tests/console-tests.txt
+++ b/trac/admin/tests/console-tests.txt
@@ -31,12 +31,13 @@
 priority list        Show possible ticket priorities
 priority order       Move a priority value up or down in the list
 priority remove      Remove a priority value
+repository notify    Notify trac about repository events
+repository resync    Re-synchronize trac with repositories
 resolution add       Add a resolution value option
 resolution change    Change a resolution value
 resolution list      Show possible ticket resolutions
 resolution order     Move a resolution value up or down in the list
 resolution remove    Remove a resolution value
-resync               Re-synchronize trac with the repository
 severity add         Add a severity value option
 severity change      Change a severity value
 severity list        Show possible ticket severities
diff --git a/trac/db_default.py b/trac/db_default.py
--- a/trac/db_default.py
+++ b/trac/db_default.py
@@ -17,7 +17,7 @@
 from trac.db import Table, Column, Index
 
 # Database version identifier. Used for automatic upgrades.
-db_version = 21
+db_version = 22
 
 def __mkreports(reports):
     """Utility function used to create report data in same syntax as the
@@ -82,20 +82,26 @@
         Index(['time'])],
 
     # Version control cache
-    Table('revision', key='rev')[
+    Table('repository', key=('id', 'name'))[
+        Column('id'),
+        Column('name'),
+        Column('value')],
+    Table('revision', key=('repos', 'rev'))[
+        Column('repos'),
         Column('rev'),
         Column('time', type='int'),
         Column('author'),
         Column('message'),
-        Index(['time'])],
-    Table('node_change', key=('rev', 'path', 'change_type'))[
+        Index(['repos', 'time'])],
+    Table('node_change', key=('repos', 'rev', 'path', 'change_type'))[
+        Column('repos'),
         Column('rev'),
         Column('path'),
         Column('node_type', size=1),
         Column('change_type', size=1),
         Column('base_path'),
         Column('base_rev'),
-        Index(['rev'])],
+        Index(['repos', 'rev'])],
 
     # Ticket system
     Table('ticket', key='id')[
@@ -384,8 +390,7 @@
            ('system',
              ('name', 'value'),
                (('database_version', str(db_version)),
-                ('initial_database_version', str(db_version)),
-                ('youngest_rev', ''))),
+                ('initial_database_version', str(db_version)))),
            ('report',
              ('author', 'title', 'query', 'description'),
                __mkreports(get_reports(db))))
diff --git a/trac/upgrades/db22.py b/trac/upgrades/db22.py
new file mode 100644
--- /dev/null
+++ b/trac/upgrades/db22.py
@@ -0,0 +1,51 @@
+from trac.db import Table, Column, Index, DatabaseManager
+
+def do_upgrade(env, ver, cursor):
+    # Make changeset cache multi-repository aware
+    cursor.execute("CREATE TEMPORARY TABLE rev_old "
+                   "AS SELECT * FROM revision")
+    cursor.execute("DROP TABLE revision")
+    cursor.execute("CREATE TEMPORARY TABLE nc_old "
+                   "AS SELECT * FROM node_change")
+    cursor.execute("DROP TABLE node_change")
+    
+    tables = [Table('repository', key=('id', 'name'))[
+                Column('id'),
+                Column('name'),
+                Column('value')],
+              Table('revision', key=('repos', 'rev'))[
+                Column('repos'),
+                Column('rev'),
+                Column('time', type='int'),
+                Column('author'),
+                Column('message'),
+                Index(['repos', 'time'])],
+              Table('node_change', key=('repos', 'rev', 'path', 'change_type'))[
+                Column('repos'),
+                Column('rev'),
+                Column('path'),
+                Column('node_type', size=1),
+                Column('change_type', size=1),
+                Column('base_path'),
+                Column('base_rev'),
+                Index(['repos', 'rev'])]]
+    
+    db_connector, _ = DatabaseManager(env)._get_connector()
+    for table in tables:
+        for stmt in db_connector.to_sql(table):
+            cursor.execute(stmt)
+    
+    cursor.execute("INSERT INTO revision (repos,rev,time,author,message) "
+                   "SELECT '',rev,time,author,message FROM rev_old")
+    cursor.execute("DROP TABLE rev_old")
+    cursor.execute("INSERT INTO node_change (repos,rev,path,node_type,"
+                   "change_type,base_path,base_rev) "
+                   "SELECT '',rev,path,node_type,change_type,base_path,"
+                   "base_rev FROM nc_old")
+    cursor.execute("DROP TABLE nc_old")
+    
+    cursor.execute("INSERT INTO repository (id,name,value) "
+                   "SELECT '',name,value FROM system "
+                   "WHERE name IN ('repository_dir', 'youngest_rev')")
+    cursor.execute("DELETE FROM system "
+                   "WHERE name IN ('repository_dir', 'youngest_rev')")
diff --git a/trac/versioncontrol/admin.py b/trac/versioncontrol/admin.py
--- a/trac/versioncontrol/admin.py
+++ b/trac/versioncontrol/admin.py
@@ -15,8 +15,9 @@
 
 from trac.admin import IAdminCommandProvider
 from trac.core import *
-from trac.util.text import printout
+from trac.util.text import printerr, printout
 from trac.util.translation import _, ngettext
+from trac.versioncontrol import IRepositoryChangeListener, RepositoryManager
 
 
 class VersionControlAdmin(Component):
@@ -27,36 +28,98 @@
     # IAdminCommandProvider methods
     
     def get_admin_commands(self):
-        yield ('resync', '[rev]',
-               """Re-synchronize trac with the repository
+        yield ('repository notify', '<event> <repos> <rev> [rev] [...]',
+               """Notify trac about repository events
+               
+               The event "changeset_added" notifies that new changesets have
+               been added to a repository.
+               
+               The event "changeset_modified" notifies that existing changesets
+               have been modified in a repository.
+               """,
+               self._complete_notify, self._do_notify)
+        yield ('repository resync', '<repos> [rev]',
+               """Re-synchronize trac with repositories
                
                When [rev] is specified, only that revision is synchronized.
                Otherwise, the complete revision history is synchronized. Note
                that this operation can take a long time to complete.
+               
+               To synchronize all repositories, specify "*" as the repository.
                """,
-               None, self._do_resync)
+               self._complete_repos, self._do_resync)
     
-    def _do_resync(self, rev=None):
-        if rev:
-            self.env.get_repository().sync_changeset(rev)
-            printout(_('%(rev)s resynced.', rev=rev))
-            return
+    _notify_events = [each for each in IRepositoryChangeListener.__dict__
+                      if not each.startswith('_')]
+    
+    def _complete_notify(self, args):
+        if len(args) == 1:
+            return self._notify_events
+        elif len(args) == 2:
+            rm = RepositoryManager(self.env)
+            return [reponame or '(default)' for reponame
+                    in rm.get_all_repositories()]
+    
+    def _complete_repos(self, args):
+        if len(args) == 1:
+            rm = RepositoryManager(self.env)
+            return [reponame or '(default)' for reponame
+                    in rm.get_all_repositories()]
+    
+    def _do_notify(self, event, reponame, *revs):
+        if event not in self._notify_events:
+            raise TracError(_('Unknown notify event "%s"' % event))
+        rm = RepositoryManager(self.env)
+        rm.notify(event, reponame, revs, None)
+    
+    def _do_resync(self, reponame, rev=None):
+        rm = RepositoryManager(self.env)
+        if reponame == '*':
+            if rev is not None:
+                raise TracError(_('Cannot synchronize a single revision '
+                                  'on multiple repositories'))
+            repositories = rm.get_real_repositories(None)
+        else:
+            if reponame == '(default)':
+                reponame = ''
+            repos = rm.get_repository(reponame, None)
+            if repos is None:
+                raise TracError(_("Unknown repository '%(reponame)s'",
+                                  reponame=reponame or '(default)'))
+            if rev is not None:
+                repos.sync_changeset(rev)
+                printout(_('%(rev)s resynced on %(reponame)s.', rev=rev,
+                           reponame=repos.reponame or '(default)'))
+                return
+            repositories = [repos]
+        
         from trac.versioncontrol.cache import CACHE_METADATA_KEYS
-        printout(_('Resyncing repository history... '))
         db = self.env.get_db_cnx()
         cursor = db.cursor()
-        cursor.execute("DELETE FROM revision")
-        cursor.execute("DELETE FROM node_change")
-        cursor.executemany("DELETE FROM system WHERE name=%s",
-                           [(k,) for k in CACHE_METADATA_KEYS])
-        cursor.executemany("INSERT INTO system (name, value) VALUES (%s, %s)",
-                           [(k, '') for k in CACHE_METADATA_KEYS])
-        db.commit()
-        repos = self.env.get_repository().sync(self._resync_feedback)
-        cursor.execute("SELECT count(rev) FROM revision")
-        for cnt, in cursor:
-            printout(ngettext('%(num)s revision cached.',
-                              '%(num)s revisions cached.', num=cnt))
+        inval = False
+        for repos in sorted(repositories, key=lambda r: r.reponame):
+            reponame = repos.reponame
+            printout(_('Resyncing repository history for %(reponame)s... ',
+                       reponame=reponame or '(default)'))
+            cursor.execute("DELETE FROM revision WHERE repos=%s", (reponame,))
+            cursor.execute("DELETE FROM node_change "
+                           "WHERE repos=%s", (reponame,))
+            cursor.executemany("DELETE FROM repository "
+                               "WHERE id=%s AND name=%s",
+                               [(reponame, k) for k in CACHE_METADATA_KEYS])
+            cursor.executemany("INSERT INTO repository (id, name, value) "
+                               "VALUES (%s, %s, %s)", [(reponame, k, '') 
+                                                for k in CACHE_METADATA_KEYS])
+            db.commit()
+            if repos.sync(self._resync_feedback):
+                inval = True
+            cursor.execute("SELECT count(rev) FROM revision WHERE repos=%s",
+                           (reponame,))
+            for cnt, in cursor:
+                printout(ngettext('%(num)s revision cached.',
+                                  '%(num)s revisions cached.', num=cnt))
+        if inval:
+            self.config.touch()     # FIXME: Brute force
         printout(_('Done.'))
 
     def _resync_feedback(self, rev):
diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py
--- a/trac/versioncontrol/api.py
+++ b/trac/versioncontrol/api.py
@@ -55,6 +55,7 @@
         """Return a Repository instance for the given repository type and dir.
         """
 
+
 class IRepositoryProvider(Interface):
     """Provide known named instances of Repository."""
 
@@ -73,8 +74,18 @@
         """
 
 
+class IRepositoryChangeListener(Interface):
+    """Listen for changes in repositories."""
+    
+    def changeset_added(self, repos, changeset):
+        """Called after a changeset has been added to a repository."""
+
+    def changeset_modified(self, repos, changeset):
+        """Called after a changeset has been modified in a repository."""
+
+
 class RepositoryManager(Component):
-    """Component registering the supported version control systems,
+    """Component registering the supported version control systems.
 
     It provides easy access to the configured implementation.
     """
@@ -83,6 +94,7 @@
 
     connectors = ExtensionPoint(IRepositoryConnector)
     providers = ExtensionPoint(IRepositoryProvider)
+    change_listeners = ExtensionPoint(IRepositoryChangeListener)
 
     repository_type = Option('trac', 'repository_type', 'svn',
         """Default repository connector type. (''since 0.10'')""")
@@ -103,7 +115,8 @@
         if handler is not Chrome(self.env):
             try:
                 # FIXME: only sync the default repository - this is bogus
-                self.get_repository('', req.authname).sync()
+                if self.get_repository('', req.authname).sync():
+                    self.config.touch()     # FIXME: Brute force
             except TracError, e:
                 add_warning(req, _("Can't synchronize with the repository "
                               "(%(error)s). Look in the Trac log for more "
@@ -184,7 +197,7 @@
     # Public API methods
 
     def get_repository(self, reponame, authname):
-        """Retrieve the appropriate Repository for the given name
+        """Retrieve the appropriate Repository for the given name.
 
            :param reponame: the key for specifying the repository.
                             If no name is given, take the the default 
@@ -195,7 +208,8 @@
         """
         repoinfo = self.get_all_repositories().get(reponame, {})
         if repoinfo and 'alias' in repoinfo:
-            repoinfo = self.get_all_repositories().get(repoinfo['alias'])
+            reponame = repoinfo['alias']
+            repoinfo = self.get_all_repositories().get(reponame)
         if repoinfo:
             rdir = repoinfo.get('dir')
             rtype = repoinfo.get('type', self.repository_type)
@@ -251,7 +265,7 @@
         return (reponame, self.get_repository(reponame, authname), path or '/')
 
     def get_default_repository(self, context):
-        """Recover the appropriatet repository from the current context.
+        """Recover the appropriate repository from the current context.
 
         Lookup the closest source or changeset resource in the context 
         hierarchy and return the name of its associated repository.
@@ -273,7 +287,58 @@
                     else:
                         self._all_repositories[reponame] = info
         return self._all_repositories
+    
+    def get_real_repositories(self, authname):
+        """Return a list of all real repositories (i.e. excluding aliases)."""
+        repositories = set()
+        for reponame in self.get_all_repositories():
+            try:
+                repos = self.get_repository(reponame, authname)
+                if repos is not None:
+                    repositories.add(repos)
+            except TracError:
+                "Skip invalid repositories"
+        return repositories
 
+    def notify(self, event, reponame, revs, authname):
+        """Notify repositories and change listeners about repository events.
+        
+        The supported events are the names of the methods defined in the
+        `IRepositoryChangeListener` interface.
+        """
+        self.log.debug('Event %s on %s for changesets %r'
+                       % (event, reponame, revs))
+        
+        # Notify a repository by name, and all repositories with the same
+        # base, or all repositories by base
+        repos = self.get_repository(reponame, None)
+        if repos:
+            base = repos.get_base()
+        else:
+            base = reponame
+        repositories = [each for each in self.get_real_repositories(authname)
+                        if each.get_base() == base]
+        inval = False
+        for repos in sorted(repositories, key=lambda r: r.reponame):
+            if repos.sync():
+                inval = True
+            for rev in revs:
+                if event == 'changeset_modified':
+                    repos.sync_changeset(rev)
+                try:
+                    changeset = repos.get_changeset(rev)
+                except NoSuchChangeset:
+                    continue
+                inval = inval or (event == 'changeset_modified')
+                self.log.debug('Event %s on %s for revision %s'
+                               % (event, repos.reponame, rev))
+                for listener in self.change_listeners:
+                    getattr(listener, event)(repos, changeset)
+        
+        if inval:
+            self.log.debug('Invalidating youngest_rev cache')
+            self.config.touch()     # FIXME: Brute force method
+    
     def shutdown(self, tid=None):
         if tid:
             assert tid == threading._get_ident()
@@ -345,6 +410,15 @@
         """Close the connection to the repository."""
         raise NotImplementedError
 
+    def get_base(self):
+        """Return the name of the base repository for this repository.
+        
+        This function returns the name of the base repository to which scoped
+        repositories belong. For non-scoped repositories, it returns the 
+        repository name.
+        """
+        return self.name
+        
     def clear(self, youngest_rev=None):
         """Clear any data that may have been cached in instance properties.
 
@@ -360,12 +434,14 @@
         The backend will call this function for each `rev` it decided to
         synchronize, once the synchronization changes are committed to the 
         cache.
+        
+        Return True if any changes have been committed to the cache.
         """
-        pass
+        return False
 
     def sync_changeset(self, rev):
         """Resync the repository cache for the given `rev`, if relevant."""
-        raise NotImplementedError
+        pass
 
     def get_quickjump_entries(self, rev):
         """Generate a list of interesting places in the repository.
@@ -378,7 +454,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):
@@ -457,7 +533,7 @@
         return row and row[0] or None
 
     def get_path_history(self, path, rev=None, limit=None):
-        """Retrieve all the revisions containing this path
+        """Retrieve all the revisions containing this path.
 
         If given, `rev` is used as a starting point (i.e. no revision
         ''newer'' than `rev` should be returned).
@@ -636,7 +712,7 @@
         return []
         
     def get_changes(self):
-        """Generator that produces a tuple for every change in the changeset
+        """Generator that produces a tuple for every change in the changeset.
 
         The tuple will contain `(path, kind, change, base_path, base_rev)`,
         where `change` can be one of Changeset.ADD, Changeset.COPY,
diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
--- a/trac/versioncontrol/cache.py
+++ b/trac/versioncontrol/cache.py
@@ -41,16 +41,25 @@
     has_linear_changesets = False
 
     def __init__(self, getdb, repos, authz, log):
+        self.repos = repos
         Repository.__init__(self, repos.name, authz, log)
         if callable(getdb):
             self.getdb = getdb
         else:
             self.getdb = lambda: getdb
-        self.repos = repos
 
+    def _set_reponame(self, value):
+        self.repos.reponame = value
+    
+    reponame = property(fget=lambda self: self.repos.reponame,
+                        fset=_set_reponame)
+    
     def close(self):
         self.repos.close()
 
+    def get_base(self):
+        return self.repos.get_base()
+        
     def get_quickjump_entries(self, rev):
         for category, name, path, rev in self.repos.get_quickjump_entries(rev):
             yield category, name, path, rev
@@ -63,9 +72,10 @@
         db = self.getdb()
         cursor = db.cursor()
         cursor.execute("SELECT rev FROM revision "
-                       "WHERE time >= %s AND time < %s "
+                       "WHERE repos=%s AND time >= %s AND time < %s"
                        "ORDER BY time DESC, rev DESC",
-                       (to_timestamp(start), to_timestamp(stop)))
+                       (self.reponame, to_timestamp(start),
+                        to_timestamp(stop)))
         for rev, in cursor:
             try:
                 if self.authz.has_permission_for_changeset(rev):
@@ -78,16 +88,18 @@
         db = self.getdb()
         cursor = db.cursor()
         cursor.execute("UPDATE revision SET time=%s, author=%s, message=%s "
-                       "WHERE rev=%s", (to_timestamp(cset.date),
-                                        cset.author, cset.message,
-                                        (str(cset.rev))))
+                       "WHERE repos=%s AND rev=%s",
+                       (to_timestamp(cset.date), cset.author, cset.message,
+                        self.reponame, str(cset.rev)))
         db.commit()
         
     def sync(self, feedback=None):
         db = self.getdb()
         cursor = db.cursor()
-        cursor.execute("SELECT name, value FROM system WHERE name IN (%s)" %
-                       ','.join(["'%s'" % key for key in CACHE_METADATA_KEYS]))
+        cursor.execute("SELECT name, value FROM repository "
+                       "WHERE id=%%s AND name IN (%s)" % 
+                       ','.join(['%s'] * len(CACHE_METADATA_KEYS)),
+                       (self.reponame,) + CACHE_METADATA_KEYS)
         metadata = {}
         for name, value in cursor:
             metadata[name] = value
@@ -103,24 +115,27 @@
                                   "'trac-admin resync' operation is needed."))
         elif repository_dir is None: # 
             self.log.info('Storing initial "repository_dir": %s' % self.name)
-            cursor.execute("INSERT INTO system (name,value) VALUES (%s,%s)",
-                           (CACHE_REPOSITORY_DIR, self.name,))
+            cursor.execute("INSERT INTO repository (id,name,value) "
+                           "VALUES (%s,%s,%s)",
+                           (self.reponame, CACHE_REPOSITORY_DIR, self.name))
         else: # 'repository_dir' cleared by a resync
             self.log.info('Resetting "repository_dir": %s' % self.name)
-            cursor.execute("UPDATE system SET value=%s WHERE name=%s",
-                           (self.name, CACHE_REPOSITORY_DIR))
-
-        db.commit() # save metadata changes made up to now
+            cursor.execute("UPDATE repository SET value=%s "
+                           "WHERE id=%s AND name=%s",
+                           (self.name, self.reponame, CACHE_REPOSITORY_DIR))
 
         # -- retrieve the youngest revision in the repository
         self.repos.clear()
         repos_youngest = self.repos.youngest_rev
 
         # -- retrieve the youngest revision cached so far
-        if CACHE_YOUNGEST_REV not in metadata:
-            raise TracError(_('Missing "youngest_rev" in cache metadata'))
-        
-        self.youngest = metadata[CACHE_YOUNGEST_REV]
+        self.youngest = metadata.get(CACHE_YOUNGEST_REV)
+        if self.youngest is None:
+            cursor.execute("INSERT INTO repository (id,name,value) "
+                           "VALUES (%s,%s,%s)",
+                           (self.reponame, CACHE_YOUNGEST_REV, ''))
+
+        db.commit() # save metadata changes made up to now
 
         if self.youngest:
             self.youngest = self.repos.normalize_rev(self.youngest)
@@ -150,19 +165,21 @@
                                     find_initial_rev=True)
                     next_youngest = self.repos.normalize_rev(next_youngest)
                 except TracError:
-                    return # can't normalize oldest_rev: repository was empty
+                    # can't normalize oldest_rev: repository was empty
+                    return False
 
             if next_youngest is None: # nothing to cache yet
-                return
+                return False
 
             # 0. first check if there's no (obvious) resync in progress
-            cursor.execute("SELECT rev FROM revision WHERE rev=%s",
-                           (str(next_youngest),))
+            cursor.execute("SELECT rev FROM revision "
+                           "WHERE repos=%s AND rev=%s",
+                           (self.reponame, str(next_youngest)))
             for rev, in cursor:
                 # already there, but in progress, so keep ''previous''
                 # notion of 'youngest'
                 self.repos.clear(youngest_rev=self.youngest)
-                return
+                return False
 
             # 1. prepare for resyncing
             #    (there still might be a race condition at this point)
@@ -182,9 +199,9 @@
                     cset = self.repos.get_changeset(next_youngest)
                     try:
                         cursor.execute("INSERT INTO revision "
-                                       " (rev,time,author,message) "
-                                       "VALUES (%s,%s,%s,%s)",
-                                       (str(next_youngest),
+                                       " (repos,rev,time,author,message) "
+                                       "VALUES (%s,%s,%s,%s,%s)",
+                                       (self.reponame, str(next_youngest),
                                         to_timestamp(cset.date),
                                         cset.author, cset.message))
                     except Exception, e: # *another* 1.1. resync attempt won 
@@ -194,7 +211,7 @@
                         # notion of 'youngest'
                         self.repos.clear(youngest_rev=self.youngest)
                         db.rollback()
-                        return
+                        return False
 
                     # 1.2. now *only* one process was able to get there
                     #      (i.e. there *shouldn't* be any race condition here)
@@ -206,10 +223,10 @@
                         kind = kindmap[kind]
                         action = actionmap[action]
                         cursor.execute("INSERT INTO node_change "
-                                       " (rev,path,node_type,change_type, "
-                                       "  base_path,base_rev) "
-                                       "VALUES (%s,%s,%s,%s,%s,%s)",
-                                       (str(next_youngest),
+                                       " (repos,rev,path,node_type,"
+                                       "  change_type,base_path,base_rev) "
+                                       "VALUES (%s,%s,%s,%s,%s,%s,%s)",
+                                       (self.reponame, str(next_youngest),
                                         path, kind, action, bpath, brev))
 
                     # 1.3. iterate (1.1 should always succeed now)
@@ -218,13 +235,17 @@
 
                     # 1.4. update 'youngest_rev' metadata 
                     #      (minimize possibility of failures at point 0.)
-                    cursor.execute("UPDATE system SET value=%s WHERE name=%s",
-                                   (str(self.youngest), CACHE_YOUNGEST_REV))
+                    cursor.execute("UPDATE repository SET value=%s "
+                                   "WHERE id=%s AND name=%s",
+                                   (str(self.youngest), self.reponame,
+                                    CACHE_YOUNGEST_REV))
                     db.commit()
 
                     # 1.5. provide some feedback
                     if feedback:
                         feedback(self.youngest)
+                
+                return True
             finally:
                 # 3. restore permission checking (after 1.)
                 self.repos.authz = authz
@@ -240,7 +261,12 @@
 
     def get_youngest_rev(self):
         if not hasattr(self, 'youngest'):
-            self.sync()
+            db = self.getdb()
+            cursor = db.cursor()
+            cursor.execute("SELECT MAX(" + db.cast('rev', 'int') + ") "
+                           "FROM revision WHERE repos=%s", (self.reponame,))
+            for rev, in cursor:
+                self.youngest = str(rev)
         return self.youngest
 
     def previous_rev(self, rev, path=''):
@@ -258,9 +284,9 @@
     def _next_prev_rev(self, direction, rev, path=''):
         db = self.getdb()
         # the changeset revs are sequence of ints:
-        sql = "SELECT rev FROM node_change WHERE " + \
+        sql = "SELECT rev FROM node_change WHERE repos=%s AND " + \
               db.cast('rev', 'int') + " " + direction + " %s"
-        args = [rev]
+        args = [self.reponame, rev]
 
         if path:
             path = path.lstrip('/')
@@ -316,7 +342,8 @@
         db = self.getdb()
         cursor = db.cursor()
         cursor.execute("SELECT time,author,message FROM revision "
-                       "WHERE rev=%s", (str(rev),))
+                       "WHERE repos=%s AND rev=%s",
+                       (self.repos.reponame, str(rev)))
         row = cursor.fetchone()
         if row:
             _date, author, message = row
@@ -330,8 +357,8 @@
         db = self.getdb()
         cursor = db.cursor()
         cursor.execute("SELECT path,node_type,change_type,base_path,base_rev "
-                       "FROM node_change WHERE rev=%s "
-                       "ORDER BY path", (str(self.rev),))
+                       "FROM node_change WHERE repos=%s AND rev=%s "
+                       "ORDER BY path", (self.repos.reponame, str(self.rev)))
         for path, kind, change, base_path, base_rev in cursor:
             if not self.authz.has_permission(posixpath.join(self.scope,
                                                             path.strip('/'))):
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
@@ -411,12 +411,13 @@
         
         # Remove any trailing slash or else subversion might abort
         if isinstance(path, unicode):
-            self.path = path
             path_utf8 = path.encode('utf-8')
         else: # note that this should usually not happen (unicode arg expected)
-            self.path = to_unicode(path)
-            path_utf8 = self.path.encode('utf-8')
+            path_utf8 = to_unicode(path).encode('utf-8')
+
         path_utf8 = os.path.normpath(path_utf8).replace('\\', '/')
+        self.path = path_utf8.decode('utf-8')
+        
         root_path_utf8 = repos.svn_repos_find_root_path(path_utf8, self.pool())
         if root_path_utf8 is None:
             raise TracError(_("%(path)s does not appear to be a Subversion "
@@ -431,7 +432,8 @@
         self.fs_ptr = repos.svn_repos_fs(self.repos)
         
         uuid = fs.get_uuid(self.fs_ptr, self.pool())
-        name = 'svn:%s:%s' % (uuid, _from_svn(path_utf8))
+        self.base = 'svn:%s:%s' % (uuid, _from_svn(root_path_utf8))
+        name = 'svn:%s:%s' % (uuid, self.path)
 
         Repository.__init__(self, name, authz, log)
 
@@ -484,6 +486,9 @@
     def close(self):
         self.repos = self.fs_ptr = self.pool = None
 
+    def get_base(self):
+        return self.base
+        
     def _get_tags_or_branches(self, paths):
         """Retrieve known branches or tags."""
         for path in self.options.get(paths, []):
diff --git a/trac/versioncontrol/tests/api.py b/trac/versioncontrol/tests/api.py
--- a/trac/versioncontrol/tests/api.py
+++ b/trac/versioncontrol/tests/api.py
@@ -26,9 +26,6 @@
     def test_raise_NotImplementedError_close(self):
         self.failUnlessRaises(NotImplementedError, self.repo_base.close)
 
-    def test_raise_NotImplementedError_sync_changeset(self):
-        self.failUnlessRaises(NotImplementedError, self.repo_base.sync_changeset, 1)
-
     def test_raise_NotImplementedError_get_changeset(self):
         self.failUnlessRaises(NotImplementedError, self.repo_base.get_changeset, 1)
 
diff --git a/trac/versioncontrol/tests/cache.py b/trac/versioncontrol/tests/cache.py
--- a/trac/versioncontrol/tests/cache.py
+++ b/trac/versioncontrol/tests/cache.py
@@ -32,8 +32,9 @@
         self.db = InMemoryDatabase()
         self.log = logger_factory('test')
         cursor = self.db.cursor()
-        cursor.execute("INSERT INTO system (name, value) VALUES (%s,%s)",
-                       ('youngest_rev', ''))
+        cursor.execute("INSERT INTO repository (id, name, value) "
+                       "VALUES (%s,%s,%s)",
+                       ('test-repos', 'youngest_rev', ''))
 
     def test_initial_sync_with_empty_repos(self):
         t = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
@@ -91,16 +92,19 @@
         t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc)
         t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc)
         cursor = self.db.cursor()
-        cursor.execute("INSERT INTO revision (rev,time,author,message) "
-                       "VALUES (0,%s,'','')", (to_timestamp(t1),))
-        cursor.execute("INSERT INTO revision (rev,time,author,message) "
-                       "VALUES (1,%s,'joe','Import')", (to_timestamp(t2),))
-        cursor.executemany("INSERT INTO node_change (rev,path,node_type,"
-                           "change_type,base_path,base_rev) "
-                           "VALUES ('1',%s,%s,%s,%s,%s)",
+        cursor.execute("INSERT INTO revision (repos,rev,time,author,message) "
+                       "VALUES ('test-repos',0,%s,'','')",
+                       (to_timestamp(t1),))
+        cursor.execute("INSERT INTO revision (repos,rev,time,author,message) "
+                       "VALUES ('test-repos',1,%s,'joe','Import')",
+                       (to_timestamp(t2),))
+        cursor.executemany("INSERT INTO node_change (repos,rev,path,"
+                           "node_type,change_type,base_path,base_rev) "
+                           "VALUES ('test-repos','1',%s,%s,%s,%s,%s)",
                            [('trunk', 'D', 'A', None, None),
                             ('trunk/README', 'F', 'A', None, None)])
-        cursor.execute("UPDATE system SET value='1' WHERE name='youngest_rev'")
+        cursor.execute("UPDATE repository SET value='1' "
+                       "WHERE id='test-repos' AND name='youngest_rev'")
 
         changes = [('trunk/README', Node.FILE, Changeset.EDIT, 'trunk/README', 1)]
         changeset = Mock(Changeset, 2, 'Update', 'joe', t3,
@@ -128,16 +132,19 @@
         t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
         t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc)
         cursor = self.db.cursor()
-        cursor.execute("INSERT INTO revision (rev,time,author,message) "
-                       "VALUES (0,%s,'','')", (to_timestamp(t1),))
-        cursor.execute("INSERT INTO revision (rev,time,author,message) "
-                       "VALUES (1,%s,'joe','Import')", (to_timestamp(t2),))
-        cursor.executemany("INSERT INTO node_change (rev,path,node_type,"
-                           "change_type,base_path,base_rev) "
-                           "VALUES ('1',%s,%s,%s,%s,%s)",
+        cursor.execute("INSERT INTO revision (repos,rev,time,author,message) "
+                       "VALUES ('test-repos',0,%s,'','')",
+                       (to_timestamp(t1),))
+        cursor.execute("INSERT INTO revision (repos,rev,time,author,message) "
+                       "VALUES ('test-repos',1,%s,'joe','Import')",
+                       (to_timestamp(t2),))
+        cursor.executemany("INSERT INTO node_change (repos,rev,path,"
+                           "node_type,change_type,base_path,base_rev) "
+                           "VALUES ('test-repos','1',%s,%s,%s,%s,%s)",
                            [('trunk', 'D', 'A', None, None),
                             ('trunk/README', 'F', 'A', None, None)])
-        cursor.execute("UPDATE system SET value='1' WHERE name='youngest_rev'")
+        cursor.execute("UPDATE repository SET value='1' "
+                       "WHERE id='test-repos' AND name='youngest_rev'")
 
         repos = Mock(Repository, 'test-repos', None, self.log,
                      get_changeset=lambda x: None,
diff --git a/trac/versioncontrol/web_ui/changeset.py b/trac/versioncontrol/web_ui/changeset.py
--- a/trac/versioncontrol/web_ui/changeset.py
+++ b/trac/versioncontrol/web_ui/changeset.py
@@ -908,14 +908,15 @@
 
         single = rev_a == rev_b
         if reponame:
-            title = ngettext('Changeset in %(repo)s', 'Changesets in %(repo)s',
+            title = ngettext('Changeset in %(repo)s ',
+                             'Changesets in %(repo)s ',
                              single and 1 or 2, repo=reponame)
         else:
-            title = ngettext('Changeset', 'Changesets', single and 1 or 2)
+            title = ngettext('Changeset ', 'Changesets ', single and 1 or 2)
         if single:
-            title = tag(title, tag.em(' [%s]' % rev_a))
+            title = tag(title, tag.em('[%s]' % rev_a))
         else:
-            title = tag(title, tag.em(' [%s-%s]' % (rev_a, rev_b)))
+            title = tag(title, tag.em('[%s-%s]' % (rev_a, rev_b)))
         if field == 'title':
             return title
         elif field == 'summary':

