[PATCH 1/1] Bazaar web interface

Goffredo Baroncelli kreijack at inwind.it
Sat Nov 12 10:26:08 GMT 2005


 README.html                           |  351 ++++++++
 README.txt                            |  281 ++++++
 TODO                                  |    3
 __init__.py                           |   42
 hgweb.cgi                             |   13
 hgweb.py                              | 1432 +++++++++++++++++++++++++++++++
 hgwebdir.cgi                          |   18
 hgwebdir.config.examples              |   23
 templates/changefilelog-rss.tmpl      |    7
 templates/changefilelog.tmpl          |   44 +
 templates/changefilelogentry-rss.tmpl |    8
 templates/changefilelogentry.tmpl     |   20
 templates/changelog-rss.tmpl          |    7
 templates/changelog.tmpl              |   72 +
 templates/changelogentry-rss.tmpl     |    8
 templates/changelogentry.tmpl         |   23
 templates/diffentry.tmpl              |   19
 templates/diffpage.tmpl               |   58 +
 templates/error.tmpl                  |   15
 templates/filecontent-raw.tmpl        |    2
 templates/filecontent.tmpl            |   74 +
 templates/footer.tmpl                 |    2
 templates/header-raw.tmpl             |    1
 templates/header-rss.tmpl             |    7
 templates/header.tmpl                 |   91 ++
 templates/index.tmpl                  |   18
 templates/inventory.tmpl              |   67 +
 templates/map                         |   76 +
 templates/map-raw                     |    3
 templates/map-rss                     |    5
 templates/navigation.tmpl             |   16
 templates/pathnotfound.tmpl           |   16
 templates/revision.tmpl               |   70 +
 templates/revisionnotfound.tmpl       |   15
 templates/textnotfound.tmpl           |   15



=== added directory 'bzrlib/plugins/webserve'
=== added file 'bzrlib/plugins/webserve/README.html'
--- /dev/null
+++ bzrlib/plugins/webserve/README.html
@@ -0,0 +1,351 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head><TITLE>Bazaar-NG web interface</TITLE></head>
+<body>
+
+
+<center>
+<h3>
+Bazaar-NG web interface
+</h3>
+<hr width=80%>
+</center>
+
+<i><small>The latest revision of this documents can be download at
+<a href="http://goffredo-baroncelli.homelinux.net/bzr/?cmd=content;rev=;path=bzrlib/plugins/webserve/README.html;pathrevid=;style=raw">http://goffredo-baroncelli.homelinux.net/bzr/?cmd=content;rev=;path=bzrlib/plugins/webserve/README.html;pathrevid=;style=raw</a></small></i>
+
+
+<p><b>Introduction</b></p>
+<p>This is the porting of the <a href=http://selenic.com/mercurial>mercurial</a> web interfaces to 
+<a href=http://bazaar-ng.org>bazaar-ng</a>; todate the porting is implemented as plugin. 
+
+
+<p><b>Index</b></p>
+<ul>
+	<LI><a href="#feat">Features</a></LI>
+	<LI><a href="#usage">Usage</a></LI>
+	<LI><a href="#internal">Internal</a></LI>
+	<LI><a href="#bug">Know bugs</a></LI>
+	<LI><a href="#todo">ToDo</a></LI>
+	<LI><a href="#download">Download</a></LI>
+</ul>
+
+<b><a name="feat">Features</a></b>
+
+<ul>
+    <li> Display the changelog of the repository 
+	<a href="http://goffredo-baroncelli.homelinux.net/bzr/">
+		http://goffredo-baroncelli.homelinux.net/bzr/</a>
+	or the changelog of a file
+	<a href="http://goffredo-baroncelli.homelinux.net/bzr/?cmd=changelog;rev=;pathrevid=;path=bzrlib/plugins/webserve/hgweb.py">
+	http://goffredo-baroncelli.homelinux.net/bzr/?cmd=changelog;rev=;pathrevid=;path=bzrlib/plugins/webserve/hgweb.py</a>
+
+    <li> Display the information about revision ( file changed/removed/renamed/added 
+      ) and a colorized diff; it is also possible to jump to the involved files
+	<a href="http://goffredo-baroncelli.homelinux.net/bzr/?cmd=revision;rev=">http://goffredo-baroncelli.homelinux.net/bzr/?cmd=revision;rev=</a>
+    <li> Display a file in the annotation mode
+	<a href="http://goffredo-baroncelli.homelinux.net/bzr/?cmd=content;path=bzrlib/plugins/webserve/README.html;pathrevid=;rev=">http://goffredo-baroncelli.homelinux.net/bzr/?cmd=content;path=bzrlib/plugins/webserve/README.html;pathrevid=;rev=</a>
+
+    <li> Display the inventory of the repository
+	<a href="http://goffredo-baroncelli.homelinux.net/bzr/?cmd=inventory;pathrevid=;path=bzrlib/plugins/webserve/;rev=">http://goffredo-baroncelli.homelinux.net/bzr/?cmd=inventory;pathrevid=;path=bzrlib/plugins/webserve/;rev=</a>
+
+    <li> Provide the changelog ( also on file basis ) via rss
+	<a href="http://goffredo-baroncelli.homelinux.net/bzr/?cmd=changelog;style=rss">
+	http://goffredo-baroncelli.homelinux.net/bzr/?cmd=changelog;style=rss</a>
+
+    <li> Jump from an annotation to the linked revision
+    <li> Search engine on the changelog
+	<a href="http://goffredo-baroncelli.homelinux.net/bzr/?rev=&amp;pathrevid=None&amp;path=None&amp;cmd=search&amp;w=annotate#">
+	http://goffredo-baroncelli.homelinux.net/bzr/?rev=&amp;pathrevid=None&amp;path=None&amp;cmd=search&amp;w=annotate#</a>
+
+    <li> Diff between two revisions: <a href="http://goffredo-baroncelli.homelinux.net/bazaar/hgweb_devel?cmd=diff;otherrevid=ghigo@therra.bhome-20050928175933-8d219cdf3b76841d;rev=ghigo@therra.bhome-20051106211328-87d304e1e7145b9d;pathrevid=ghigo@therra.bhome-20051106211328-87d304e1e7145b9d;path=bzrlib/plugins/webserve/templates/revision.tmpl">http://goffredo-baroncelli.homelinux.net/bazaar/hgweb_devel?cmd=diff;otherrevid=ghigo@therra.bhome-20050928175933-8d219cdf3b76841d;rev=ghigo@therra.bhome-20051106211328-87d304e1e7145b9d;pathrevid=ghigo@therra.bhome-20051106211328-87d304e1e7145b9d;path=bzrlib/plugins/webserve/templates/revision.tmpl</a>
+
+    <li> Download the file content
+	<a href="http://goffredo-baroncelli.homelinux.net/bzr/?cmd=content;rev=;path=NEWS;pathrevid=;style=raw">
+	http://goffredo-baroncelli.homelinux.net/bzr/?cmd=content;rev=;path=NEWS;pathrevid=;style=raw</a>
+	
+    <li> Multiple repositories support <a href="http://goffredo-baroncelli.homelinux.net/bazaar">http://goffredo-baroncelli.homelinux.net/bazaar</a>
+
+    <li> Web interface for remote repository: try 
+	<pre>bzr webserve --port=9099 "knit" http://people.ubuntu.com/~mbp/bzr.mbp.knit</pre>
+	and go to http://127.0.0.1:9099....
+
+    <li> Download of a tar/zip archive of the selected revision.
+
+
+     
+</ul>
+
+<p><a name="usage"><b>Usage</b></a>
+
+<p>The web interface can be started in three ways
+
+<ol>
+  <li> <em>standalone server</em>: use 'bzr serve' command
+
+<pre>
+  $ bzr serve --help
+  usage: serve [NAME] [ROOTREPOSITORY]
+
+  Start the webserver
+
+  options:
+    --templates
+    --address
+    --port
+    --ipv6
+    --acceslog
+    --errorlog
+    --lock
+    --profile 
+    --tararchive
+
+</pre>
+
+  where
+<pre>
+        NAME            -&gt; name of the repository ( default the current dir )
+        ROOTREPOSITORY  -&gt; repository directory ( default the current dir ) 
+                           or an URL ( currently only http:// transport is supported )
+        --templates     -&gt; set the template directory ( default the 
+                                ROOTREPOSITORY )
+        --address       -&gt; interface to listen
+        --port          -&gt; port to listen ( default 8088 )
+        --ipv6          -&gt; ipv6 enable/disable ( untested )
+        --accesslog     -&gt; accesslog file ( default stderr )
+        --errorlog      -&gt; errorlog file ( default stderr )
+	--lock		-&gt; enable the locking of the repository when a request
+			   is performed
+    	--profile       -&gt; show the profile info at the bottom of page
+    	--tararchive    -&gt; permit the download of the archive as a tar.gz file
+
+</pre>
+
+  <p>After the start of the server with the default parameters ( bzr serve ),
+  you can browse the repository at the address: 'http://127.0.0.1:8088/'
+
+<li> <em>cgi script via apache</em>: configure apache to use the 'hgweb.cgi' python 
+script as the default index script. Below is an example of the httpd.conf file
+
+<pre>
+        &lt;Directory BlaBlaBla/BlaBlaBla/bzr&gt;
+                DirectoryIndex index.html hgweb.cgi
+                AllowOverride None
+                Options ExecCGI -MultiViews +SymLinksIfOwnerMatch Indexes
+                Order allow,deny
+                Allow from all
+                AddHandler cgi-script .cgi
+        &lt;/Directory&gt;
+</pre>
+
+<p>You have to change the script 'hgweb.cgi' in order to set the title, the
+template path, and the bzrlib path via the sys.path variable.
+
+
+<li> <em>cgi script via apache with multiple repositories</em>: the pache configuration
+is like the previous one, the only difference is that you have to use the 
+cgi script 'hgwebdir.cgi'. Moreover you have to prepare the configure file
+hgwebdir.config ( see the file 'hgwebdir.config.examples' as examples ) in order
+to manage your repositories layout. 
+
+
+
+<p>If you have a repositories layout like:
+
+<pre>
+Repositories1:    BlaBlaBla/BlaBlaBla/bzr/repo1
+Repositories2:    BlaBlaBla/BlaBlaBla/bzr/repo2
+Repositories3:    BlaBlaBla/BlaBlaBla/bzr/repo3
+</pre>
+
+a tipical configuration should be:
+<ul>
+
+<li>Apache:
+<pre>
+        Alias /bazaar BlaBlaBla/BlaBlaBla/bzr/hgwebdir.cgi
+        &lt;Directory BlaBlaBla/BlaBlaBla/bzr&gt;
+                AllowOverride None
+                Options ExecCGI -MultiViews +SymLinksIfOwnerMatch Indexes
+                Order allow,deny
+                Allow from all
+                AddHandler cgi-script .cgi
+        &lt;/Directory&gt;
+</pre>
+
+  <p>You have to copy the hgwebdir.cgi script under BlaBlaBla/BlaBlaBla/bzr/
+
+  <p>With the above configuration you can browse the repositories starting on
+    <pre>http://&lt;address of webserver&gt;/bazaar. </pre>
+
+  <p>Every repository will be browsable at
+
+<pre>
+    http://&lt;address of webserver&gt;/bazaar/repo1
+    http://&lt;address of webserver&gt;/bazaar/repo2
+    [...]
+</pre>
+
+  <p>If you also want to pull the source, you have to add the following alias 
+  to the apache config file
+<pre>
+        Alias /bazaar/repo1/.bzr BlaBlaBla/BlaBlaBla/bzr/repo1/.bzr
+	Alias /bazaar/repo2/.bzr BlaBlaBla/BlaBlaBla/bzr/repo2/.bzr
+	[...]
+</pre>
+
+<li>hgwebdir.config ( under BlaBlaBla/BlaBlaBla/bzr/ )
+<pre>
+        [paths]
+        repositories1 = BlaBlaBla/BlaBlaBla/bzr/repo1
+        repositories2 = BlaBlaBla/BlaBlaBla/bzr/repo2
+
+	[options]
+	; if 1, it is allowable to download a tar of the archive
+	tararchive = 1
+	; if 1, the bottom of the page contains the profiling informations
+	profile = 0
+	; if 1, during a command a read_lock is performed
+	lock = 0
+
+        [repositories1]
+        author = Goffredo Baroncelli
+        name  = repo1
+        description = bazaar-ng stable branch
+        email = email at email.email
+
+        [repositories2]
+        author = Goffredo Baroncelli
+        name  = repo2
+        description = bazaar-ng branch two
+        email = email at email.email
+</pre>
+
+<p> For every repositories the [paths] section contains a touple
+   &lt;repoid&gt; = &lt;repopath&gt;, 
+  and a section named [&lt;repoid&gt;] has to be specified 
+  where 
+  <ul>
+        <li><strong>&lt;repoid&gt;</strong>:        repository path in the url
+	<li><strong>&lt;repopath&gt;</strong>:      path of repository in the filesystem
+	<li><strong>name</strong>:            name of the repository
+	<li><strong>description</strong>:     short repository description
+	<li><strong>email</strong>:           author email
+	<li><strong>author</strong>:          author/responsible
+  </ul>
+<br>
+<p> The [options] section contains the following options:
+<ul>
+	<LI><b>tararchive</b> permit the download of the archive as a tar.gz/zip file
+	<li><b>lock</b> enable the locking of the repository when a request is performed
+	<li><b>profile</b> show the profile info at the bottom of page
+</ul>
+<br>
+
+<p>Moreover you have to change the script 'hgwebdir.cgi' in order to set the 
+bzrlib path (via the sys.path variable), and the config file path.
+</ul>
+
+<p>Below you can download my config files
+<ul>
+	<LI><a href=http://goffredo-baroncelli.homelinux.net/bzr-docs/hgwebdir.config>hgwebdir.config</a>
+	<LI><a href=http://goffredo-baroncelli.homelinux.net/bzr-docs/hgwebdir.cgi>hgwebdir.cgi</a>
+	<LI><a href=http://goffredo-baroncelli.homelinux.net/bzr-docs/httpd.conf>httpd.conf</a>
+
+</ul>
+</ol>
+
+<p><a name="internal"><b>Internal</b></a>
+
+<p>The code of the web interface is based on expansion of a template files. Under 
+the directory templates/ there are files which are the skeleton of the html 
+pages. These files have field like '#&lt;field name&gt;#' which the code expand 
+with a value or with another template expansion....
+
+<p>In code:
+<pre>
+        $ cat templatefile
+        &lt;html&gt;
+        &lt;body&gt;
+        &lt;table&gt;
+            #rows#
+        &lt;/table&gt;
+        &lt;/body&gt;
+        &lt;/html&gt;
+
+        $ cat templaterows
+        &lt;tr&gt;
+           &lt;td&gt;#tag#
+
+        $
+
+        [...]
+        t = templater("mapfile")
+
+        def func( ):
+                for i in range(1,10):
+                        yield t("templaterows", tag = str(i) )
+
+
+        write(t("templatefile", rows = func( ) ))
+        [...]
+
+</pre>
+
+<p>the code above, takes the contents of the file "templatefile" and
+replace every string '#tag#' with the value(s) returned by
+the function func( ).
+
+<p>The function func( ) return 9 times the content of the file 
+"templaterows", replacing the field '#tag#' with a value
+between 1 and 9.
+
+<p>Because the function 'func( )' return 9 different values,
+the unique field '#rows#' is expanded 9 times.
+The 'mapfile' file contains the mapping between the name of the
+template name and the template filename...
+
+<p>The goal is to separate the html code to the python code. In fact the
+template contains the major part of the html code, only the
+dynamic part are generated by the python code, tipically on the basis of another
+template; an example is the changelog web page: the main page is
+a template, where the table rows are an another template expansion...
+
+<p><a name="bug"><b>Bug</b></a>
+<ul>
+    <li>A pathname can't contain a ';' 
+</ul>
+
+<p><a name="todo"><b>Todo</b></a>
+<ul>
+    <li> BugFix, BugFix, BugFix
+    <li> <s>Manage the tar archive</s> [2005/11/08]
+    <li> Implement raw diff and raw revision
+    <li> <s>Implement a diff view between two revisions</s> [2005/11/07]
+    <li> <s>Implement the lock code</s> [2005/11/07]
+    <li> Group the code which require a call to the bzr library into specific functions.
+</ul>
+
+<p><a name="download"><b>Download</b></a>
+<p>You can download a tar archive of the plugin at the following address:
+<a href="http://goffredo-baroncelli.homelinux.net/bzr/tar">
+	http://goffredo-baroncelli.homelinux.net/bzr/tar
+</a>.
+<br>
+
+<p>You can pull the source at the following address:
+<pre>
+	bzr pull http://goffredo-baroncelli.homelinux.net/bazaar/hgweb_devel
+</pre>
+<br>
+
+
+
+
+<center><hr width=80%></center>
+
+<div align="left">
+	Goffredo Baroncelli <i>&lt;kreijack AT inwind DOT it&gt;</i>
+</div>
+</body>
+</html>
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/README.txt'
--- /dev/null
+++ bzrlib/plugins/webserve/README.txt
@@ -0,0 +1,281 @@
+
+                            Bazaar-NG web interface
+
+-------------------------------------------------------------------------------
+The latest revision of this documents can be download at http://goffredo-
+baroncelli.homelinux.net/bzr/?cmd=content;rev=;path=bzrlib/plugins/webserve/
+README.html;pathrevid=;style=raw
+Introduction
+This is the porting of the mercurial web interfaces to bazaar-ng; todate the
+porting is implemented as plugin.
+Index
+
+* Features
+* Usage
+* Internal
+* Know_bugs
+* ToDo
+* Download
+
+Features
+
+* Display the changelog of the repository http://goffredo-
+  baroncelli.homelinux.net/bzr/ or the changelog of a file http://goffredo-
+  baroncelli.homelinux.net/bzr/?cmd=changelog;rev=;pathrevid=;path=bzrlib/
+  plugins/webserve/hgweb.py
+* Display the information about revision ( file changed/removed/renamed/added )
+  and a colorized diff; it is also possible to jump to the involved files http:
+  //goffredo-baroncelli.homelinux.net/bzr/?cmd=revision;rev=
+* Display a file in the annotation mode http://goffredo-
+  baroncelli.homelinux.net/bzr/?cmd=content;path=bzrlib/plugins/webserve/
+  README.html;pathrevid=;rev=
+* Display the inventory of the repository http://goffredo-
+  baroncelli.homelinux.net/bzr/?cmd=inventory;pathrevid=;path=bzrlib/plugins/
+  webserve/;rev=
+* Provide the changelog ( also on file basis ) via rss http://goffredo-
+  baroncelli.homelinux.net/bzr/?cmd=changelog;style=rss
+* Jump from an annotation to the linked revision
+* Search engine on the changelog http://goffredo-baroncelli.homelinux.net/bzr/
+  ?rev=&pathrevid=None&path=None&cmd=search&w=annotate#
+* Diff between two revisions: http://goffredo-baroncelli.homelinux.net/bazaar/
+  hgweb_devel?cmd=diff;otherrevid=ghigo at therra.bhome-20050928175933-
+  8d219cdf3b76841d;rev=ghigo at therra.bhome-20051106211328-
+  87d304e1e7145b9d;pathrevid=ghigo at therra.bhome-20051106211328-
+  87d304e1e7145b9d;path=bzrlib/plugins/webserve/templates/revision.tmpl
+* Download the file content http://goffredo-baroncelli.homelinux.net/bzr/
+  ?cmd=content;rev=;path=NEWS;pathrevid=;style=raw
+* Multiple repositories support http://goffredo-baroncelli.homelinux.net/bazaar
+* Web interface for remote repository: try
+
+    bzr webserve --port=9099 "knit" http://people.ubuntu.com/~mbp/bzr.mbp.knit
+
+  and go to http://127.0.0.1:9099....
+* Download of a tar/zip archive of the selected revision.
+
+Usage
+The web interface can be started in three ways
+
+  1. standalone server: use 'bzr serve' command
+
+         $ bzr serve --help
+         usage: serve [NAME] [ROOTREPOSITORY]
+
+         Start the webserver
+
+         options:
+           --templates
+           --address
+           --port
+           --ipv6
+           --acceslog
+           --errorlog
+           --lock
+           --profile
+           --tararchive
+
+       where
+
+               NAME            -> name of the repository ( default the current
+       dir )
+               ROOTREPOSITORY  -> repository directory ( default the current
+       dir )
+                                  or an URL ( currently only http:// transport
+       is supported )
+               --templates     -> set the template directory ( default the
+                                       ROOTREPOSITORY )
+               --address       -> interface to listen
+               --port          -> port to listen ( default 8088 )
+               --ipv6          -> ipv6 enable/disable ( untested )
+               --accesslog     -> accesslog file ( default stderr )
+               --errorlog      -> errorlog file ( default stderr )
+       	--lock		-> enable the locking of the repository when a request
+       			   is performed
+           	--profile       -> show the profile info at the bottom of page
+           	--tararchive    -> permit the download of the archive as a tar.gz
+       file
+
+      
+     After the start of the server with the default parameters ( bzr serve ),  
+     you can browse the repository at the address: 'http://127.0.0.1:8088/'
+  2. cgi script via apache: configure apache to use the 'hgweb.cgi' python
+     script as the default index script. Below is an example of the httpd.conf
+     file
+
+               <Directory BlaBlaBla/BlaBlaBla/bzr>
+                       DirectoryIndex index.html hgweb.cgi
+                       AllowOverride None
+                       Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
+       Indexes
+                       Order allow,deny
+                       Allow from all
+                       AddHandler cgi-script .cgi
+               </Directory>
+
+     You have to change the script 'hgweb.cgi' in order to set the title, the
+     template path, and the bzrlib path via the sys.path variable.
+  3. cgi script via apache with multiple repositories: the pache configuration
+     is like the previous one, the only difference is that you have to use the
+     cgi script 'hgwebdir.cgi'. Moreover you have to prepare the configure file
+     hgwebdir.config ( see the file 'hgwebdir.config.examples' as examples ) in
+     order to manage your repositories layout.
+     If you have a repositories layout like:
+
+       Repositories1:    BlaBlaBla/BlaBlaBla/bzr/repo1
+       Repositories2:    BlaBlaBla/BlaBlaBla/bzr/repo2
+       Repositories3:    BlaBlaBla/BlaBlaBla/bzr/repo3
+
+     a tipical configuration should be:
+
+     o Apache:
+
+                 Alias /bazaar BlaBlaBla/BlaBlaBla/bzr/hgwebdir.cgi
+                 <Directory BlaBlaBla/BlaBlaBla/bzr>
+                         AllowOverride None
+                         Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
+         Indexes
+                         Order allow,deny
+                         Allow from all
+                         AddHandler cgi-script .cgi
+                 </Directory>
+
+       You have to copy the hgwebdir.cgi script under BlaBlaBla/BlaBlaBla/bzr/
+       With the above configuration you can browse the repositories starting on
+
+         http://<address of webserver>/bazaar.
+
+       Every repository will be browsable at
+
+             http://<address of webserver>/bazaar/repo1
+             http://<address of webserver>/bazaar/repo2
+             [...]
+
+       If you also want to pull the source, you have to add the following alias
+       to the apache config file
+
+                 Alias /bazaar/repo1/.bzr BlaBlaBla/BlaBlaBla/bzr/repo1/.bzr
+         	Alias /bazaar/repo2/.bzr BlaBlaBla/BlaBlaBla/bzr/repo2/.bzr
+         	[...]
+
+     o hgwebdir.config ( under BlaBlaBla/BlaBlaBla/bzr/ )
+
+                 [paths]
+                 repositories1 = BlaBlaBla/BlaBlaBla/bzr/repo1
+                 repositories2 = BlaBlaBla/BlaBlaBla/bzr/repo2
+
+         	[options]
+         	; if 1, it is allowable to download a tar of the archive
+         	tararchive = 1
+         	; if 1, the bottom of the page contains the profiling informations
+         	profile = 0
+         	; if 1, during a command a read_lock is performed
+         	lock = 0
+
+                 [repositories1]
+                 author = Goffredo Baroncelli
+                 name  = repo1
+                 description = bazaar-ng stable branch
+                 email = email at email.email
+
+                 [repositories2]
+                 author = Goffredo Baroncelli
+                 name  = repo2
+                 description = bazaar-ng branch two
+                 email = email at email.email
+
+       For every repositories the [paths] section contains a touple <repoid> =
+       <repopath>, and a section named [<repoid>] has to be specified where
+
+       # <repoid>: repository path in the url
+       # <repopath>: path of repository in the filesystem
+       # name: name of the repository
+       # description: short repository description
+       # email: author email
+       # author: author/responsible
+
+
+       The [options] section contains the following options:
+
+       # tararchive permit the download of the archive as a tar.gz/zip file
+       # lock enable the locking of the repository when a request is performed
+       # profile show the profile info at the bottom of page
+
+
+       Moreover you have to change the script 'hgwebdir.cgi' in order to set
+       the bzrlib path (via the sys.path variable), and the config file path.
+
+     Below you can download my config files
+
+     o hgwebdir.config
+     o hgwebdir.cgi
+     o httpd.conf
+
+
+Internal
+The code of the web interface is based on expansion of a template files. Under
+the directory templates/ there are files which are the skeleton of the html
+pages. These files have field like '#<field name>#' which the code expand with
+a value or with another template expansion....
+In code:
+
+          $ cat templatefile
+          <html>
+          <body>
+          <table>
+              #rows#
+          </table>
+          </body>
+          </html>
+
+          $ cat templaterows
+          <tr>
+             <td>#tag#
+
+          $
+
+          [...]
+          t = templater("mapfile")
+
+          def func( ):
+                  for i in range(1,10):
+                          yield t("templaterows", tag = str(i) )
+
+
+          write(t("templatefile", rows = func( ) ))
+          [...]
+
+the code above, takes the contents of the file "templatefile" and replace every
+string '#tag#' with the value(s) returned by the function func( ).
+The function func( ) return 9 times the content of the file "templaterows",
+replacing the field '#tag#' with a value between 1 and 9.
+Because the function 'func( )' return 9 different values, the unique field
+'#rows#' is expanded 9 times. The 'mapfile' file contains the mapping between
+the name of the template name and the template filename...
+The goal is to separate the html code to the python code. In fact the template
+contains the major part of the html code, only the dynamic part are generated
+by the python code, tipically on the basis of another template; an example is
+the changelog web page: the main page is a template, where the table rows are
+an another template expansion...
+Bug
+
+* A pathname can't contain a ';'
+
+Todo
+
+* BugFix, BugFix, BugFix
+* Manage the tar archive [2005/11/08]
+* Implement raw diff and raw revision
+* Implement a diff view between two revisions [2005/11/07]
+* Implement the lock code [2005/11/07]
+* Group the code which require a call to the bzr library into specific
+  functions.
+
+Download
+You can download a tar archive of the plugin at the following address: http://
+goffredo-baroncelli.homelinux.net/bzr/tar.
+You can pull the source at the following address:
+
+  	bzr pull http://goffredo-baroncelli.homelinux.net/bazaar/hgweb_devel
+
+
+-------------------------------------------------------------------------------
+Goffredo Baroncelli <kreijack AT inwind DOT it>

=== added file 'bzrlib/plugins/webserve/TODO'
--- /dev/null
+++ bzrlib/plugins/webserve/TODO
@@ -0,0 +1,3 @@
+- review of the lock code
+
+

=== added file 'bzrlib/plugins/webserve/__init__.py'
--- /dev/null
+++ bzrlib/plugins/webserve/__init__.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+"""\
+web interface
+"""
+
+import bzrlib, bzrlib.commands
+import os
+import hgweb, bzrlib.option
+
+class cmd_webserve(bzrlib.commands.Command):
+   """Start the webserver"""
+
+   takes_options = ['templates', 'address', 'port', 'ipv6', 'acceslog',
+        'errorlog',"lock","profile","tararchive"]
+   takes_args = ['name?', 'rootrepository?']
+   def run(self, name = None, rootrepository = None,
+            templates = None, address = "", port = 8088, ipv6 = None,
+            accesslog = '', errorlog = '', dolock=None, profile=None,
+            tararchive=None ):
+        #import hgweb
+
+        if rootrepository == None: rootrepository = os.getcwd( )
+        if name == None: name = os.path.basename( rootrepository )
+        #if templates == None:
+        #    templates = os.path.join( rootrepository, "templates")
+
+        httpd = hgweb.create_server(rootrepository, name, templates,
+                 address, port, ipv6, accesslog, errorlog,
+                 dolock, tararchive, profile)
+        httpd.serve_forever()
+
+bzrlib.option._global_option('templates', type=str)
+bzrlib.option._global_option('address', type=str)
+bzrlib.option._global_option('port', type=int)
+bzrlib.option._global_option('ipv6')
+bzrlib.option._global_option('acceslog', type=str)
+bzrlib.option._global_option('errorlog', type=str)
+bzrlib.option._global_option('lock')
+bzrlib.option._global_option('profile')
+bzrlib.option._global_option('tararchive')
+
+bzrlib.commands.register_command(cmd_webserve)

=== added file 'bzrlib/plugins/webserve/hgweb.cgi'
--- /dev/null
+++ bzrlib/plugins/webserve/hgweb.cgi
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+#
+# An example CGI script to use hgweb, edit as necessary
+
+import cgitb, os, sys
+cgitb.enable()
+
+# sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
+from bzrlib.plugins.webserve import hgweb
+
+h = hgweb.hgweb(os.getcwd( ), "Experimental bazaar-ng web interface", 
+	"bzrlib/plugins/webserve/templates" )
+h.run()

=== added file 'bzrlib/plugins/webserve/hgweb.py'
--- /dev/null
+++ bzrlib/plugins/webserve/hgweb.py
@@ -0,0 +1,1432 @@
+# hgweb.py - web interface to a mercurial repository
+#
+# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake at edge2.net>
+# Copyright 2005 Matt Mackall <mpm at selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+import os, cgi, time, re, difflib, socket, sys, zlib, ConfigParser
+
+from bzrlib.log import _enumerate_history
+from bzrlib.osutils import format_date
+from bzrlib.diff import show_diff_trees
+from bzrlib.errors import *
+from bzrlib.revisionspec import RevisionSpec
+import bzrlib
+import bzrlib.branch
+from bzrlib.annotate import _annotate_file
+from bzrlib.delta import compare_trees
+from bzrlib.tree import EmptyTree
+from bzrlib.errors import *
+from cStringIO import StringIO
+
+def templatepath():
+    p = os.path.join(os.getcwd( ),"templates")
+    if os.path.isdir(p): return p
+
+    for f in "templates", "../templates":
+        p = os.path.join(os.path.dirname(__file__), f)
+        if os.path.isdir(p): return p
+
+def age(t):
+    if t == 0:
+        return "Unavailable"
+
+    def plural(t, c):
+        if c == 1: return t
+        return t + "s"
+    def fmt(t, c):
+        return "%d %s" % (c, plural(t, c))
+
+    now = time.time()
+    delta = max(1, int(now - t))
+
+    scales = [["second", 1],
+              ["minute", 60],
+              ["hour", 3600],
+              ["day", 3600 * 24],
+              ["week", 3600 * 24 * 7],
+              ["month", 3600 * 24 * 30],
+              ["year", 3600 * 24 * 365]]
+
+    scales.reverse()
+
+    for t, s in scales:
+        n = delta / s
+        if n >= 2 or s == 1: return fmt(t, n)
+
+def nl2br(text):
+    return text.replace('\n', '<br/>\n')
+
+def obfuscate(text):
+    return ''.join([ '&#%d;' % ord(c) for c in text ])
+
+def up(p):
+    if p[0] != "/": p = "/" + p
+    if p[-1] == "/": p = p[:-1]
+    up = os.path.dirname(p)
+    if up == "/":
+        return "/"
+    return up + "/"
+
+def httphdr(type):
+    sys.stdout.write('Content-type: %s\n\n' % type)
+
+def write(*things):
+    for thing in things:
+        if hasattr(thing, "__iter__"):
+            for part in thing:
+                write(part)
+        else:
+            sys.stdout.write(str(thing))
+
+def template(tmpl, filters = {}, **map):
+    while tmpl:
+        m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl)
+        if m:
+            yield tmpl[:m.start(0)]
+            v = map.get(m.group(1), "")
+            v = callable(v) and v(**map) or v
+
+            fl = m.group(2)
+            if fl:
+                for f in fl.split("|")[1:]:
+                    v = filters[f](v)
+
+            yield v
+            tmpl = tmpl[m.end(0):]
+        else:
+            yield tmpl
+            return
+
+class templater:
+    def __init__(self, mapfile, filters = {}, defaults = {}):
+        self.cache = {}
+        self.map = {}
+        self.base = os.path.dirname(mapfile)
+        self.filters = filters
+        self.defaults = defaults
+
+        for l in file(mapfile):
+            while len(l) > 0 and l[-1] in "\n\r": l = l[:-1]
+            ls = l.strip( )
+            if ls == "": continue
+            if len(ls ) > 0 and ls[0] == '#': continue
+
+            m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
+            if m:
+                self.cache[m.group(1)] = m.group(2)
+            else:
+                m = re.match(r'(\S+)\s*=\s*(\S+)', l)
+                if m:
+                    self.map[m.group(1)] = os.path.join(self.base, m.group(2))
+                else:
+                    raise "unknown map entry '%s'"  % l
+
+    def __call__(self, t, **map):
+        m = self.defaults.copy()
+        m.update(map)
+        try:
+            tmpl = self.cache[t]
+        except KeyError:
+            tmpl = self.cache[t] = file(self.map[t]).read()
+        return template(tmpl, self.filters, **m)
+
+def rfc822date(x):
+    return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
+
+def breakentities(x):
+    for i in x:
+        yield i
+        yield "<br>\n"
+
+def entities2nl(x):
+    for i in x:
+        #sys.stderr.write("i=-->%s<--\n"%i)
+        yield i
+        yield "\n"
+
+common_filters = {
+    "escape": cgi.escape,
+    "age": age,
+    "date": (lambda x: time.asctime(time.gmtime(x))),
+    "addbreaks": nl2br,
+    "obfuscate": obfuscate,
+    "breakentities": breakentities,
+    "addnl" : entities2nl,
+    "short": (lambda x: x[:12]),
+    "firstline": (lambda x: x.splitlines(1)[0]),
+    "permissions": (lambda x: x and "rwxr-xr-x" or "rw-r--r--"),
+    "rfc822date": rfc822date,
+    }
+
+class hgweb:
+
+
+    def __init__(self, path, name=None, templates=""):
+        self.templates = templates
+        self.reponame = name
+        self.path = path
+        self.mtime = -1
+        self.viewonly = 0
+        self.fullhistory = [ ]
+        self.refreshed = 0
+
+        # set to 1 if you want the lock enabled
+        self.dolock = 1
+
+        # Set to 1 if you want the profile info at the bottom of the web page
+        self.profile = 0
+
+        # set to 1 to allow a download of a tar.gz archive
+        self.tararchive = 0
+
+        # number of revision in changelog page
+        self.maxchanges = 20
+
+    def refresh(self):
+        if not self.path.startswith("http://"):
+            s = os.stat(os.path.join(self.path, ".bzr", "revision-history"))
+            if s.st_mtime == self.mtime: return
+            self.mtime = s.st_mtime
+        else:
+            # refresh only 1st time
+            if self.refreshed: return
+            self.refreshed = 1
+
+
+        self.branch = bzrlib.branch.Branch.open(self.path)
+        self.history = _enumerate_history(self.branch)
+
+        w = self.branch.get_inventory_weave( )
+        i = 1
+        self.fullhistory = []
+        for ids in w.iter_names( ):
+            self.fullhistory.append( (i,ids) )
+            i += 1
+
+
+        # the format of self.set_key is
+        #   <revid>: (revision-history-revn, flag, full-history-revno )
+        # where
+        #   revision-history-revn: order in history ( not merged )
+        #   full-history-revno:   order in the full history ( merged and
+        #                         not merged )
+        #   flag:   false: merged revision, true: not merged revision
+        #
+        #   when flag == false, revision-history-revn is the index of the
+        #   revision where revision is merged
+        self.set_history = {}
+        i = len(self.history)
+        j = len(self.fullhistory)
+        found = 0
+        while j > 0:
+
+            revid = self.fullhistory[j-1][1]
+            if self.history[i-1][1] == revid:
+                found = i
+                i -= 1
+                self.set_history[revid] = ( found,True, j )
+                #sys.stderr.write("%5d %s %s\n"%(found,'t' ,revid ))
+            else:
+                self.set_history[revid] = ( found,False, j )
+                #sys.stderr.write("%5d %s %s\n"%(found,'f' ,revid ))
+
+            j -= 1
+
+    def date(self, cs):
+        return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
+
+    def revid2revno( self, hist, revid ):
+        for rno,rid in hist:
+            if rid == revid: return rno
+
+        return 0
+
+    def get_fileid( self, path, revid ):
+        inv =self.branch.get_revision_inventory(revid)
+        return inv.path2id( path )
+
+
+    def test_exist_file( self, rev, path, pathrevid ):
+
+        file_id= self.get_fileid( path, pathrevid )
+
+        if file_id == None: return False
+
+        inv =self.branch.get_revision_inventory(rev)
+        return inv.has_id( file_id )
+
+    def get_history_file( self, path, pathrevid):
+
+        file_id = self.get_fileid( path, pathrevid )
+
+        w = self.branch.weave_store.get_weave(file_id,
+                self.branch.get_transaction())
+        count = 1
+        hist = []
+        for ids in w.iter_names( ):
+            hist.append( (count, ids) )
+            count += 1
+
+        return hist
+
+    def template_revid( self, rev ):
+        revno,flag,idx = self.set_history[rev]
+
+        if flag:
+            yield self.t("tmplrevid",revno=revno, revid=rev )
+        else:
+            yield self.t("tmplrevidmerged",revno=revno, revid=rev,
+                revm=self.history[revno-1][1] )
+
+    def template_path( self,rev, path, pathrevid=None ):
+
+        if pathrevid == None:
+            short = 1
+            pathrevid = rev
+        else:
+            short = 0
+
+        if path == '/': path =""
+        fpath = ""
+        lpath = path.split('/')
+
+        yield self.t("ptdir", rev = rev, fullpath=fpath,
+            pathrevid=pathrevid, elem="" )
+
+        if path != "":
+            for e in lpath[:-1]:
+                fpath+=e+'/'
+                yield self.t("ptdir", rev = rev, fullpath=fpath,
+                    pathrevid=pathrevid, elem=e )
+
+        if short == 0:
+            yield self.t("ptfname", rev = rev, fullpath=path,
+                pathrevid=pathrevid, elem=lpath[-1],
+                atpathrevid=self.template_revid(pathrevid) )
+        else:
+            yield self.t("ptfnameshort", rev = rev, fullpath=path,
+                pathrevid=pathrevid, elem=lpath[-1],
+                atpathrevid=self.template_revid(pathrevid) )
+
+
+    def template_nav( self, template, pos, history, pagesize=1,**other ):
+
+        def navigation( template, pos, history,  **other ):
+            def seq(factor = 1):
+
+                yield 1 * factor
+                yield 3 * factor
+
+                for f in seq(factor * 10):
+                    yield f
+
+            count = len(history)
+            l = []
+            res = ""
+            for f in seq():
+                if f > count: break
+                r = "%d" % f
+
+                if pos + f <= count: l.append(("+" + r, pos + f))
+                if pos - f > 0: l.insert(0, ("-" + r, pos - f))
+
+            #yield self.t( "navigationpos", pos=pos, count=count )
+
+            yield self.t( template, revid = history[0][1], label="(1)",
+                **other )
+
+            for label, rev in l:
+                yield self.t(template, label = label,
+                    revid = history[rev-1][1],
+                    **other)
+
+            yield self.t( template, label="tip", **other )
+
+        if pos >pagesize:
+            xpos = pos - pagesize -1
+            if xpos <1 : xpos = 1
+            prev = self.t( template, revid = history[xpos][1], label="&lt;",
+                **other )
+        else:
+            prev = "&lt;"
+
+        if pos < len(history):
+            xpos = pos + pagesize -1
+            if xpos >= len(history) : xpos = len(history)-1
+
+            next = self.t( template, revid = history[xpos][1], label="&gt;",
+                **other )
+        else:
+            next = "&gt;"
+
+        yield self.t( "navigation", pagenext=next, pageprev=prev,
+            pos = pos, count = len(history),
+            navigation = navigation( template, pos, history, **other ) )
+
+
+    def changelog(self, revid, search=None, path=None,
+        pathrevid=None, otherrevid=None ):
+
+        def changelist(cut_revs):
+            parity = (start - end) & 1
+
+            l = []
+            i = len(cut_revs)
+            for dummy, rev_id in cut_revs:
+                i = i - 1
+                try:
+                    rev = self.branch.get_revision(rev_id)
+                    revno,flag,idx = self.set_history[rev_id]
+
+                    date_str = format_date(rev.timestamp,
+                               rev.timezone or 0,
+                               'original')
+
+                    parent = ""
+                    if not path and len(rev.parent_ids) > 0:
+                        if pos-i >2:
+                            p = rev.parent_ids[0]
+                            if p <> history[pos-i-2][1]:
+                                revno_,flag_,idx_ = self.set_history[p]
+                                if not flag_:
+                                    revno_ = '['+str(revno_)+']'
+                                else:
+                                    revno_ = str(revno_)
+
+                                parent = self.t("changelogparent",
+                                    revlink = self.t( "naventry",
+                                        revid = p ,
+                                        label = revno_+':'+p
+                                    )
+                                )
+
+                    dodiff = ""
+                    if otherrevid and otherrevid <> rev.revision_id:
+                        dodiff = self.t("dodiff",otherrevid=otherrevid,
+                            revid=rev.revision_id, path=path and path or "",
+                            pathrevid=path and pathrevid or "")
+
+                    l.insert(0, self.t(
+                        'changelogentry',
+                        parity = parity,
+                        rev = flag and revno or '['+str(revno)+']',
+                        node = rev.revision_id,
+                        author = rev.committer,
+                        desc = rev.message,
+                        date = rev.timestamp,
+                        parent = parent,
+                        dodiff = dodiff,
+                        otherrevid = rev.revision_id,
+                        revid = revid,
+                        path = path and path or "",
+                        pathrevid = pathrevid,
+                        revlink = self.template_revid(rev.revision_id)
+                    ))
+                except (KeyError, NoSuchRevision):
+                    # in case of missing parent
+                    l.insert(0, self.t(
+                        'changelogentry',
+                        parity = parity,
+                        rev = "missing",
+                        node = rev_id,
+                        author = "missing",
+                        desc = "missing",
+                        date = 0,
+                        revlink = rev_id,
+                    ))
+
+                parity = 1 - parity
+
+            yield l
+
+        if revid == "" or revid == None:
+            revid = history[-1][1]
+
+        if not revid in self.set_history.keys( ):
+            yield self.t("revisionnotfound",rev=revid )
+            return
+
+        if not otherrevid : otherrevid=""
+        if otherrevid and not otherrevid in self.set_history.keys( ):
+            yield self.t("revisionnotfound",rev=otherrevid )
+            return
+
+
+        if path <> None:
+
+            if pathrevid == None:
+                pathrevid = revid
+
+            if not pathrevid in self.set_history.keys( ):
+               yield self.t("revisionnotfound",rev=pathrevid )
+               return
+
+            file_id = self.get_fileid( path, pathrevid )
+            inv = self.branch.get_revision_inventory(revid)
+
+            if file_id == None or not inv.has_id(file_id):
+                yield self.t("pathnotfound", path=path,
+                    pathrevid = pathrevid, rev = revid, )
+                return
+
+            history = self.get_history_file(path,pathrevid)
+            revid = inv[file_id].revision
+
+        else:
+            history = self.fullhistory
+            path=""
+            pathrevid=""
+
+        count = len(history)
+        pos=0
+        for rn,rid in history:
+            if rid == revid:
+                pos = rn
+                break
+
+        if search != None:
+            lsearch = search.lower( )
+
+            #search before in the revision list
+            spos=pos
+            while spos >= 1 :
+
+                revno,rev_id = history[spos-1]
+
+                if rev_id.lower( ) == lsearch: break
+                spos = spos - 1
+
+            #otherwise search in the  message
+            if spos==0:
+                spos=pos
+                while spos >= 1 :
+
+                    revno,rev_id = history[spos-1]
+
+                    try:
+                        rev = self.branch.get_revision(rev_id)
+                    except (KeyError, NoSuchRevision):
+                        yield self.t("revisionnotfound",rev=rev_id )
+                        return
+
+                    if rev.message.lower( ).find(lsearch) >= 0 : break
+                    spos = spos - 1
+
+
+            if spos <> 0:
+                pos=spos
+            else:
+                yield self.t("textnotfound",search=search)
+                return
+
+
+        start = max(1, pos - self.maxchanges + 1)
+        end = min(count, start + self.maxchanges -1)
+        pos = end
+
+        searchfrom = history[pos-1][1]
+        if search != None: searchfrom = history[pos-2][1]
+
+        cut_revs = history[(start-1):(end)]
+
+        if path:
+            changelogpath = self.t("changelogpath",
+                pathlink = self.template_path( revid, path, pathrevid ) )
+            def nav( **map): return self.template_nav( "cflnaventry",pos,
+                            history, path = path, pathrevid = pathrevid,
+                            pagesize=self.maxchanges,
+                            otherrevid=otherrevid)
+        else:
+            changelogpath = ""
+            def nav( **map ): return self.template_nav( "naventry",pos,
+                                    history,pagesize=self.maxchanges,
+                                    otherrevid=otherrevid)
+
+        if otherrevid:
+            diffrevision = self.t("diffrevision",
+                                   revid=revid,
+                                   path = path and path or "",
+                                   pathrevid = pathrevid,
+                                   link = self.template_revid(otherrevid))
+        else:
+            diffrevision = ""
+
+        yield self.t('changelog',
+                     changenav = nav,
+                     rev = pos,
+                     revid = revid,
+                     path = path and  path or "",
+                     pathrevid = pathrevid and pathrevid or "",
+                     searchfrom = searchfrom,
+                     otherrevid = otherrevid,
+                     changesets = 10,
+                     changelogpath = changelogpath,
+                     diffrevision = diffrevision,
+                     entries = changelist(cut_revs))
+
+
+
+    def diff2revs(self, revid, otherrevid=None,path=None,pathrevid=None ):
+
+        new_tree = self.branch.revision_tree(revid)
+        if otherrevid:
+            revidbefore = otherrevid
+            old_tree = self.branch.revision_tree(revidbefore)
+        else:
+            old_tree = EmptyTree()
+
+        if path:
+            fileid=self.get_fileid(path,pathrevid)
+            pathlist=[new_tree.id2path(fileid)]
+        else:
+            pathlist=None
+
+        # FIXME:the path is not pathrevid related
+        delta = compare_trees(old_tree, new_tree,specific_files=pathlist)
+
+        def diff(**map):
+            def dodiff( b, old_tree, new_tree):
+                class stream:
+                    buf = []
+                    def write( self, x ):
+                        self.buf.append(x)
+
+                s = stream( )
+                show_diff_trees(old_tree, new_tree, s, specific_files=pathlist)
+
+                return s.buf
+
+            def prettyprintlines( diff ):
+                for l in diff:
+                    if l.startswith('--- '):
+                        if otherrevid and l[4:-1] <> '/dev/null':
+                            parentid = otherrevid
+                            path = self.template_path( parentid,l[4:-1] )
+                        else:
+                            path = l[4:-1]
+
+                        yield self.t("difffileminus", path=path)
+                    elif l.startswith('+++ '):
+                        if l[4:-1] <> '/dev/null':
+                            path = self.template_path( revid,l[4:-1] )
+                        else:
+                            path = l[4:-1]
+                        yield self.t("difffileplus", path=path)
+                    elif l.startswith('='):
+                        yield self.t("difflineinfo", line = l)
+                    elif l.startswith('+'):
+                        yield self.t("difflineplus", line = l)
+                    elif l.startswith('-'):
+                        yield self.t("difflineminus", line = l)
+                    elif l.startswith('@'):
+                        yield self.t("difflineat", line = l)
+                    else:
+                        yield self.t("diffline", line = l)
+
+            yield self.t("diffblock",
+                lines = prettyprintlines(dodiff(self.branch,
+                    old_tree, new_tree)),
+                parity = 0 )
+
+        def files(files):
+            for path, fid, kind in files:
+                if kind == 'directory':
+                    path += '/'
+                elif kind == 'symlink':
+                    path += '@'
+                yield path+'<br>\n'
+
+        def files_renamed(files):
+            for oldpath, newpath, fid, kind, text_modified, meta_modified in files:
+                if kind == 'directory':
+                    yield self.t("revrename", oldpath = '/'+oldpath,
+                        newpath = self.template_path( revid, newpath+'/' ))
+                elif kind == 'symlink':
+                    yield self.t("revrename", oldpath = '/'+oldpath,
+                        newpath = self.t("revlinkentry",
+                        str = self.template_path( revid, newpath ) ))
+                else:
+                    yield self.t("revrename", oldpath = '/'+oldpath,
+                        newpath = self.template_path( revid, newpath ))
+
+        def fileentry(files):
+            for path, fid, kind in files:
+                if kind == 'directory':
+                    yield self.template_path( revid, path+'/' )
+                elif kind == 'symlink':
+                    yield self.t("revlinkentry",
+                        str = self.template_path( revid, path ) )
+                else:
+                    yield self.template_path( revid, path )
+
+        def fileremoved(files):
+            for path, fid, kind in files:
+                if kind == 'directory':
+                    yield self.t( "revdelete", path=path+'/' )
+                elif kind == 'symlink':
+                    yield self.t( "revdelete", path='@'+path )
+                else:
+                    yield self.t( "revdelete", path=path )
+
+        yield self.t('diffentry',
+            diff = diff,
+            filesadded = fileentry( delta.added),
+            filesmodified = fileentry( [ (path,id,kind)
+                        for path, id, kind, text_modified, meta_modified in
+                            delta.modified]),
+            filesrenamed = files_renamed(delta.renamed),
+            filesremoved = fileremoved(delta.removed),
+        )
+
+
+    def diffpage(self, rev, otherrevid=None,path=None,pathrevid=None):
+
+        history = self.fullhistory
+
+        if rev == "" or rev == None:
+            revid = history[-1][1]
+            revno,flag,pos = self.set_history[revid]
+        else:
+            revid = rev
+            if not revid in self.set_history.keys( ):
+               yield self.t("revisionnotfound",rev=revid )
+               return
+
+            revno,flag,pos = self.set_history[revid]
+
+        if otherrevid and not otherrevid in self.set_history.keys( ):
+            yield self.t("revisionnotfound",rev=otherrevid )
+            return
+
+        count = len(history)
+
+        pos=0
+        for rn,rid in history:
+            if rid == revid:
+                pos = rn
+                break
+
+        # compute the delta
+        rev = self.branch.get_revision(revid)
+        if not otherrevid and len(rev.parent_ids):
+            otherrevid = rev.parent_ids[0]
+
+        def getparents( rev ):
+            for pid in rev.parent_ids:
+                yield self.template_revid(pid)
+                yield "<br>"
+
+        if path:
+            if not self.test_exist_file(otherrevid,path,pathrevid):
+                yield self.t("pathnotfound", path=path,
+                    pathrevid = pathrevid, rev = otherrevid, )
+                return
+            if not self.test_exist_file(revid,path,pathrevid):
+                yield self.t("pathnotfound", path=path,
+                    pathrevid = pathrevid, rev = revid, )
+                return
+
+            changelogpath = self.t("changelogpath",
+                pathlink = self.template_path( revid, path, pathrevid ) )
+        else:
+            changelogpath = ""
+
+        yield self.t('diffpage',
+            diff = self.diff2revs(revid,otherrevid,path,pathrevid),
+
+            linkrevid = self.template_revid(revid),
+            linkotherrevid = self.template_revid(otherrevid),
+            diffnav = lambda **map: self.template_nav("diffnaventry",
+                                        pos,history, otherrevid=otherrevid,
+                                        path=path and path or "",
+                                        pathrevid=path and pathrevid or ""),
+            changelogpath = changelogpath,
+            revid = rev.revision_id,
+            otherrevid = otherrevid,
+            path = path and path or "",
+            pathrevid = path and pathrevid or ""
+        )
+
+
+    def revision(self, rev ):
+
+        history = self.fullhistory
+
+        if rev == "" or rev == None:
+            revid = history[-1][1]
+            revno,flag,pos = self.set_history[revid]
+        else:
+            revid = rev
+            if not revid in self.set_history.keys( ):
+               yield self.t("revisionnotfound",rev=revid )
+               return
+
+            revno,flag,pos = self.set_history[revid]
+
+        count = len(history)
+
+        pos=0
+        for rn,rid in history:
+            if rid == revid:
+                pos = rn
+                break
+
+        # compute the delta
+        rev = self.branch.get_revision(revid)
+        if len(rev.parent_ids):
+            previous = rev.parent_ids[0]
+        else:
+            previous = None
+
+        def getparents( rev ):
+            for pid in rev.parent_ids:
+                yield self.template_revid(pid)
+                yield "<br>"
+
+        yield self.t('revision',
+            diff = self.diff2revs(revid,previous),
+            rev = flag and str(revno) or '['+str(revno)+']',
+            revisionnav = lambda **map:
+                    self.template_nav("revnaventry", pos, history ),
+            parents = getparents( rev ),
+            linkrev = self.template_revid(rev.revision_id),
+            node = rev.revision_id,
+            author = rev.committer,
+            desc = rev.message,
+            date = rev.timestamp,
+        )
+
+    def content(self, revid, path, pathrevid ):
+
+
+        if revid == None:
+            revid = self.fullhistory[-1][1]
+        if pathrevid == None:
+            pathrevid = revid
+
+        if not pathrevid in self.set_history.keys( ):
+            yield self.t("revisionnotfound",rev=pathrevid )
+            return
+
+        if not revid in self.set_history.keys( ):
+            yield self.t("revisionnotfound",rev=revid )
+            return
+
+        rev = self.branch.get_revision(revid)
+
+        file_id = self.get_fileid( path, pathrevid )
+        inv = self.branch.get_revision_inventory(revid)
+
+        if file_id == None or not inv.has_id(file_id ):
+            yield self.t("pathnotfound", path=path,
+                pathrevid = pathrevid, rev = revid, )
+            return
+
+
+        finc = inv[file_id]
+        revid = finc.revision
+
+        history = self.get_history_file(path,pathrevid)
+        count = len(history)
+
+        pos=0
+        for rn,rid in history:
+            if rid == revid:
+                pos = rn
+                break
+
+        def file_contents_annotate( file_id, file_version ):
+            count = 0
+            parity = 1
+
+            prevanno=''
+            for (revno_str, author, date_str, line_rev_id, text ) in \
+                    _annotate_file(self.branch, file_version, file_id ):
+
+                anno = "%5s %-7s " % ( revno_str, author[:7] )
+
+                if anno.lstrip() == "" :
+                    anno = prevanno
+                else:
+                    parity = 1- parity
+
+                count = count + 1
+                prevanno = anno
+
+                if revno_str == "":
+                    tmpl = "annotateline"
+                    (revno_str,author ) = oldvalues
+                else:
+                    tmpl = "annotateline"
+                    oldvalues = (revno_str,author )
+
+                revno,flags,idx = self.set_history[line_rev_id]
+
+                if flags:
+                    revno_str = str(revno)
+                    revno_revid = line_rev_id
+                else:
+                    revno_str = '['+str(revno)+']'
+                    revno_revid = self.history[revno][1]
+
+                yield self.t( tmpl,
+                    parity = parity,
+                    linenumber = count,
+                    author = author[:7],
+                    revnostr = revno_str,
+                    revid = line_rev_id,
+                    revnorevid = revno_revid,
+                    line = text )
+
+        revno,flags,idx = self.set_history[revid]
+
+        if flags:
+            revno_str = str(revno)
+        else:
+            revno_str = '['+str(revno)+']'
+
+        yield self.t('filecontent',
+                     entries = finc.kind == "symlink" and
+                        self.t("linkline", name=finc.name, value=finc.symlink_target) or
+                        file_contents_annotate( file_id, revid ),
+                     rev = revno_str,
+                     cntnav = lambda **map: self.template_nav( "cntnaventry",
+                        pos,history, path=path, pathrevid=pathrevid),
+                     node = rev.revision_id,
+                     author = rev.committer,
+                     desc = rev.message,
+                     path = path,
+                     date = rev.timestamp,
+                     pathrevid = pathrevid,
+                     linkrev = self.template_revid( rev.revision_id ),
+                     linkpath = self.template_path( rev.revision_id, path, pathrevid ),
+        )
+
+    def inventory(self, rev, path = None, pathrevid = None):
+
+        def dname( path ):
+            d = path.rfind('/')
+            if d>= 0:
+                return path[:d]
+            else:
+                return ""
+
+        def fname( path ):
+            d = path.rfind('/')
+            return path[d+1:]
+
+        history = self.fullhistory
+
+        if rev == None:
+            rev = history[-1][1]
+
+        if pathrevid == None: pathrevid = rev
+
+        if not rev in self.set_history.keys( ):
+            yield self.t("revisionnotfound",rev=rev )
+            return
+
+        if not pathrevid in self.set_history.keys( ):
+            yield self.t("revisionnotfound",rev=pathrevid )
+            return
+
+        count = len(history)
+        pos=0
+        for rn,rid in history:
+            if rid == rev:
+                pos = rn
+                break
+
+        inv = self.branch.get_revision_inventory(rev)
+        if not path :
+            path2 = ""
+            path = "/"
+            file_id = None
+        else:
+            file_id = self.get_fileid( path, pathrevid )
+            if file_id == None or not inv.has_id(file_id):
+                yield self.t("pathnotfound", path=path,
+                    pathrevid = pathrevid, rev = rev, )
+                return
+            path2 = inv.id2path(file_id)
+
+        def filelist(**map):
+            parity=0
+            for fpath, entry in inv.entries():
+                if dname(fpath) != path2:
+                    continue
+                bname = fname(fpath)
+                #FIXME: remeber of the link, and the x attribute
+                if entry.kind == "file":
+                    yield self.t("inventoryfileentry",
+                                    perm = entry.executable,
+                                    parity = parity,
+                                    name = bname,
+                                    fullname = fpath,
+                                    id = entry.file_id,
+                                    rev = rev,
+                    )
+                elif entry.kind == "directory":
+                    yield self.t("inventorydirentry",
+                                    perm = entry.executable,
+                                    parity = parity,
+                                    name = bname + '/',
+                                    path = fpath,
+                                    id = entry.file_id,
+                                    rev = rev,
+                    )
+                elif entry.kind == "symlink":
+                    yield self.t("inventorylinkentry",
+                                    perm = entry.executable,
+                                    parity = parity,
+                                    name = bname ,
+                                    fullname = fpath,
+                                    id = entry.file_id,
+                                    rev = rev,
+                    )
+                parity = 1 - parity
+
+
+        yield self.t("inventory",
+                     rev = rev,
+                     invnav = lambda **map: self.template_nav(
+                            "invnaventry", pos, history,path=path,
+                            pathrevid=pathrevid ),
+                     path = path,
+                     actualpath = path2,
+                     linkpath = self.template_path(rev,path,pathrevid),
+                     pathrevid = pathrevid,
+                     revlink = self.template_revid(rev),
+                     parent = dname(path[:-1]),
+                     entries = filelist)
+                     
+
+    # TODO unify with the code of export command
+    def export_archive(self, name,revid, path="", mode=""):
+
+
+
+        def tar_exporter(tree, dest, root, filter_func=None, compression=None):
+            """Export this tree to a new tar file.
+
+            `dest` will be created holding the contents of this tree; if it
+            already exists, it will be clobbered, like with "tar -c".
+            """
+            from time import time
+            import tarfile
+            now = time()
+            compression = str(compression or '')
+            if root is None:
+                root = get_root_name(dest)
+            try:
+                if isinstance(dest, basestring):
+                    ball = tarfile.open(dest, 'w:' + compression)
+                else:
+                    ball = tarfile.open("none", 'w:' + compression, dest)
+            except tarfile.CompressionError, e:
+                raise BzrError(str(e))
+            #mutter('export version %r' % tree)
+            inv = tree.inventory
+            for dp, ie in inv.iter_entries():
+                if filter_func and filter_func( dp,ie ): continue
+                #mutter("  export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
+                item, fileobj = ie.get_tar_item(root, dp, now, tree)
+                ball.addfile(item, fileobj)
+            ball.close()
+
+        def zip_exporter( tree, dest, root, filter_func=None ):
+            """export this tree to a zip file"""
+
+            from time import time
+            import zipfile
+            now = time()
+
+            if root is None:
+                root = get_root_name(dest)
+
+            try:
+                ball = zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED)
+            except zipfile.BadZipfile, e:
+                raise BzrError(str(e))
+
+            inv = tree.inventory
+            for dp, ie in inv.iter_entries():
+                if filter_func and filter_func( dp,ie ): continue
+                # FIXME: we have to handle also directories and symlinks
+                if ie.kind <> "file": continue
+                item, fileobj = ie.get_tar_item(root, dp, now, tree)
+                ball.writestr(str(item.name), fileobj.read())
+            ball.close()
+
+        tree = self.branch.revision_tree(revid)
+        root = name + "_" + revid
+
+        if path:
+            f = lambda dp,ie : not dp.startswith(path)
+        else:
+            f = None
+
+        # FIXME: it is right to use application/force-download
+        # see http://lists.nyphp.org/pipermail/talk/2003-September/005058.html
+
+        #
+        # header("Cache-Control: no-cache, must-revalidate");
+        # header("Pragma: no-cache");
+        # header("Content-type: application/octet-stream");
+        # header("Content-Disposition: attachment; filename=\"$filename\"");
+        #
+
+        if mode == "tgz":
+            dest = root + ".tar.gz"
+            sys.stdout.write( 'Content-Type: application/force-download\n'  \
+              'Content-Disposition: attachment; filename=%s\n\n'  % ( dest ))
+            tar_exporter( tree, sys.stdout, root, filter_func=f, compression="gz" )
+        elif mode == "tbz2":
+            dest = root + ".tar.bz2"
+            sys.stdout.write( 'Content-Type: application/force-download\n'  \
+              'Content-Disposition: attachment; filename=%s\n\n'  % ( dest ))
+            tar_exporter( tree, sys.stdout, root, filter_func=f, compression="bz2" )
+        elif mode == "tar":
+            dest = root + ".tar"
+            sys.stdout.write( 'Content-Type: application/force-download\n'  \
+              'Content-Disposition: attachment; filename=%s\n\n'  % ( dest ))
+            tar_exporter( tree, sys.stdout, root, filter_func=f, compression="" )
+        else: # mode == zip
+            dest = root + ".zip"
+            fo = os.tmpfile( )
+            sys.stdout.write( 'Content-Type: application/force-download\n'  \
+                'Content-Disposition: attachment; filename=%s\n\n'  % ( dest ))
+            zip_exporter( tree, fo, root, filter_func=f )
+            fo.seek(0)
+            sys.stdout.write(fo.read())
+
+
+
+
+    def write( self, arg ):
+        if not self.profile:
+            return write( arg )
+
+        import hotshot, hotshot.stats
+
+        try:
+            tmpfilename = os.tempnam( )
+
+            prof = hotshot.Profile(tmpfilename)
+            prof.runcall(write, arg )
+            prof.close()
+
+            print """
+                <hr width=100%>
+                <pre>
+            """
+
+            import hotshot, hotshot.stats
+
+            stats = hotshot.stats.load(tmpfilename)
+            stats.strip_dirs()
+            stats.sort_stats('time', 'file','calls')
+            stats.print_stats()
+        finally:
+            os.unlink( tmpfilename )
+
+        print """
+            </pre>
+            <hr width=100%>
+        """
+
+        write(self.t("footer"))
+
+    def run(self):
+        def header(**map):
+            yield self.t("header", **map)
+
+        def footer(**map):
+            if not self.profile:
+                yield self.t("footer", **map)
+
+        def get(arg):
+            if args.has_key(arg):
+                return args[arg][0]
+            else:
+                return None
+
+        self.refresh()
+        args = cgi.parse()
+
+        t = self.templates or templatepath( )
+        #or self.repo.ui.config("web", "templates",
+        #                                          templatepath())
+        m = os.path.join(t, "map")
+        if args.has_key('style'):
+            b = os.path.basename("map-" + args['style'][0])
+            p = os.path.join(t, b)
+            if os.path.isfile(p): m = p
+
+        port = os.environ["SERVER_PORT"]
+        port = port != "80" and (":" + port) or ""
+        uri = os.environ["REQUEST_URI"]
+        if "?" in uri: uri = uri.split("?")[0]
+        url = "http://%s%s%s" % (os.environ["SERVER_NAME"], port, uri)
+
+        name = self.reponame
+
+        self.t = templater(m, common_filters,
+                           {"url":url,
+                            "repo":name,
+                            "header":header,
+                            "footer":footer,
+                            })
+
+        if not args.has_key('cmd'):
+            args['cmd'] = [self.t.cache['default'],]
+
+        if args.has_key('rev'):
+            revno = args['rev'][0]
+        else:
+            revno = self.fullhistory[-1][1]
+
+        if self.dolock: self.branch.lock_read( )
+        try:
+
+
+            if args['cmd'][0] == 'changelog':
+
+                otherrevid = get("otherrevid")
+                pathrevid = get("pathrevid")
+                path = get("path")
+
+                self.write(self.changelog(revno, None, path, pathrevid, otherrevid))
+
+            elif args['cmd'][0] == 'search':
+                w = get('w')
+                otherrevid = get("otherrevid")
+                pathrevid = get("pathrevid")
+                path = get("path")
+                self.write(self.changelog(revno,search=w,
+                    path=path,pathrevid=pathrevid,otherrevid=otherrevid))
+
+            elif args['cmd'][0] == 'revision':
+                if args.has_key("revid"):
+                    self.write(self.revision(args['revid'][0] ))
+                else:
+                    self.write(self.revision(str(revno) ))
+
+            elif args['cmd'][0] == 'diff':
+                revid = get("rev")
+                otherrevid = get("otherrevid")
+                path = get("path")
+                pathrevid = get("pathrevid")
+                self.write(self.diffpage(revid,otherrevid,path,pathrevid))
+
+            elif args['cmd'][0] == 'inventory':
+                path = pathrevid = None
+
+                if args.has_key('path'):
+                    path = args['path'][0]
+
+                if args.has_key('pathrevid'):
+                    pathrevid = args['pathrevid'][0]
+
+                self.write(self.inventory(revno, path, pathrevid))
+
+            elif args['cmd'][0] == 'content':
+
+                path = pathrevid = None
+                revid = self.fullhistory[-1][1]
+
+                if args.has_key('rev'):
+                    revid = args['rev'][0]
+
+                if args.has_key('pathrevid'):
+                    pathrevid = args['pathrevid'][0]
+
+                if args.has_key('path'):
+                    path = args['path'][0]
+
+                self.write(self.content(revid, path, pathrevid))
+
+            elif self.tararchive and args['cmd'][0] == 'export':
+                path = get("path")
+                mode = get("mode")
+                self.export_archive(name, revno, path and path or "", mode)
+
+            else:
+                self.write(self.t("error",command=args['cmd'][0]))
+
+        finally:
+                if self.dolock: self.branch.unlock( )
+
+def create_server(path, name, templates, address, port, use_ipv6,
+                  accesslog, errorlog, dolock, tararchive,
+                  profile):
+
+    def openlog(opt, default):
+        if opt and opt != '-':
+            return open(opt, 'w')
+        return default
+
+    accesslog = sys.stdout
+    errorlog = sys.stderr
+
+    import BaseHTTPServer
+
+    class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
+        address_family = getattr(socket, 'AF_INET6', None)
+
+        def __init__(self, *args, **kwargs):
+            if self.address_family is None:
+                raise RepoError('IPv6 not available on this system')
+            BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
+
+    class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
+        def log_error(self, format, *args):
+            errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
+                                                 self.log_date_time_string(),
+                                                 format % args))
+
+        def log_message(self, format, *args):
+            accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
+                                                  self.log_date_time_string(),
+                                                  format % args))
+
+        def do_POST(self):
+            try:
+                self.do_hgweb()
+            except socket.error, inst:
+                if inst.args[0] != 32: raise
+
+        def do_GET(self):
+            self.do_POST()
+
+        def do_hgweb(self):
+            query = ""
+            p = self.path.find("?")
+            if p:
+                query = self.path[p + 1:]
+                query = query.replace('+', ' ')
+
+            env = {}
+            env['GATEWAY_INTERFACE'] = 'CGI/1.1'
+            env['REQUEST_METHOD'] = self.command
+            env['SERVER_NAME'] = self.server.server_name
+            env['SERVER_PORT'] = str(self.server.server_port)
+            env['REQUEST_URI'] = "/"
+            if query:
+                env['QUERY_STRING'] = query
+            host = self.address_string()
+            if host != self.client_address[0]:
+                env['REMOTE_HOST'] = host
+                env['REMOTE_ADDR'] = self.client_address[0]
+
+            if self.headers.typeheader is None:
+                env['CONTENT_TYPE'] = self.headers.type
+            else:
+                env['CONTENT_TYPE'] = self.headers.typeheader
+            length = self.headers.getheader('content-length')
+            if length:
+                env['CONTENT_LENGTH'] = length
+            accept = []
+            for line in self.headers.getallmatchingheaders('accept'):
+                if line[:1] in "\t\n\r ":
+                    accept.append(line.strip())
+                else:
+                    accept = accept + line[7:].split(',')
+            env['HTTP_ACCEPT'] = ','.join(accept)
+
+            os.environ.update(env)
+
+            save = sys.argv, sys.stdin, sys.stdout, sys.stderr
+            try:
+                sys.stdin = self.rfile
+                sys.stdout = self.wfile
+                sys.argv = ["hgweb.py"]
+                if '=' not in query:
+                    sys.argv.append(query)
+                self.send_response(200, "Script output follows")
+                hg.run()
+            finally:
+                sys.argv, sys.stdin, sys.stdout, sys.stderr = save
+
+    hg = hgweb(path, name, templates)
+    if dolock: hg.dolock=1
+    if tararchive: hg.tararchive=1
+    if profile: hg.profile=1
+    if use_ipv6:
+        return IPv6HTTPServer((address, port), hgwebhandler)
+    else:
+        return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
+
+
+class hgwebdir:
+    def __init__(self, config):
+        self.cp = ConfigParser.SafeConfigParser()
+        self.cp.read(config)
+
+    def run(self):
+
+        def get(sec, val, default):
+            try:
+                return self.cp.get(sec, val)
+            except:
+                return default
+
+        try:
+            virtual = os.environ["PATH_INFO"].strip('/')
+        except:
+            virtual = ""
+
+        if virtual:
+            real = self.cp.get("paths", virtual)
+
+            h = hgweb(real, get(virtual,"name",virtual),
+                get(virtual,"template",""))
+
+            h.dolock = int(get("options","lock","0"))
+            h.profile = int(get("options","profile","0"))
+            h.tararchive = int(get("options","tararchive","0"))
+
+            h.run()
+            return
+
+        def header(**map):
+            yield tmpl("header", **map)
+
+        def footer(**map):
+            yield tmpl("footer", **map)
+
+        templates = templatepath()
+        m = os.path.join(templates, "map")
+        tmpl = templater(m, common_filters,
+                         {"header": header, "footer": footer})
+
+        def entries(**map):
+            parity = 0
+            l = self.cp.items("paths")
+            l.sort()
+            for v,r in l:
+
+
+
+                url = ('/'+os.environ["REQUEST_URI"]+'/'+v).replace("//","/")
+                if not r.startswith("http://"):
+                    lastupdate = os.stat(os.path.join(r, ".bzr",
+                        "revision-history")).st_mtime
+                else:
+                    lastupdate=0
+                yield tmpl("indexentry",
+                           author = get(v, "author", "unknown"),
+                           name = get(v, "name", v),
+                           email = get(v,"email",""),
+                           url = url,
+                           parity = parity,
+                           shortdesc = get(v, "description", "unknown"),
+                           lastupdate = lastupdate
+                )
+
+                parity = 1 - parity
+
+        write(tmpl("index", entries = entries))

=== added file 'bzrlib/plugins/webserve/hgwebdir.cgi'
--- /dev/null
+++ bzrlib/plugins/webserve/hgwebdir.cgi
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+#
+# An example CGI script to export multiple hgweb repos, edit as necessary
+
+import cgitb, sys
+cgitb.enable()
+
+# sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
+sys.path.insert(0, "bzr-hgweb-revision") # if not a system-wide install
+from bzrlib.plugins.webserve import hgweb
+
+# The config file looks like this:
+# [paths]
+# virtual/path = /real/path
+# virtual/path = /real/path
+
+h = hgweb.hgwebdir("hgwebdir.config")
+h.run()

=== added file 'bzrlib/plugins/webserve/hgwebdir.config.examples'
--- /dev/null
+++ bzrlib/plugins/webserve/hgwebdir.config.examples
@@ -0,0 +1,23 @@
+[paths]
+repo-bazaar-NG_stable_branch = bzr.dev
+repo-hgweb_devel = bzr-hgweb-revision
+
+[options]
+; if 1, it is allowable to download a tar of the archive
+tararchive = 0
+; if 1, and the bottom of the page it is displayed the profiling informations
+profile = 0
+; if 1, during a command a read_lock is performed
+lock = 0
+
+[repo-bazaar-ng_stable_branch]
+author = Goffredo Baroncelli
+name  = bazaar-ng
+description = bazaar-ng stable branch
+email = email at email.email
+
+[repo-hgweb_devel]
+author = Goffredo Baroncelli
+name  = bazaar-hgweb
+description = bazaar-ng web interface
+email = email at email.email

=== added directory 'bzrlib/plugins/webserve/templates'
=== added file 'bzrlib/plugins/webserve/templates/changefilelog-rss.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/changefilelog-rss.tmpl
@@ -0,0 +1,7 @@
+#header#
+    <!-- derived from mercurial project -->
+    <title>#repo|escape#: Changelog of file #path|escape#</title>
+    <description>#repo|escape#: Changelog of file #path|escape#</description>
+    #entries#
+  </channel>
+</rss>
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/changefilelog.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/changefilelog.tmpl
@@ -0,0 +1,44 @@
+#header#
+<title>#repo|escape#: filelog</title>
+<link rel="alternate" type="application/rss+xml"
+      href="?cmd=changefilelog;rev=;path=#path|escape#;style=rss" 
+      title="RSS feed for file #path|escape#, repository #repo|escape#">
+</head>
+<body>
+
+<table width=100%>
+  <td align=left>
+    <div class="buttons">
+    <!--<a href="?cmd=tags">tags</a>-->
+    <a href="?cmd=inventory;rev=#rev#;path=">inventory</a>
+    <a href="?cmd=content;rev=#rev#;path=#path|escape#">content</a>
+    <a type="application/rss+xml" 
+       href="?cmd=changefilelog;rev=;path=#path|escape#;style=rss">rss</a>
+    </div>
+  <td align=right>
+    navigate: <small>#changenav#</small>
+</table>
+
+<h2>Repository '#repo|escape#'</h2><br>
+<h2>log for file #path|escape#</h2>
+<!--<form action="#">
+<p>
+<label for="search2">search:</label>
+<input type="hidden" name="rev" value="#searchfrom#">
+<input type="hidden" name="cmd" value="search">
+<input name="w" id="search2" type="text" size="30">
+</p>
+</form>
+-->
+#entries#
+<!--
+<form action="#">
+<p>
+<label for="search2">search:</label>
+<input type="hidden" name="rev" value="#searchfrom#">
+<input type="hidden" name="cmd" value="search">
+<input name="w" id="search2" type="text" size="30">
+</p>
+</form>
+-->
+#footer#

=== added file 'bzrlib/plugins/webserve/templates/changefilelogentry-rss.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/changefilelogentry-rss.tmpl
@@ -0,0 +1,8 @@
+<item>
+    <!-- derived from mercurial project -->
+    <title>#desc|firstline|escape#</title>
+    <link>#url#?cmd=revision;rev=#rev#</link>
+    <description><![CDATA[#desc|escape|addbreaks#]]></description>
+    <author>#author|obfuscate#</author>
+    <pubDate>#date|rfc822date#</pubDate>
+</item>

=== added file 'bzrlib/plugins/webserve/templates/changefilelogentry.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/changefilelogentry.tmpl
@@ -0,0 +1,20 @@
+<table class="changelogEntry parity#parity#">
+ <tr>
+  <th class="age">#date|age# ago:</th>
+  <th class="firstline">#desc|firstline|escape#</th>
+ </tr>
+ <tr>
+  <th class="changesetRev">revision #rev#:</th>
+  <td class="changesetNode"><a href="?cmd=revision;rev=#rev#">#node#</a></td>
+ </tr>
+ #parent#
+ #changelogtag#
+ <tr>
+  <th class="author">commiter:</th>
+  <td class="author">#author|obfuscate#</td>
+ </tr>
+ <tr>
+  <th class="date">date:</th>
+  <td class="date">#date|date#</td>
+ </tr>
+</table>
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/changelog-rss.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/changelog-rss.tmpl
@@ -0,0 +1,7 @@
+#header#
+    <!-- derived from mercurial project -->
+    <title>#repo|escape# Changelog</title>
+    <description>#repo|escape# Changelog</description>
+    #entries#
+  </channel>
+</rss>
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/changelog.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/changelog.tmpl
@@ -0,0 +1,72 @@
+#header#
+<title>#repo|escape#: log</title>
+<link rel="alternate" type="application/rss+xml"
+   href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
+</head>
+<body>
+
+<table width=100%>
+  <td align=left>
+    <div class="buttons">
+    <a href="?cmd=changelog;rev=">main</a>
+    <a href="?cmd=inventory;rev=#revid#;pathrevid=#revid#;path=">inventory</a>
+    <a type="application/rss+xml" href="?cmd=changelog;style=rss">rss</a>
+    </div>
+  <td align=right>
+    #changenav#
+</table>
+
+
+<center>
+    <h1>Log</h1>
+    <hr width=80%>
+</center>
+
+
+<table id=changesetEntry>
+    #changelogpath#
+    #diffrevision#
+</table>
+
+<form action="#">
+    <p>
+    <label for="search2">search:</label>
+    <input type="hidden" name="rev" value="#searchfrom#">
+    <input type="hidden" name="pathrevid" value="#pathrevid#">
+    <input type="hidden" name="path" value="#path#">
+    <input type="hidden" name="otherrevid" value="#otherrevid#">
+    <input type="hidden" name="cmd" value="search">
+    <input name="w" id="search2" type="text" size="30">
+    </p>
+</form>
+
+#entries#
+
+<form action="#">
+    <p>
+    <label for="search2">search:</label>
+    <input type="hidden" name="rev" value="#searchfrom#">
+    <input type="hidden" name="pathrevid" value="#pathrevid#">
+    <input type="hidden" name="path" value="#path#">
+    <input type="hidden" name="cmd" value="search">
+    <input type="hidden" name="otherrevid" value="#otherrevid#">    
+    <input name="w" id="search2" type="text" size="30">
+    </p>
+</form>
+
+<center>
+    <hr width=80%>
+</center>
+
+<table width=100%>
+  <td align=left>
+    <div class="buttons">
+    <a href="?cmd=changelog;rev=">main</a>
+    <a href="?cmd=inventory;rev=#revid#;pathrevid=#revid#;path=">inventory</a>
+    <a type="application/rss+xml" href="?cmd=changelog;style=rss">rss</a>
+    </div>
+  <td align=right>
+    #changenav#
+</table>
+
+#footer#

=== added file 'bzrlib/plugins/webserve/templates/changelogentry-rss.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/changelogentry-rss.tmpl
@@ -0,0 +1,8 @@
+<item>
+    <!-- derived from mercurial project -->
+    <title>#desc|firstline|escape#</title>
+    <link>#url#?cmd=revision;rev=#node#</link>
+    <description><![CDATA[#desc|escape|addbreaks#]]></description>
+    <author>#author|obfuscate#</author>
+    <pubDate>#date|rfc822date#</pubDate>
+</item>

=== added file 'bzrlib/plugins/webserve/templates/changelogentry.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/changelogentry.tmpl
@@ -0,0 +1,23 @@
+<table class="changelogEntry parity#parity#">
+ <tr>
+  <th class="age">#date|age# ago:</th>
+  <th class="firstline">#desc|firstline|escape#</th>
+  #dodiff#
+  <th><a href="?cmd=changelog;otherrevid=#otherrevid#;rev=#revid#;pathrevid=#revid#;path=#path|escape#">
+    Mark for diff</a>
+ </tr>
+ <tr>
+  <th class="changesetRev">revision:</th>
+  <td class="changesetNode">#revlink#</td>
+ </tr>
+ #parent#
+ #changelogtag#
+ <tr>
+  <th class="author">commiter:</th>
+  <td class="author">#author|obfuscate#</td>
+ </tr>
+ <tr>
+  <th class="date">date:</th>
+  <td class="date">#date|date#</td>
+ </tr>
+</table>
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/diffentry.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/diffentry.tmpl
@@ -0,0 +1,19 @@
+
+<table id="changesetEntry">
+    <tr>
+    <th class="files">files added:</th>
+    <td class="files">#filesadded|breakentities#</td></tr>
+    <tr>
+    <th class="files">files removed:</th>
+    <td class="files">#filesremoved|breakentities#</td></tr>
+    <tr>
+    <th class="files">files modified:</th>
+    <td class="files">#filesmodified|breakentities#</td></tr>
+    <tr>
+    <th class="files">files renamed:</th>
+    <td class="files">#filesrenamed|breakentities#</td></tr>
+</table>
+
+<div id="changesetDiff">
+#diff#
+</div>

=== added file 'bzrlib/plugins/webserve/templates/diffpage.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/diffpage.tmpl
@@ -0,0 +1,58 @@
+#header#
+<title>#repo|escape#: Diff #otherrevid# - #revid# </title>
+</head>
+<body>
+
+<table width=100%>
+  <tr>
+     <td align=left>
+        <div class="buttons">
+        <a href="?cmd=changelog;rev=">main</a>
+        <a href="?cmd=changelog;rev=#revid#">log</a>
+        <a href="?cmd=inventory;rev=#revid#;path=">inventory</a>
+        <a href="?cmd=diff;otherrevid=#revid#;path=#path#;pathrevid=#pathrevid#;rev=#otherrevid#">swap diff</a>        
+        </div>
+     <td align=right>#diffnav#
+</table>
+
+<center>
+    <h1>Diff</h1>
+    <hr width=80%>
+</center>
+
+<table id="changesetEntry">
+    <tr>
+        <th class="revision">revision 1:</th>
+        <td class="revision">#linkrevid#</td>
+    </tr>
+    <tr>
+    <tr>
+        <th class="revision">revision 2:</th>
+        <td class="revision">#linkotherrevid#</td>
+    </tr>
+    #changelogpath#
+</table>
+
+#diff#
+
+<br>
+<center>
+    <hr width=80%>
+</center>
+<table width=100%>
+  <tr>
+     <td align=left>
+        <div class="buttons">
+        <a href="?cmd=changelog;rev=">main</a>
+        <a href="?cmd=changelog;rev=#revid#">log</a>
+        <a href="?cmd=inventory;rev=#revid#;path=">inventory</a>
+        <a href="?cmd=diff;otherrevid=#revid#;path=#path#;pathrevid=#pathrevid#;rev=#otherrevid#">swap diff</a>
+        </div>
+     <td align=right>#diffnav#
+</table>
+
+
+</body>
+</html>
+
+

=== added file 'bzrlib/plugins/webserve/templates/error.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/error.tmpl
@@ -0,0 +1,15 @@
+#header#
+    <title>
+    #repo|escape#:
+    </title>
+</head>
+
+<body>
+
+<center><h1><font color=red>Warning</font></h1></center>
+The command <br>
+    <center><h2><i>#command#</i><h2></center>
+is unknown or not available
+
+</body>
+<html>
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/filecontent-raw.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/filecontent-raw.tmpl
@@ -0,0 +1,2 @@
+#header#
+#entries|addnl#

=== added file 'bzrlib/plugins/webserve/templates/filecontent.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/filecontent.tmpl
@@ -0,0 +1,74 @@
+#header#
+<title>#repo|escape#: content #rev#</title>
+</head>
+<body>
+
+<table width=100%>
+  <tr>
+     <td align=left>
+        <div class="buttons">
+        <a href="?cmd=changelog;rev=">main</a>
+        <a href="?cmd=changelog;rev=#node#">log</a>
+        <a href="?cmd=inventory;rev=#node#;path=">inventory</a>
+        <a href="?cmd=changelog;rev=#node#;path=#path|escape#;pathrevid=#pathrevid#">filelog</a>
+        <a href="?cmd=content;rev=#node#;path=#path|escape#;pathrevid=#pathrevid#;style=raw">raw</a>
+        </div>
+     <td align=right>#cntnav#
+</table>
+
+<center>
+    <h1>Annotate</h1>
+    <hr width=80%>
+</center>
+
+<table id="changesetEntry">
+<tr>
+ <th class="changeset">revision:</th>
+ <td class="changeset">#linkrev#</td>
+</tr>
+
+<tr>
+ <th class="path">path:</th>
+ <td class="path">#linkpath#</td>
+</tr>
+
+<tr>
+ <th class="author">commiter:</th>
+ <td class="author">#author|obfuscate#</td>
+</tr>
+<tr>
+ <th class="date">date:</th>
+ <td class="date">#date|date# (#date|age# ago)</td></tr>
+
+<tr>
+ <th class="description">description:</th>
+ <td class="description">#desc|escape|addbreaks#</td>
+</tr>
+</table>
+
+<div id="filelog">
+<table cellspacing="0" cellpadding="0">
+#entries|addnl#
+<table>
+</div>
+
+
+<center>
+    <hr width=80%>
+</center>
+
+<table width=100%>
+  <tr>
+     <td align=left>
+        <div class="buttons">
+        <a href="?cmd=changelog;rev=">main</a>
+        <a href="?cmd=changelog;rev=#node#">log</a>
+        <a href="?cmd=inventory;rev=#node#;path=">inventory</a>
+        <a href="?cmd=changelog;rev=#node#;path=#path|escape#;pathrevid=#pathrevid#">filelog</a>
+        <a href="?cmd=content;rev=#node#;path=#path|escape#;pathrevid=#pathrevid#;style=raw">raw</a>
+        </div>
+     <td align=right>#cntnav#
+</table>
+
+#footer#
+

=== added file 'bzrlib/plugins/webserve/templates/footer.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/footer.tmpl
@@ -0,0 +1,2 @@
+</body>
+</html>

=== added file 'bzrlib/plugins/webserve/templates/header-raw.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/header-raw.tmpl
@@ -0,0 +1,1 @@
+Content-type: text/text

=== added file 'bzrlib/plugins/webserve/templates/header-rss.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/header-rss.tmpl
@@ -0,0 +1,7 @@
+Content-type: text/xml
+
+<!-- derived from mercurial project -->
+<rss version="2.0">
+  <channel>
+    <link>#url#</link>
+    <language>en-us</language>

=== added file 'bzrlib/plugins/webserve/templates/header.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/header.tmpl
@@ -0,0 +1,91 @@
+Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<style type="text/css">
+<!--
+a { text-decoration:none; }
+.parity0 { background-color: #aabbcc; }
+.parity1 { background-color: #bbccdd; }
+.lineno { width: 60px; color: #ffffff; font-size: smaller; }
+.plusline { color: green; }
+.minusline { color: red; }
+.atline { color: purple; }
+.infoline { color: blue; }
+.annotate { font-size: smaller; text-align: right; padding-right: 1em; }
+
+
+.navbuttons {
+  background-color: #446677;
+  padding: 0;
+  color: white;
+  font-family: sans;
+  font-weight: bold;
+}
+
+.navbuttons a {
+  background-color: #446677;
+  padding: 0;
+  color: white;
+  font-family: sans;
+  font-weight: bold;
+}
+
+
+.navbuttons a:hover {
+  background-color: #bbddee;
+  padding: 0;
+  color: black;
+  font-family: sans;
+  font-weight: bold;
+}
+
+
+.buttons a {
+  background-color: #446677;
+  padding: 2pt;
+  color: white;
+  font-family: sans;
+  font-weight: bold;
+}
+
+.buttons a:hover {
+  background-color: #bbddee;
+  padding: 2pt;
+  color: black;
+  font-family: sans;
+  font-weight: bold;
+}
+.metatag {
+  background-color: #888888;
+  color: white;
+  text-align: right; 
+}
+
+/* Common */
+pre { margin: 0; }
+
+
+/* Changelog entries */
+.changelogEntry { width: 100%; }
+.changelogEntry th { font-weight: normal; text-align: right; vertical-align: top; width: 15%;}
+.changelogEntry th.age, .changelogEntry th.firstline { font-weight: bold; }
+.changelogEntry th.firstline { text-align: left; width: inherit; }
+
+/* Tag entries */
+#tagEntries { list-style: none; margin: 0; padding: 0; }
+#tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
+#tagEntries .tagEntry span.node { font-family: monospace; }
+
+/* Changeset entry */
+#changesetEntry { }
+#changesetEntry th { font-weight: normal; background-color: #446677; color: white; text-align: right; }
+#changesetEntry th.files, #changesetEntry th.description { vertical-align: top; }
+
+/* File diff view */
+#filediffEntry { }
+#filediffEntry th { font-weight: normal; background-color: #446677; color: white; text-align: right; }
+
+-->
+</style>

=== added file 'bzrlib/plugins/webserve/templates/index.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/index.tmpl
@@ -0,0 +1,18 @@
+#header#
+<title>Bazaar-NG repositories index</title>
+</head>
+<body>
+
+<h2>Bazaar-NG Repositories</h2>
+
+<table>
+    <tr>
+        <td>Name</td>
+        <td>Description</td>
+        <td>Author</td>
+        <td>Last change</td>
+    <tr>
+    #entries#
+</table>
+
+#footer#

=== added file 'bzrlib/plugins/webserve/templates/inventory.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/inventory.tmpl
@@ -0,0 +1,67 @@
+#header#
+<title>#repo|escape#: inventory #rev#: #path|escape#</title>
+</head>
+<body>
+
+
+
+<table width=100%>
+  <tr>
+     <td align=left>
+        <div class="buttons">
+        <a href="?cmd=changelog;rev=">main</a>
+        <a href="?cmd=changelog;rev=#rev#">log</a>
+        <a href="?cmd=revision;rev=#rev#">revision</a>
+        <a href="?cmd=export;rev=#rev#;path=#actualpath|escape#;mode=tgz">targz</a>
+        <a href="?cmd=export;rev=#rev#;path=#actualpath|escape#;mode=zip">zip</a>
+        </div>
+     <td align=right>
+        #invnav#
+</table>
+
+<center>
+    <h1>Inventory</h1>
+    <hr width=80%>
+</center>
+
+<table id="changesetEntry">
+<tr>
+ <th class="changeset">Revision:</th>
+ <td class="changeset">#revlink#</td>
+</tr>
+<tr>
+ <th class="path">Path:</th>
+ <td class="path">#linkpath#</td>
+</tr>
+</table>
+
+
+<table cellpadding="0" cellspacing="0" width="100%">
+<tr class="parity1">
+  <td><tt>drwxr-xr-x</tt>&nbsp;
+  <td>
+  <td>
+  <td><a href="?cmd=inventory;rev=#rev#;path=#parent|escape#/;pathrevid=#pathrevid#">[up]</a>
+  <td align=right>
+#entries#
+</table>
+
+<center>
+    <hr width=80%>
+</center>
+
+<table width=100%>
+  <tr>
+     <td align=left>
+        <div class="buttons">
+        <a href="?cmd=changelog;rev=">main</a>
+        <a href="?cmd=changelog;rev=#rev#">log</a>
+        <a href="?cmd=revision;rev=#rev#">revision</a>
+        <a href="?cmd=export;rev=#rev#;path=#actualpath|escape#;mode=tgz">targz</a>
+        <a href="?cmd=export;rev=#rev#;path=#actualpath|escape#;mode=zip">zip</a>
+        </div>
+     <td align=right>
+        #invnav#
+</table>
+
+#footer#

=== added file 'bzrlib/plugins/webserve/templates/map'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/map
@@ -0,0 +1,76 @@
+#
+# this is comment
+# empty line are allowed
+#
+
+default = "changelog"
+header = header.tmpl
+footer = footer.tmpl
+search = search.tmpl
+
+changelog = changelog.tmpl
+naventry = "<a href="?cmd=changelog;rev=#revid#;otherrevid=#otherrevid#">#label#</a> "
+cflnaventry = "<a href="?cmd=changelog;;otherrevid=#otherrevid#;rev=#revid#;pathrevid=#revid#;path=#path|escape#">#label#</a> "
+changelogentry = changelogentry.tmpl
+changelogparent = "<th class="changesetRev">parent:</th> <td class="changesetNode">#revlink#</td>"
+
+dodiff = "<th><a href="?cmd=diff;otherrevid=#otherrevid#;rev=#revid#;pathrevid=#pathrevid#;path=#path|escape#">Diff</a>"
+changelogpath = "<tr><th class=path>path:<td class=path>#pathlink#"
+diffrevision = "<tr><th class=path>diff:<td class=path>#link#<td ><a href="?cmd=changelog;rev=#revid#;pathrevid=#revid#;path=#path|escape#">[Clear]</a>"
+
+
+inventory = inventory.tmpl
+inventoryfileentry = "<tr class="parity#parity#"><td><tt>-#perm|permissions#</tt>&nbsp;<td><a href='?cmd=content;rev=#rev#;path=#fullname|escape#'>[A]<td><a href='?cmd=changelog;rev=#rev#;pathrevid=#rev#;path=#fullname|escape#'>[L]</a><td>#name|escape#<td align=right>#id#"
+inventorylinkentry = "<tr class="parity#parity#"><td><tt>-#perm|permissions#</tt>&nbsp;<td><a href='?cmd=content;rev=#rev#;path=#fullname|escape#'>[A]<td><a href='?cmd=changelog;rev=#rev#;pathrevid=#rev#;path=#fullname|escape#'>[L]</a><td>@#name|escape#<td align=right>#id#"
+inventorydirentry = "<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td><td><td><a href="?cmd=inventory;rev=#rev#;path=#path|escape#/">#name|escape#</a><td align=right>#id#"
+
+filecontent = filecontent.tmpl
+
+revision = revision.tmpl
+difflineplus = "<span class="plusline">#line|escape#</span>"
+difflineminus = "<span class="minusline">#line|escape#</span>"
+
+difffileminus = "<span class="minusline">--- #path#</span><br>"
+difffileplus = "<span class="plusline">+++ #path#</span><br>"
+
+difflineat = "<span class="atline">#line|escape#</span>"
+difflineinfo = "<span class="infoline">#line|escape#</span>"
+diffline = "#line|escape#"
+
+diffentry = diffentry.tmpl
+
+diffpage = diffpage.tmpl
+diffnaventry = "<a href="?cmd=diff;otherrevid=#otherrevid#;revid=#revid#pathrevid=#pathrevid#;path=#path|escape#">#label#</a> "
+
+diffblock = "<pre class="parity#parity#">#lines#</pre>"
+indexentry = "<tr class="parity#parity#"><td><a  href="#url#">#name#</a></td><td>#shortdesc#</td><td>#author# <i>#email|obfuscate#</i></td><td>#lastupdate|age# ago</td></tr>"
+index = index.tmpl
+
+revfileentry = "<a href='?cmd=content;rev=#rev#;fileid=#fileid#;pathrevid=#pathrevid#;path=#fullname|escape#'>#fullname|escape#</a><br>"
+revlinkentry = "@&nbsp;#str#"
+revdirentry = "<a href='?cmd=inventory;rev=#rev#;fileid=#fileid#;pathrevid=#pathrevid#;path=#fullname|escape#/'>#fullname|escape#/</a><br>"
+revrename = "#oldpath|escape# =&gt; #newpath#"
+revdelete = "#path#"
+
+revnaventry = "<a href="?cmd=revision;revid=#revid#">#label#</a> "
+invnaventry = "<a href="?cmd=inventory;pathrevid=#pathrevid#;path=#path|escape#;rev=#revid#">#label#</a> "
+cntnaventry = "<a href="?cmd=content;path=#path|escape#;pathrevid=#pathrevid#;rev=#revid#">#label#</a> "
+
+annotateline = "<tr class="parity#parity#"><td class="annotate"><a title="#revnorevid#" href="?cmd=revision;rev=#revnorevid#">#revnostr#</a>:<a title="#revid#" href="?cmd=revision;rev=#revid#">#author|obfuscate#</a></td><td><pre>#line|escape#</pre></td></tr>"
+linkline = "<tr class="parity#parity#"><td class="annotate"><pre>#name|escape#<pre><td><pre>&nbsp;-&gt;&nbsp;<pre><td><pre>#value|escape#</pre></td></tr>"
+
+pathnotfound = pathnotfound.tmpl
+revisionnotfound = revisionnotfound.tmpl
+textnotfound = textnotfound.tmpl
+error=error.tmpl
+
+# template_path
+ptdir = "<a href="?cmd=inventory;pathrevid=#pathrevid#;path=#fullpath|escape#;rev=#rev#">#elem|escape#/&nbsp;</a>"
+ptfname = "<a href="?cmd=content;path=#fullpath|escape#;pathrevid=#pathrevid#;rev=#rev#">#elem|escape#</a> @ #atpathrevid#"
+ptfnameshort = "<a href="?cmd=content;path=#fullpath|escape#;pathrevid=#pathrevid#;rev=#rev#">#elem|escape#</a>"
+
+# template_revid
+tmplrevid = "<a href="?cmd=revision;revid=#revid#">#revno#:#revid#</a>"
+tmplrevidmerged = "<a title="#revm#" href="?cmd=revision;revid=#revm#">[#revno#]:<a href="?cmd=revision;revid=#revid#">#revid#</a>"
+
+navigation = navigation.tmpl

=== added file 'bzrlib/plugins/webserve/templates/map-raw'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/map-raw
@@ -0,0 +1,3 @@
+header = header-raw.tmpl
+filecontent = filecontent-raw.tmpl
+annotateline = "#line#"

=== added file 'bzrlib/plugins/webserve/templates/map-rss'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/map-rss
@@ -0,0 +1,5 @@
+changelog = changelog-rss.tmpl
+changelogentry = changelogentry-rss.tmpl
+header = header-rss.tmpl
+changefilelog = changefilelog-rss.tmpl
+changefilelogentry = changefilelogentry-rss.tmpl
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/navigation.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/navigation.tmpl
@@ -0,0 +1,16 @@
+<!-- <table class="navbuttons" border=1>
+<tr>
+    <td> #pa geprev# 
+    <td ><table>
+                <tr><td><center><b>#po s# &nbsp;/ &nbsp; #co unt#</center></b>
+                <tr><td>#na vigation#
+        </table>
+    <td> #page next# </a>
+
+</table>
+-->
+
+#pageprev# 
+ <b>#pos# &nbsp;/ &nbsp; #count#</b> :
+ #navigation# 
+#pagenext#
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/pathnotfound.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/pathnotfound.tmpl
@@ -0,0 +1,16 @@
+#header#
+    <title>
+    #repo|escape#: 
+    </title>
+</head>
+
+<body>
+
+<center><h1><font color=red>Warning</font></h1></center>
+The path <br>
+    <center><h2><i>#path# @ #pathrevid#</i><h2></center>
+doesn't exist at revision 
+    <center><h2><i>#rev#</i></h2></center>
+
+</body>
+<html>
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/revision.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/revision.tmpl
@@ -0,0 +1,70 @@
+#header#
+<title>#repo|escape#: revision #node#</title>
+</head>
+<body>
+
+<table width=100%>
+  <tr>
+     <td align=left>
+        <div class="buttons">
+        <a href="?cmd=changelog;rev=">main</a>
+        <a href="?cmd=changelog;rev=#node#">log</a>
+        <a href="?cmd=inventory;rev=#node#;path=">inventory</a>
+        </div>
+     <td align=right>#revisionnav#
+</table>
+
+
+<!--<h2>revision: #desc|escape|firstline#</h2>-->
+<center>
+    <h1>Revision</h1>
+    <hr width=80%>
+</center>
+
+<table id="changesetEntry">
+<tr>
+ <th class="revision">revision:</th>
+ <td class="revision">#linkrev#</td>
+</tr>
+<tr>
+<tr>
+ <th class="revision">parents:</th>
+ <td class="revision">#parents#</td>
+</tr>
+<tr>
+ <th class="author">commiter:</th>
+ <td class="author">#author|obfuscate#</td>
+</tr>
+<tr>
+ <th class="date">date:</th>
+ <td class="date">#date|date# (#date|age# ago)</td></tr>
+<tr>
+ <th class="description">description:</th>
+ <td class="description">#desc|escape|addbreaks#</td>
+</tr>
+</table>
+
+
+#diff#
+
+
+<br>
+<center>
+    <hr width=80%>
+</center>
+<table width=100%>
+  <tr>
+     <td align=left>
+        <div class="buttons">
+        <a href="?cmd=changelog;rev=">main</a>
+        <a href="?cmd=changelog;rev=#node#">log</a>
+        <a href="?cmd=inventory;rev=#node#;path=">inventory</a>
+        </div>
+     <td align=right>#revisionnav#
+</table>
+
+
+</body>
+</html>
+
+

=== added file 'bzrlib/plugins/webserve/templates/revisionnotfound.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/revisionnotfound.tmpl
@@ -0,0 +1,15 @@
+#header#
+    <title>
+    #repo|escape#: 
+    </title>
+</head>
+
+<body>
+
+<center><h1><font color=red>Warning</font></h1></center>
+The revision <br>
+    <center><h2><i>#rev#</i><h2></center>
+doesn't exist 
+
+</body>
+<html>
\ No newline at end of file

=== added file 'bzrlib/plugins/webserve/templates/textnotfound.tmpl'
--- /dev/null
+++ bzrlib/plugins/webserve/templates/textnotfound.tmpl
@@ -0,0 +1,15 @@
+#header#
+    <title>
+    #repo|escape#: 
+    </title>
+</head>
+
+<body>
+
+<center><h1><font color=red>Warning</font></h1></center>
+The string <br>
+    <center><h2><i>#search#</i><h2></center>
+isn't found 
+
+</body>
+<html>
\ No newline at end of file



-- 
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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20051112/30adb293/attachment.pgp 


More information about the bazaar mailing list