--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -21,6 +21,7 @@ Vicent SeguĂ Pascual <vseguip at gmail.com>
Sean Perry <shaleh at speakeasy.net>
Nguyen Anh Quynh <aquynh at gmail.com>
Ollivier Robert <roberto at keltia.freenix.fr>
+Alexander Schremmer <alex at alexanderweb.de>
Arun Sharma <arun at sharma-home.net>
Josef "Jeff" Sipek <jeffpc at optonline.net>
Kevin Smith <yarcs at qualitycode.com>
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2495,6 +2495,8 @@ def serve(ui, repo, **opts):
"""
if opts["stdio"]:
+ if repo is None:
+ raise hg.RepoError(_('no repo found'))
fin, fout = sys.stdin, sys.stdout
sys.stdout = sys.stderr
@@ -2566,11 +2568,15 @@ def serve(ui, repo, **opts):
r = repo.addchangegroup(fin)
respond(str(r))
- optlist = "name templates style address port ipv6 accesslog errorlog"
+ optlist = ("name templates style address port ipv6"
+ " accesslog errorlog webdir_conf")
for o in optlist.split():
if opts[o]:
ui.setconfig("web", o, opts[o])
+ if repo is None and not ui.config("web", "webdir_conf"):
+ raise hg.RepoError(_('no repo found'))
+
if opts['daemon'] and not opts['daemon_pipefds']:
rfd, wfd = os.pipe()
args = sys.argv[:]
@@ -2582,7 +2588,7 @@ def serve(ui, repo, **opts):
os._exit(0)
try:
- httpd = hgweb.create_server(repo)
+ httpd = hgweb.create_server(ui, repo)
except socket.error, inst:
raise util.Abort(_('cannot start server: ') + inst.args[1])
@@ -2997,8 +3003,8 @@ table = {
"import|patch":
(import_,
[('p', 'strip', 1,
- _('directory strip option for patch. This has the same\n') +
- _('meaning as the corresponding patch option')),
+ _('directory strip option for patch. This has the same\n'
+ 'meaning as the corresponding patch option')),
('b', 'base', '', _('base path')),
('f', 'force', None,
_('skip check for outstanding uncommitted changes'))],
@@ -3127,6 +3133,8 @@ table = {
('a', 'address', '', _('address to use')),
('n', 'name', '',
_('name to show in web pages (default: working dir)')),
+ ('', 'webdir-conf', '', _('name of the webdir config file'
+ ' (serve more than one repo)')),
('', 'pid-file', '', _('name of file to write process ID to')),
('', 'stdio', None, _('for remote clients')),
('t', 'templates', '', _('web templates to use')),
@@ -3199,7 +3207,7 @@ globalopts = [
norepo = ("clone init version help debugancestor debugcomplete debugdata"
" debugindex debugindexdot")
-optionalrepo = ("paths debugconfig")
+optionalrepo = ("paths serve debugconfig")
def findpossible(cmd):
"""
--- a/mercurial/hgweb.py
+++ b/mercurial/hgweb.py
@@ -10,11 +10,23 @@ import os, cgi, sys
import mimetypes
from demandload import demandload
demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
-demandload(globals(), "tempfile StringIO BaseHTTPServer util")
-demandload(globals(), "archival mimetypes templater")
+demandload(globals(), "tempfile StringIO BaseHTTPServer util SocketServer")
+demandload(globals(), "archival mimetypes templater urllib")
from node import *
from i18n import gettext as _
+def splitURI(uri):
+ """ Return path and query splited from uri
+
+ Just like CGI environment, the path is unquoted, the query is
+ not.
+ """
+ if '?' in uri:
+ path, query = uri.split('?', 1)
+ else:
+ path, query = uri, ''
+ return urllib.unquote(path), query
+
def up(p):
if p[0] != "/":
p = "/" + p
@@ -776,54 +788,54 @@ class hgweb(object):
if not req.form.has_key('cmd'):
req.form['cmd'] = [self.t.cache['default'],]
- if req.form['cmd'][0] == 'changelog':
- c = self.repo.changelog.count() - 1
- hi = c
+ cmd = req.form['cmd'][0]
+ if cmd == 'changelog':
+ hi = self.repo.changelog.count() - 1
if req.form.has_key('rev'):
hi = req.form['rev'][0]
try:
hi = self.repo.changelog.rev(self.repo.lookup(hi))
except hg.RepoError:
- req.write(self.search(hi))
+ req.write(self.search(hi)) # XXX redirect to 404 page?
return
req.write(self.changelog(hi))
- elif req.form['cmd'][0] == 'changeset':
+ elif cmd == 'changeset':
req.write(self.changeset(req.form['node'][0]))
- elif req.form['cmd'][0] == 'manifest':
+ elif cmd == 'manifest':
req.write(self.manifest(req.form['manifest'][0],
clean(req.form['path'][0])))
- elif req.form['cmd'][0] == 'tags':
+ elif cmd == 'tags':
req.write(self.tags())
- elif req.form['cmd'][0] == 'summary':
+ elif cmd == 'summary':
req.write(self.summary())
- elif req.form['cmd'][0] == 'filediff':
+ elif cmd == 'filediff':
req.write(self.filediff(clean(req.form['file'][0]),
req.form['node'][0]))
- elif req.form['cmd'][0] == 'file':
+ elif cmd == 'file':
req.write(self.filerevision(clean(req.form['file'][0]),
req.form['filenode'][0]))
- elif req.form['cmd'][0] == 'annotate':
+ elif cmd == 'annotate':
req.write(self.fileannotate(clean(req.form['file'][0]),
req.form['filenode'][0]))
- elif req.form['cmd'][0] == 'filelog':
+ elif cmd == 'filelog':
req.write(self.filelog(clean(req.form['file'][0]),
req.form['filenode'][0]))
- elif req.form['cmd'][0] == 'heads':
+ elif cmd == 'heads':
req.httphdr("application/mercurial-0.1")
h = self.repo.heads()
req.write(" ".join(map(hex, h)) + "\n")
- elif req.form['cmd'][0] == 'branches':
+ elif cmd == 'branches':
req.httphdr("application/mercurial-0.1")
nodes = []
if req.form.has_key('nodes'):
@@ -831,7 +843,7 @@ class hgweb(object):
for b in self.repo.branches(nodes):
req.write(" ".join(map(hex, b)) + "\n")
- elif req.form['cmd'][0] == 'between':
+ elif cmd == 'between':
req.httphdr("application/mercurial-0.1")
nodes = []
if req.form.has_key('pairs'):
@@ -840,7 +852,7 @@ class hgweb(object):
for b in self.repo.between(pairs):
req.write(" ".join(map(hex, b)) + "\n")
- elif req.form['cmd'][0] == 'changegroup':
+ elif cmd == 'changegroup':
req.httphdr("application/mercurial-0.1")
nodes = []
if not self.allowpull:
@@ -859,7 +871,7 @@ class hgweb(object):
req.write(z.flush())
- elif req.form['cmd'][0] == 'archive':
+ elif cmd == 'archive':
changeset = self.repo.lookup(req.form['node'][0])
type = req.form['type'][0]
if (type in self.archives and
@@ -869,7 +881,7 @@ class hgweb(object):
req.write(self.t("error"))
- elif req.form['cmd'][0] == 'static':
+ elif cmd == 'static':
fname = req.form['file'][0]
req.write(staticfile(static, fname)
or self.t("error", error="%r not found" % fname))
@@ -877,20 +889,39 @@ class hgweb(object):
else:
req.write(self.t("error"))
-def create_server(repo):
+def create_server(ui, repo):
+ use_threads = True
def openlog(opt, default):
if opt and opt != '-':
return open(opt, 'w')
return default
- address = repo.ui.config("web", "address", "")
- port = int(repo.ui.config("web", "port", 8000))
- use_ipv6 = repo.ui.configbool("web", "ipv6")
- accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
- errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
+ address = ui.config("web", "address", "")
+ port = int(ui.config("web", "port", 8000))
+ use_ipv6 = ui.configbool("web", "ipv6")
+ webdir_conf = ui.config("web", "webdir_conf")
+ accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
+ errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
+
+ if use_threads:
+ try:
+ from threading import activeCount
+ except ImportError:
+ use_threads = False
- class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
+ if use_threads:
+ _mixin = SocketServer.ThreadingMixIn
+ else:
+ if hasattr(os, "fork"):
+ _mixin = SocketServer.ForkingMixIn
+ else:
+ class _mixin: pass
+
+ class MercurialHTTPServer(_mixin, BaseHTTPServer.HTTPServer):
+ pass
+
+ class IPv6HTTPServer(MercurialHTTPServer):
address_family = getattr(socket, 'AF_INET6', None)
def __init__(self, *args, **kwargs):
@@ -899,6 +930,7 @@ def create_server(repo):
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(),
@@ -920,11 +952,7 @@ def create_server(repo):
self.do_POST()
def do_hgweb(self):
- query = ""
- p = self.path.find("?")
- if p:
- query = self.path[p + 1:]
- query = query.replace('+', ' ')
+ path_info, query = splitURI(self.path)
env = {}
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
@@ -932,6 +960,7 @@ def create_server(repo):
env['SERVER_NAME'] = self.server.server_name
env['SERVER_PORT'] = str(self.server.server_port)
env['REQUEST_URI'] = "/"
+ env['PATH_INFO'] = path_info
if query:
env['QUERY_STRING'] = query
host = self.address_string()
@@ -956,13 +985,20 @@ def create_server(repo):
req = hgrequest(self.rfile, self.wfile, env)
self.send_response(200, "Script output follows")
- hg.run(req)
- hg = hgweb(repo)
+ if webdir_conf:
+ hgwebobj = hgwebdir(webdir_conf)
+ elif repo is not None:
+ hgwebobj = hgweb(repo.__class__(repo.ui, repo.origroot))
+ else:
+ raise hg.RepoError(_('no repo found'))
+ hgwebobj.run(req)
+
+
if use_ipv6:
return IPv6HTTPServer((address, port), hgwebhandler)
else:
- return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
+ return MercurialHTTPServer((address, port), hgwebhandler)
# This is a stopgap
class hgwebdir(object):
--- a/templates/header-gitweb.tmpl
+++ b/templates/header-gitweb.tmpl
@@ -7,5 +7,5 @@ Content-type: text/html
<link rel="icon" href="?static=hgicon.png" type="image/png">
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta name="robots" content="index, nofollow"/>
-<style type="text/css">/*<![CDATA[*/ @import "?static=style-gitweb.css"; /*]]>*/</style>
+<link rel="stylesheet" href="?static=style-gitweb.css" type="text/css" />
--- a/templates/header.tmpl
+++ b/templates/header.tmpl
@@ -5,4 +5,4 @@ Content-type: text/html
<head>
<link rel="icon" href="?static=hgicon.png" type="image/png">
<meta name="robots" content="index, nofollow" />
-<style type="text/css">/*<![CDATA[*/ @import "?static=style.css"; /*]]>*/</style>
+<link rel="stylesheet" href="?static=style.css" type="text/css" />