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,7 +31,12 @@
 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 add        Add a source repository
+repository alias      Create an alias for a repository
 repository changeset  Notify trac about new changesets
+repository list       List source repositories
+repository remove     Remove a source repository
+repository rename     Rename a source repository
 repository resync     Re-synchronize trac with repositories
 resolution add        Add a resolution value option
 resolution change     Change a resolution value
diff --git a/trac/versioncontrol/admin.py b/trac/versioncontrol/admin.py
--- a/trac/versioncontrol/admin.py
+++ b/trac/versioncontrol/admin.py
@@ -13,9 +13,9 @@
 
 import sys
 
-from trac.admin import IAdminCommandProvider
+from trac.admin import IAdminCommandProvider, get_dir_list
 from trac.core import *
-from trac.util.text import printerr, printout
+from trac.util.text import print_table, printerr, printout
 from trac.util.translation import _, ngettext
 from trac.versioncontrol import RepositoryManager
 
@@ -28,9 +28,24 @@
     # IAdminCommandProvider methods
     
     def get_admin_commands(self):
+        yield ('repository add', '<repos> <dir> [type]',
+               'Add a source repository',
+               self._complete_add, self._do_add)
+        yield ('repository alias', '<repos> <alias>',
+               'Create an alias for a repository',
+               self._complete_repos, self._do_alias)
         yield ('repository changeset', '<repos> <rev> [rev] [...]',
                'Notify trac about new changesets',
                self._complete_repos, self._do_changeset)
+        yield ('repository list', '',
+               'List source repositories',
+               None, self._do_list)
+        yield ('repository remove', '<repos>',
+               'Remove a source repository',
+               self._complete_repos, self._do_remove)
+        yield ('repository rename', '<repos> <newname>',
+               'Rename a source repository',
+               self._complete_repos, self._do_rename)
         yield ('repository resync', '<repos> [rev]',
                """Re-synchronize trac with repositories
                
@@ -42,16 +57,95 @@
                """,
                self._complete_repos, self._do_resync)
     
+    def get_supported_types(self):
+        rm = RepositoryManager(self.env)
+        return [type_ for connector in rm.connectors
+                for (type_, prio) in connector.get_supported_types()
+                if prio >= 0]
+    
+    def _complete_add(self, args):
+        if len(args) == 2:
+            return get_dir_list(args[-1], True)
+        elif len(args) == 3:
+            return self.get_supported_types()
+    
     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_add(self, reponame, dir, type_=None):
+        if reponame == '(default)':
+            reponame = ''
+        if type_ is not None and type_ not in self.get_supported_types():
+            raise TracError(_("The repository type '%(type)s' is not "
+                              "supported", type=type_))
+        db = self.env.get_db_cnx()
+        cursor = db.cursor()
+        cursor.executemany("INSERT INTO repository (id, name, value) "
+                           "VALUES (%s, %s, %s)",
+                           [(reponame, 'dir', dir), (reponame, 'type', type_)])
+        db.commit()
+        RepositoryManager(self.env).reload_repositories()
+    
+    def _do_alias(self, reponame, alias):
+        if reponame == '(default)':
+            reponame = ''
+        if alias in ('', '(default)'):
+            raise TracError(_("Invalid alias name '%(alias)s'", alias=alias))
+        db = self.env.get_db_cnx()
+        cursor = db.cursor()
+        cursor.executemany("INSERT INTO repository (id, name, value) "
+                           "VALUES (%s, %s, %s)",
+                           [(alias, 'dir', None), (alias, 'alias', reponame)])
+        db.commit()
+        RepositoryManager(self.env).reload_repositories()
+    
     def _do_changeset(self, reponame, *revs):
         rm = RepositoryManager(self.env)
         rm.notify_changesets_added(reponame, revs, None)
     
+    def _do_list(self):
+        rm = RepositoryManager(self.env)
+        values = []
+        for (reponame, info) in sorted(rm.get_all_repositories().iteritems()):
+            alias = ''
+            if 'alias' in info:
+                alias = info['alias'] or '(default)'
+            values.append((reponame or '(default)', info.get('type', ''),
+                           alias, info.get('dir', '')))
+        print_table(values, [_('Name'), _('Type'), _('Alias'), _('Directory')])
+    
+    def _do_remove(self, reponame):
+        if reponame == '(default)':
+            reponame = ''
+        db = self.env.get_db_cnx()
+        cursor = db.cursor()
+        cursor.execute("DELETE FROM repository "
+                       "WHERE id=%s AND name IN ('dir', 'type', 'alias')",
+                       (reponame,))
+        cursor.execute("DELETE FROM revision WHERE repos=%s", (reponame,))
+        cursor.execute("DELETE FROM node_change WHERE repos=%s", (reponame,))
+        db.commit()
+        RepositoryManager(self.env).reload_repositories()
+    
+    def _do_rename(self, reponame, newname):
+        if reponame == '(default)':
+            reponame = ''
+        if newname == '(default)':
+            newname = ''
+        db = self.env.get_db_cnx()
+        cursor = db.cursor()
+        cursor.execute("UPDATE repository SET id=%s WHERE id=%s",
+                       (newname, reponame))
+        cursor.execute("UPDATE revision SET repos=%s WHERE repos=%s",
+                       (newname, reponame))
+        cursor.execute("UPDATE node_change SET repos=%s WHERE repos=%s",
+                       (newname, reponame))
+        db.commit()
+        RepositoryManager(self.env).reload_repositories()
+    
     def _do_resync(self, reponame, rev=None):
         rm = RepositoryManager(self.env)
         if reponame == '*':
diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py
--- a/trac/versioncontrol/api.py
+++ b/trac/versioncontrol/api.py
@@ -81,6 +81,28 @@
         """Called after a changeset has been added to a repository."""
 
 
+class DbRepositoryProvider(Component):
+    """Component providing repositories registered in the DB."""
+
+    implements(IRepositoryProvider)
+
+    # IRepositoryProvider methods
+
+    def get_repositories(self):
+        """Retrieve repositories specified in the repository DB table."""
+        db = self.env.get_db_cnx()
+        cursor = db.cursor()
+        cursor.execute("SELECT id,name,value FROM repository "
+                       "WHERE name IN ('dir', 'alias', 'type')")
+        reponames = {}
+        for (id, name, value) in cursor:
+            if value is not None:
+                reponames.setdefault(id, {})[name] = value
+        
+        for reponame, info in reponames.iteritems():
+            yield (reponame, info)
+
+
 class RepositoryManager(Component):
     """Component registering the supported version control systems.
 
@@ -296,6 +318,17 @@
                 "Skip invalid repositories"
         return repositories
 
+    def reload_repositories(self):
+        """Reload the repositories from the providers."""
+        self._lock.acquire()
+        try:
+            # FIXME: trac-admin doesn't reload the environment
+            self._cache = {}
+            self._all_repositories = None
+        finally:
+            self._lock.release()
+        self.config.touch()     # Force environment reload
+    
     def notify_changesets_added(self, reponame, revs, authname):
         """Notify repositories and change listeners about added changesets."""
         self.log.debug('Notification on %s for changesets %r'

