diff --git a/trac/admin/console.py b/trac/admin/console.py
--- a/trac/admin/console.py
+++ b/trac/admin/console.py
@@ -709,39 +709,54 @@
            project_dir=os.path.basename(self.envname),
            config_path=os.path.join(self.envname, 'conf', 'trac.ini')))
 
-    _help_resync = [('resync', 'Re-synchronize trac with the repository'),
-                    ('resync <rev>', 'Re-synchronize only the given <rev>')]
+    ## Resync
+    _help_resync = [('resync', 'Re-synchronize trac with all repositories'),
+                    ('resync <repos>', 
+                     'Re-synchronize only the given <repos>'),
+                    ('resync <repos> <rev>', 
+                     'Re-synchronize only the given <rev> of <repos>')]
 
     def _resync_feedback(self, rev):
         sys.stdout.write(' [%s]\r' % rev)
         sys.stdout.flush()
         
-    ## Resync
     def do_resync(self, line):
         env = self.env_open()
         argv = self.arg_tokenize(line)
-        if argv:
-            rev = argv[0]
-            if rev:
-                env.get_repository().sync_changeset(rev)
-                printout(_("%(rev)s resynced.", rev=rev))
-                return
+        if argv[0]:
+            reponame = argv[0]
+            if len(argv) >= 2:
+                rev = argv[1]
+                if rev:
+                    env.get_repository(reponame).sync_changeset(rev)
+                    printout(_("%(rev)s resynced on %(reponame)s.", rev=rev,
+                               reponame=reponame))
+                    return
+            repositories = [(reponame, env.get_repository(reponame))]
+        else:
+            repositories = list(env.get_all_repositories())
+        
         from trac.versioncontrol.cache import CACHE_METADATA_KEYS
-        printout(_("Resyncing repository history... "))
-        cnx = self.db_open()
-        cursor = cnx.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])
-        cnx.commit()
-        repos = 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))
+        for (reponame, repos) in sorted(repositories):
+            printout(_("Resyncing repository history for %(reponame)s... ",
+                       reponame=reponame or '(default)'))
+            cnx = self.db_open()
+            cursor = cnx.cursor()
+            cursor.execute("DELETE FROM revision WHERE id=%s", (reponame,))
+            cursor.execute("DELETE FROM node_change WHERE id=%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])
+            cnx.commit()
+            repos.sync(self._resync_feedback)
+            cursor.execute("SELECT count(rev) FROM revision WHERE id=%s",
+                           (reponame,))
+            for cnt, in cursor:
+                printout(ngettext("%(num)s revision cached.",
+                                  "%(num)s revisions cached.", num=cnt))
         printout(_("Done."))
 
     ## Wiki
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=('id', 'rev'))[
+        Column('id'),
         Column('rev'),
         Column('time', type='int'),
         Column('author'),
         Column('message'),
-        Index(['time'])],
-    Table('node_change', key=('rev', 'path', 'change_type'))[
+        Index(['id', 'time'])],
+    Table('node_change', key=('id', 'rev', 'path', 'change_type'))[
+        Column('id'),
         Column('rev'),
         Column('path'),
         Column('node_type', size=1),
         Column('change_type', size=1),
         Column('base_path'),
         Column('base_rev'),
-        Index(['rev'])],
+        Index(['id', 'rev'])],
 
     # Ticket system
     Table('ticket', key='id')[
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=('id', 'rev'))[
+                Column('id'),
+                Column('rev'),
+                Column('time', type='int'),
+                Column('author'),
+                Column('message'),
+                Index(['id', 'time'])],
+              Table('node_change', key=('id', 'rev', 'path', 'change_type'))[
+                Column('id'),
+                Column('rev'),
+                Column('path'),
+                Column('node_type', size=1),
+                Column('change_type', size=1),
+                Column('base_path'),
+                Column('base_rev'),
+                Index(['id', '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 (id,rev,time,author,message) "
+                   "SELECT '',rev,time,author,message FROM rev_old")
+    cursor.execute("DROP TABLE rev_old")
+    cursor.execute("INSERT INTO node_change (id,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/cache.py b/trac/versioncontrol/cache.py
--- a/trac/versioncontrol/cache.py
+++ b/trac/versioncontrol/cache.py
@@ -47,6 +47,15 @@
             self.getdb = lambda: getdb
         self.repos = repos
 
+    def _set_reponame(self, value):
+        try:
+            self.repos.reponame = value
+        except AttributeError:
+            pass
+    
+    reponame = property(fget=lambda self: self.repos.reponame,
+                        fset=_set_reponame)
+    
     def close(self):
         self.repos.close()
 
@@ -62,9 +71,10 @@
         db = self.getdb()
         cursor = db.cursor()
         cursor.execute("SELECT rev FROM revision "
-                       "WHERE time >= %s AND time < %s "
+                       "WHERE id=%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):
@@ -77,16 +87,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 id=%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
@@ -101,12 +113,14 @@
                                   "'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))
+            cursor.execute("UPDATE repository SET value=%s "
+                           "WHERE id=%s AND name=%s",
+                           (self.name, self.reponame, CACHE_REPOSITORY_DIR))
 
         db.commit() # save metadata changes made up to now
 
@@ -154,8 +168,8 @@
                 return
 
             # 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 id=%s AND rev=%s",
+                           (self.reponame, str(next_youngest)))
             for rev, in cursor:
                 # already there, but in progress, so keep ''previous''
                 # notion of 'youngest'
@@ -180,9 +194,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),
+                                       " (id,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 
@@ -204,10 +218,10 @@
                         kind = kindmap[kind]
                         action = actionmap[action]
                         cursor.execute("INSERT INTO node_change "
-                                       " (rev,path,node_type,change_type, "
+                                       " (id,rev,path,node_type,change_type, "
                                        "  base_path,base_rev) "
-                                       "VALUES (%s,%s,%s,%s,%s,%s)",
-                                       (str(next_youngest),
+                                       "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)
@@ -216,8 +230,10 @@
 
                     # 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
@@ -256,9 +272,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 id=%s AND " + \
               db.cast('rev', 'int') + " " + direction + " %s"
-        args = [rev]
+        args = [self.reponame, rev]
 
         if path:
             path = path.lstrip('/')
@@ -314,7 +330,8 @@
         db = self.getdb()
         cursor = db.cursor()
         cursor.execute("SELECT time,author,message FROM revision "
-                       "WHERE rev=%s", (str(rev),))
+                       "WHERE id=%s AND rev=%s",
+                       (self.repos.reponame, str(rev)))
         row = cursor.fetchone()
         if row:
             _date, author, message = row
@@ -328,8 +345,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 id=%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/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
@@ -906,14 +906,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':

