diff --git a/trac/versioncontrol/admin.py b/trac/versioncontrol/admin.py
--- a/trac/versioncontrol/admin.py
+++ b/trac/versioncontrol/admin.py
@@ -187,7 +187,7 @@
                     # Modify repository
                     changed = False
                     changes = {}
-                    for field in ['alias', 'dir', 'type']:
+                    for field in db_provider.repository_attrs:
                         value = req.args.get(field)
                         if value is not None and value != info.get(field):
                             changes[field] = value
diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py
--- a/trac/versioncontrol/api.py
+++ b/trac/versioncontrol/api.py
@@ -95,6 +95,8 @@
 
     implements(IRepositoryProvider, IAdminCommandProvider)
 
+    repository_attrs = ('alias', 'dir', 'type', 'url')
+    
     # IRepositoryProvider methods
 
     def get_repositories(self):
@@ -102,14 +104,13 @@
         db = self.env.get_db_cnx()
         cursor = db.cursor()
         cursor.execute("SELECT id,name,value FROM repository "
-                       "WHERE name IN ('dir', 'alias', 'type')")
+                       "WHERE name IN (%s)" % ",".join(
+                           "'%s'" % each for each in self.repository_attrs))
         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)
+        return reponames.iteritems()
 
     # IAdminCommandProvider methods
     
@@ -229,8 +230,15 @@
         db = self.env.get_db_cnx()
         cursor = db.cursor()
         for (k, v) in changes.iteritems():
+            if k not in self.repository_attrs:
+                continue
             cursor.execute("UPDATE repository SET value=%s "
                            "WHERE id=%s AND name=%s", (v, reponame, k))
+            cursor.execute("SELECT value FROM repository "
+                           "WHERE id=%s AND name=%s", (reponame, k))
+            if not cursor.fetchone():
+                cursor.execute("INSERT INTO repository VALUES (%s, %s, %s)",
+                               (reponame, k, v))
         db.commit()
         RepositoryManager(self.env).reload_repositories()
 
@@ -257,6 +265,9 @@
         This means that if you want to use Trac without the source browser,
         simply remove that entry from the [trac] section.""")
 
+    repository_url = Option('trac', 'repository_url', '',
+        """Base URL of the default repository. (''since 0.12'')""")
+
     repository_sync_per_request = ListOption('trac',
         'repository_sync_per_request', '(default)',
         doc="""List of repositories that should be synchronized on every page
@@ -364,13 +375,14 @@
                 name, detail = option[:dotindex], option[dotindex+1:]
                 if name in reponames:
                     reponames[name][detail] = repositories.get(option)
-                else: # alias?
+                elif detail == 'alias':
                     alias = repositories.get(option)
                     if alias in reponames:
                         reponames[option] = {'alias': alias}
         # eventually add pre-0.12 default repository
         if '' not in reponames and self.repository_dir:
-            reponames[''] = {'dir': self.repository_dir}
+            reponames[''] = {'dir': self.repository_dir,
+                             'url': self.repository_url}
 
         for reponame, info in reponames.iteritems():
             yield (reponame, info)
@@ -461,9 +473,9 @@
                  been truncated, if needed.
         """
         matches = []
-        path = path and path.strip('/')+'/' or '/'
+        path = path and path.strip('/') + '/' or '/'
         for reponame in self.get_all_repositories().keys():
-            stripped_reponame = reponame.strip('/')+'/'
+            stripped_reponame = reponame.strip('/') + '/'
             if path.startswith(stripped_reponame):
                 matches.append((len(stripped_reponame), reponame))
         if matches:
@@ -472,7 +484,8 @@
             path = path[length:]
         else:
             reponame = ''
-        return (reponame, self.get_repository(reponame, authname), path or '/')
+        return (reponame, self.get_repository(reponame, authname),
+                path.rstrip('/') or '/')
 
     def get_default_repository(self, context):
         """Recover the appropriate repository from the current context.
@@ -681,6 +694,15 @@
         """
         return []
     
+    def get_path_url(self, path, rev):
+        """Return the repository URL for the given path and revision.
+        
+        The returned URL can be `None`, meaning that no URL has been specified
+        for the repository, an absolute URL, or a scheme-relative URL starting
+        with `//`, in which case the scheme of the request should be prepended.
+        """
+        return None
+    
     def get_changeset(self, rev):
         """Retrieve a Changeset corresponding to the given revision `rev`."""
         raise NotImplementedError
diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
--- a/trac/versioncontrol/cache.py
+++ b/trac/versioncontrol/cache.py
@@ -70,6 +70,9 @@
     def get_quickjump_entries(self, rev):
         return self.repos.get_quickjump_entries(self.normalize_rev(rev))
 
+    def get_path_url(self, path, rev):
+        return self.repos.get_path_url(path, rev)
+
     def get_changeset(self, rev):
         return CachedChangeset(self.repos, self.normalize_rev(rev),
                                self.env, self.authz)
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
@@ -280,7 +280,8 @@
             self.env.systeminfo.append(('Subversion', self._version))
         fs_repos = SubversionRepository(dir, None, self.log,
                                         {'tags': self.tags,
-                                         'branches': self.branches})
+                                         'branches': self.branches,
+                                         'url': options.get('url')})
         if type == 'direct-svnfs':
             repos = fs_repos
         else:
@@ -422,6 +423,13 @@
         for n in self._get_tags_or_branches('tags'):
             yield 'tags', n.path, n.created_path, n.created_rev
 
+    def get_path_url(self, path, rev):
+        url = self.options.get('url', '').rstrip('/')
+        if url:
+            if not path or path == '/':
+                return url
+            return url + '/' + path.lstrip('/')
+    
     def get_changeset(self, rev):
         rev = self.normalize_rev(rev)
         return SubversionChangeset(rev, self.authz, self.scope,
diff --git a/trac/versioncontrol/templates/admin_repositories.html b/trac/versioncontrol/templates/admin_repositories.html
--- a/trac/versioncontrol/templates/admin_repositories.html
+++ b/trac/versioncontrol/templates/admin_repositories.html
@@ -57,6 +57,9 @@
               <div class="field">
                 <label>Directory:<br/><input type="text" name="dir" size="48" value="$info.dir" readonly="$readonly"/></label>
               </div>
+              <div class="field">
+                <label>URL:<br/><input type="text" name="url" size="48" value="$info.url" readonly="$readonly"/></label>
+              </div>
             </py:otherwise>
           </py:choose>
           <div class="buttons">
diff --git a/trac/versioncontrol/web_ui/browser.py b/trac/versioncontrol/web_ui/browser.py
--- a/trac/versioncontrol/web_ui/browser.py
+++ b/trac/versioncontrol/web_ui/browser.py
@@ -408,6 +408,11 @@
 
         # Links for contextual navigation
         if node:
+            path_url = repos.get_path_url(path, rev)
+            if path_url:
+                if path_url.startswith('//'):
+                    path_url = req.scheme + ':' + path_url
+                add_ctxtnav(req, _('Repository URL'), href=path_url)
             add_ctxtnav(req, tag.a(_('Last Change'), 
                         href=req.href.changeset(node.rev, reponame,
                                                 node.created_path)))

