Add multiple support to hg serve

Goffredo Baroncelli kreijack at libero.it
Mon Jun 20 16:24:15 CDT 2005


Hi,

below is my second patch for supporting multiple repositories in hgweb, comments 
are accepted and required. The patch is NOT (yet) READY FOR PRODUCTION OR FOR INCLUSION 
in the mainstream.

Example of usage

$ hg serve --help
hg serve: option --help not recognized
hg serve [options] name,title,repos_path

 -p --port (8000)
   listen port
 -a --address
   interface address
 -t --templates
   template map

export the repository via HTTP



$ hg serve -p 8001 -t /home/ghigo/mercurial/dev-hgweb/templates \
  hgweb,"Multiple repository support for hgweb",/home/ghigo/mercurial/dev-hgweb/ \
  stable,"Stable repository",/home/ghigo/mercurial/stable \
  pull,"Check that the local repository exist",/home/ghigo/mercurial/dev-pull \
  multipleheads,"Examples of multiple heads",/home/ghigo/mercurial/multiple-heads/

and try the following URL [*]

http://kreijack.homelinux.net:8001/index.html

As side effect, it is possible not only to access multiple repositories via
web, but also pull a single repositories from the ones exported, adding the
name af the end of url: 
referring to the examples above you can pull directly the repository 'hgweb' (
the first one )

$ mkdir hg
$ cd hg
$ hg init http://selenic.com/hg
requesting all changes
adding changesets
adding manifests
adding file revisions
modified 93 files, added 397 changesets and 770 new revisions

$ hg pull http://kreijack.homelinux.net:8001/hgweb/
searching for changes
adding changesets
adding manifests
adding file revisions
modified 6 files, added 2 changesets and 7 new revisions

$ hg heads
changeset:   398:dd0ae12e69e4d120fbd19abede9c02afa279ed46
user:        ghigo at therra.bhome
date:        Mon Jun 20 22:51:25 2005
summary:     Added a template file missing

$ hg id
dd0ae12e69e4 tip


TODO:
- review the code
- better error handling
- spelling checker


Goffredo

NB:
If you are luky, some my repositories are online at the URL above on the hg patched; 
so you can see the effect of my patches. Please don't strees too much my
home-linuxbox :)

------------------------


 b/templates/index.tmpl      |   16 +++++++
 b/templates/indexrepo.tmpl  |    5 ++
 b/templates/reposheads.tmpl |    5 ++
 mercurial/commands.py       |   19 ++++++--
 mercurial/hgweb.py          |   95 +++++++++++++++++++++++++++++++++++++++-----
 templates/map               |    3 +
 6 files changed, 130 insertions(+), 13 deletions(-)



--- a/templates/map Fri Jun 17 19:37:23 2005
+++ b/templates/map Mon Jun 20 20:51:25 2005
@@ -28,3 +28,6 @@
 tags = tags.tmpl
 tagentry = "<div class="parity#parity#"><tt>#node#</tt> <a href="?cmd=changeset;node=#node#">#tag#</a><br /></div>"
 diffblock = "<div class="parity#parity#">#lines#</div>"
+index = index.tmpl
+indexrepo = indexrepo.tmpl
+reposheads = reposheads.tmpl
--- a/mercurial/commands.py Fri Jun 17 19:37:23 2005
+++ b/mercurial/commands.py Mon Jun 20 20:51:25 2005
@@ -545,9 +545,19 @@
     """remove the specified files on the next commit"""
     repo.remove(relpath(repo, (file,) + files))
 
-def serve(ui, repo, **opts):
+def serve(ui, repo, *paths, **opts):
     """export the repository via HTTP"""
-    hgweb.server(repo.root, opts["name"], opts["templates"],
+    r=[]
+    for i in paths: 
+        l=i.split(",")
+        if len(l)==3:
+            r=r+[l]
+        elif len(l)==2:
+            r=r+[(l[0],l[0],l[1])]
+        else:
+            raise "ERROR: not enough parameters"
+        
+    hgweb.server( r, opts["templates"],
                  opts["address"], opts["port"])
     
 def status(ui, repo):
@@ -663,9 +673,10 @@
     "remove": (remove, [], "hg remove [files]"),
     "serve": (serve, [('p', 'port', 8000, 'listen port'),
                       ('a', 'address', '', 'interface address'),
-                      ('n', 'name', os.getcwd(), 'repository name'),
+#                      ('n', 'name', os.getcwd(), 'repository name'),
+#                      ('P', 'name', os.getcwd(), 'repository path'),                      
                       ('t', 'templates', "", 'template map')],
-              "hg serve [options]"),
+              "hg serve [options] [name,]title,repos_path"),
     "status": (status, [], 'hg status'),
     "tags": (tags, [], 'hg tags'),
     "tip": (tip, [], 'hg tip'),
--- a/mercurial/hgweb.py Fri Jun 17 19:37:23 2005
+++ b/mercurial/hgweb.py Mon Jun 20 20:51:25 2005
@@ -1,3 +1,4 @@
+
 # hgweb.py - web interface to a mercurial repository
 #
 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake at edge2.net>
@@ -56,13 +57,17 @@
 def httphdr(type):
     print 'Content-type: %s\n' % type
 
-def write(*things):
+
+def writefile(f,*things):
     for thing in things:
         if hasattr(thing, "__iter__"):
             for part in thing:
-                write(part)
+                writefile(f,part)
         else:
-            sys.stdout.write(str(thing))
+            f.write(str(thing))
+
+def write(*things):
+    writefile(sys.stdout,*things)
 
 def template(tmpl, filters = {}, **map):
     while tmpl:
@@ -509,7 +514,6 @@
                                  path = os.path.join(path, f),
                                  manifest = mnode, basename = f[:-1])
                 parity = 1 - parity
-
         yield self.t("manifest",
                      header = self.header(),
                      footer = self.footer(),
@@ -653,11 +657,57 @@
         else:
             write(self.t("error"))
 
-def server(path, name, templates, address, port):
+def print_index( templates, reps,  repositories ):
+    templates = templates or templatepath()
+    m = os.path.join( templates, "map")
+    t = templater((m),{
+            "escape": cgi.escape,
+            "age": age,
+            "date": (lambda x: time.asctime(time.gmtime(x))),
+            "addbreaks": nl2br,
+            "obfuscate": obfuscate,
+            "short": (lambda x: x[:12]),
+            "firstline": (lambda x: x.splitlines(1)[0]),
+            "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--")
+            } )
+
+    def get_repoheads(name):
+        for (n,hgw ) in reps:
+          if n == name:
+              hgw.refresh()
+              hs=hgw.repo.changelog.heads( )
+              for i in hs:
+                  changes=hgw.repo.changelog.read(i)
+                  yield t("reposheads",
+                    name = name,
+                    date = float(changes[2].split(' ')[0]),
+                    desc = changes[4],
+                    node = hex(i))
+        
+    def index_entry( ):
+        parity = 0
+        for (name,title,path) in repositories:
+            yield t("indexrepo",
+                  repname=name,
+                  title=title,
+                  parity=parity,
+                  reposheads=get_repoheads(name))
+            parity = 1-parity
+
+
+    return t("index",
+          header=t("header"),
+          footer=t("footer"),
+          entries = index_entry() )
+
+
+def server(repositories, templates, address, port):
 
     import BaseHTTPServer
     import sys, os
 
+    reps=[]
+    
     class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
         def do_POST(self):
             try:
@@ -671,10 +721,14 @@
         def do_hgweb(self):
             query = ""
             p = self.path.find("?")
-            if p:
+            if p>=0:
                 query = self.path[p + 1:]
                 query = query.replace('+', ' ')
-        
+                rep = self.path[1:p-1]
+            else:
+                query=self.path
+                rep=""
+
             env = {}
             env['GATEWAY_INTERFACE'] = 'CGI/1.1'
             env['REQUEST_METHOD'] = self.command
@@ -710,10 +764,33 @@
                 if '=' not in query:
                     sys.argv.append(query)
                 self.send_response(200, "Script output follows")
-                hg.run()
+
+                f=0
+                if query == "/index.html":
+                     writefile(self.wfile, print_index( templates, reps, repositories ))
+                     f=1
+                for (p,hgw) in reps:
+                        if p == rep: 
+                            hgw.run()
+                            f=1
+
+                if not f: 
+                    # TODO: rewrite it !!!!!!!!
+                    sys.stdout.write("""
+                    <HTML><head><title>Page error</title></head>
+                    <BODY>
+                      You see this page because the url '%s' is
+                      incorrect !<br>
+                      Click <a href=/index.html>here</a> for the repository index
+                    </BODY>
+                    </HTML>
+                    """%(self.path))
+
             finally:
                 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
 
-    hg = hgweb(path, name, templates)
+    for (name,title,path) in repositories:
+        reps=reps+[(name,hgweb(path, title, templates))]
+        
     httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
     httpd.serve_forever()
--- /dev/null Fri Jun 17 19:37:23 2005
+++ b/templates/indexrepo.tmpl Mon Jun 20 20:51:25 2005
@@ -0,0 +1,5 @@
+<tr class="parity#parity#">
+  <td><a href="#repname#/?"><h3>#title#</h3></a></td>
+  <td><table>#reposheads#</table></td>
+</tr>
+
--- /dev/null Fri Jun 17 19:37:23 2005
+++ b/templates/index.tmpl Mon Jun 20 20:51:25 2005
@@ -0,0 +1,16 @@
+#header#
+<title>Mercurial repositories index</title>
+</head>
+<body>
+
+<div class="buttons">
+<a href="http://selenic.com/mercurial">mercurial</a>
+</div>
+
+<h2>Mercurial repositories index</h2>
+
+<table width="100%" cellpadding="0" cellspacing="0">
+   #entries#
+</table>
+
+#footer#
--- /dev/null Fri Jun 17 19:37:23 2005
+++ b/templates/reposheads.tmpl Mon Jun 20 20:51:25 2005
@@ -0,0 +1,5 @@
+  <tr>
+    <td>#date|date#</td>
+    <td><a href="/#name#/?cmd=changeset;node=#node#">#node|short#</a></td>
+    <td>#desc|escape|firstline#</td></tr>
+  </tr>

-- 
gpg key@ keyserver.linux.it: Goffredo Baroncelli (ghigo) <kreijack AT inwind DOT it>
Key fingerprint = CE3C 7E01 6782 30A3 5B87  87C0 BB86 505C 6B2A CFF9


More information about the Mercurial mailing list