hgweb.py
689 lines
| 23.1 KiB
| text/x-python
|
PythonLexer
/ mercurial / hgweb.py
jake@edge2.net
|
r131 | #!/usr/bin/env python | ||
# | ||||
jake@edge2.net
|
r132 | # hgweb.py - 0.2 - 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | ||
jake@edge2.net
|
r131 | # - web interface to a mercurial repository | ||
# | ||||
# This software may be used and distributed according to the terms | ||||
# of the GNU General Public License, incorporated herein by reference. | ||||
# useful for debugging | ||||
import cgitb | ||||
cgitb.enable() | ||||
import os, cgi, time, re, difflib, sys, zlib | ||||
mpm@selenic.com
|
r138 | from mercurial.hg import * | ||
mpm@selenic.com
|
r157 | def templatepath(): | ||
for f in "templates/map", "../templates/map": | ||||
p = os.path.join(os.path.dirname(__file__), f) | ||||
if os.path.isfile(p): return p | ||||
mpm@selenic.com
|
r138 | def age(t): | ||
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 | ||||
mpm@selenic.com
|
r195 | if n >= 2 or s == 1: return fmt(t, n) | ||
jake@edge2.net
|
r131 | |||
def nl2br(text): | ||||
mpm@selenic.com
|
r138 | return text.replace('\n', '<br/>') | ||
jake@edge2.net
|
r131 | |||
def obfuscate(text): | ||||
mpm@selenic.com
|
r138 | 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 + "/" | ||||
jake@edge2.net
|
r131 | |||
def httphdr(type): | ||||
print 'Content-type: %s\n' % type | ||||
jake@edge2.net
|
r135 | def write(*things): | ||
for thing in things: | ||||
if hasattr(thing, "__iter__"): | ||||
for part in thing: | ||||
write(part) | ||||
else: | ||||
sys.stdout.write(str(thing)) | ||||
mpm@selenic.com
|
r138 | def template(tmpl, **map): | ||
while tmpl: | ||||
m = re.search(r"#([a-zA-Z0-9]+)#", tmpl) | ||||
if m: | ||||
yield tmpl[:m.start(0)] | ||||
v = map.get(m.group(1), "") | ||||
yield callable(v) and v() or v | ||||
tmpl = tmpl[m.end(0):] | ||||
else: | ||||
yield tmpl | ||||
return | ||||
class templater: | ||||
def __init__(self, mapfile): | ||||
self.cache = {} | ||||
self.map = {} | ||||
self.base = os.path.dirname(mapfile) | ||||
for l in file(mapfile): | ||||
m = re.match(r'(\S+)\s*=\s*"(.*)"$', l) | ||||
jake@edge2.net
|
r133 | if m: | ||
mpm@selenic.com
|
r138 | 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)) | ||||
jake@edge2.net
|
r133 | else: | ||
mpm@selenic.com
|
r138 | raise "unknown map entry '%s'" % l | ||
jake@edge2.net
|
r133 | |||
mpm@selenic.com
|
r138 | def __call__(self, t, **map): | ||
try: | ||||
tmpl = self.cache[t] | ||||
except KeyError: | ||||
tmpl = self.cache[t] = file(self.map[t]).read() | ||||
return template(tmpl, **map) | ||||
class hgweb: | ||||
maxchanges = 20 | ||||
maxfiles = 10 | ||||
jake@edge2.net
|
r133 | |||
mpm@selenic.com
|
r157 | def __init__(self, path, name, templatemap = ""): | ||
templatemap = templatemap or templatepath() | ||||
mpm@selenic.com
|
r138 | self.reponame = name | ||
self.repo = repository(ui(), path) | ||||
self.t = templater(templatemap) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | def date(self, cs): | ||
return time.asctime(time.gmtime(float(cs[2].split(' ')[0]))) | ||||
def listfiles(self, files, mf): | ||||
for f in files[:self.maxfiles]: | ||||
yield self.t("filenodelink", node = hex(mf[f]), file = f) | ||||
if len(files) > self.maxfiles: | ||||
yield self.t("fileellipses") | ||||
def listfilediffs(self, files, changeset): | ||||
for f in files[:self.maxfiles]: | ||||
yield self.t("filedifflink", node = hex(changeset), file = f) | ||||
if len(files) > self.maxfiles: | ||||
yield self.t("fileellipses") | ||||
mpm@selenic.com
|
r156 | def parent(self, t1, node=nullid, rev=-1, **args): | ||
mpm@selenic.com
|
r142 | if node != hex(nullid): | ||
mpm@selenic.com
|
r156 | yield self.t(t1, node = node, rev = rev, **args) | ||
mpm@selenic.com
|
r142 | |||
mpm@selenic.com
|
r138 | def diff(self, node1, node2, files): | ||
def filterfiles(list, files): | ||||
l = [ x for x in list if x in files ] | ||||
for f in files: | ||||
if f[-1] != os.sep: f += os.sep | ||||
l += [ x for x in list if x.startswith(f) ] | ||||
return l | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r172 | parity = [0] | ||
def diffblock(diff, f, fn): | ||||
yield self.t("diffblock", | ||||
lines = prettyprintlines(diff), | ||||
parity = parity[0], | ||||
file = f, | ||||
filenode = hex(fn)) | ||||
parity[0] = 1 - parity[0] | ||||
def prettyprintlines(diff): | ||||
mpm@selenic.com
|
r138 | for l in diff.splitlines(1): | ||
line = cgi.escape(l) | ||||
if line.startswith('+'): | ||||
yield self.t("difflineplus", line = line) | ||||
elif line.startswith('-'): | ||||
yield self.t("difflineminus", line = line) | ||||
elif line.startswith('@'): | ||||
yield self.t("difflineat", line = line) | ||||
else: | ||||
yield self.t("diffline", line = line) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | r = self.repo | ||
cl = r.changelog | ||||
mf = r.manifest | ||||
change1 = cl.read(node1) | ||||
change2 = cl.read(node2) | ||||
mmap1 = mf.read(change1[0]) | ||||
mmap2 = mf.read(change2[0]) | ||||
date1 = self.date(change1) | ||||
date2 = self.date(change2) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | c, a, d = r.diffrevs(node1, node2) | ||
c, a, d = map(lambda x: filterfiles(x, files), (c, a, d)) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | for f in c: | ||
to = r.file(f).read(mmap1[f]) | ||||
tn = r.file(f).read(mmap2[f]) | ||||
mpm@selenic.com
|
r172 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) | ||
mpm@selenic.com
|
r138 | for f in a: | ||
to = "" | ||||
tn = r.file(f).read(mmap2[f]) | ||||
mpm@selenic.com
|
r172 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) | ||
mpm@selenic.com
|
r138 | for f in d: | ||
to = r.file(f).read(mmap1[f]) | ||||
tn = "" | ||||
mpm@selenic.com
|
r172 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) | ||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r142 | def header(self): | ||
yield self.t("header", repo = self.reponame) | ||||
def footer(self): | ||||
yield self.t("footer", repo = self.reponame) | ||||
mpm@selenic.com
|
r180 | def changelog(self, pos): | ||
mpm@selenic.com
|
r138 | def changenav(): | ||
def seq(factor = 1): | ||||
yield 1 * factor | ||||
mpm@selenic.com
|
r173 | yield 3 * factor | ||
#yield 5 * factor | ||||
mpm@selenic.com
|
r138 | for f in seq(factor * 10): | ||
yield f | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r173 | l = [] | ||
for f in seq(): | ||||
if f < self.maxchanges / 2: continue | ||||
if f > count: break | ||||
r = "%d" % f | ||||
if pos + f < count - (f/2): l.append(("+" + r, pos + f)) | ||||
if pos - f >= 0 + (f/2): l.insert(0, ("-" + r, pos - f)) | ||||
yield self.t("naventry", rev = 0, label="(0)") | ||||
mpm@selenic.com
|
r138 | |||
mpm@selenic.com
|
r173 | for label, rev in l: | ||
yield self.t("naventry", label = label, rev = rev) | ||||
yield self.t("naventry", rev = count - 1, label="tip") | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | def changelist(): | ||
mpm@selenic.com
|
r142 | parity = (start - end) & 1 | ||
mpm@selenic.com
|
r138 | cl = self.repo.changelog | ||
l = [] # build a list in forward order for efficiency | ||||
for i in range(start, end + 1): | ||||
n = cl.node(i) | ||||
changes = cl.read(n) | ||||
hn = hex(n) | ||||
p1, p2 = cl.parents(n) | ||||
t = float(changes[2].split(' ')[0]) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | l.insert(0, self.t( | ||
'changelogentry', | ||||
mpm@selenic.com
|
r142 | parity = parity, | ||
mpm@selenic.com
|
r138 | author = obfuscate(changes[1]), | ||
shortdesc = cgi.escape(changes[4].splitlines()[0]), | ||||
age = age(t), | ||||
mpm@selenic.com
|
r142 | parent1 = self.parent("changelogparent", | ||
hex(p1), cl.rev(p1)), | ||||
parent2 = self.parent("changelogparent", | ||||
hex(p2), cl.rev(p2)), | ||||
mpm@selenic.com
|
r138 | p1 = hex(p1), p2 = hex(p2), | ||
p1rev = cl.rev(p1), p2rev = cl.rev(p2), | ||||
manifest = hex(changes[0]), | ||||
desc = nl2br(cgi.escape(changes[4])), | ||||
date = time.asctime(time.gmtime(t)), | ||||
files = self.listfilediffs(changes[3], n), | ||||
rev = i, | ||||
node = hn)) | ||||
mpm@selenic.com
|
r142 | parity = 1 - parity | ||
mpm@selenic.com
|
r138 | |||
yield l | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r168 | cl = self.repo.changelog | ||
mf = cl.read(cl.tip())[0] | ||||
count = cl.count() | ||||
mpm@selenic.com
|
r138 | end = min(pos, count - 1) | ||
start = max(0, pos - self.maxchanges) | ||||
end = min(count - 1, start + self.maxchanges) | ||||
mpm@selenic.com
|
r142 | yield self.t('changelog', | ||
header = self.header(), | ||||
footer = self.footer(), | ||||
repo = self.reponame, | ||||
changenav = changenav, | ||||
mpm@selenic.com
|
r168 | manifest = hex(mf), | ||
mpm@selenic.com
|
r142 | rev = pos, changesets = count, entries = changelist) | ||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | def changeset(self, nodeid): | ||
n = bin(nodeid) | ||||
cl = self.repo.changelog | ||||
changes = cl.read(n) | ||||
p1, p2 = cl.parents(n) | ||||
p1rev, p2rev = cl.rev(p1), cl.rev(p2) | ||||
t = float(changes[2].split(' ')[0]) | ||||
jake@edge2.net
|
r133 | files = [] | ||
mpm@selenic.com
|
r138 | mf = self.repo.manifest.read(changes[0]) | ||
jake@edge2.net
|
r131 | for f in changes[3]: | ||
mpm@selenic.com
|
r138 | files.append(self.t("filenodelink", | ||
filenode = hex(mf[f]), file = f)) | ||||
def diff(): | ||||
yield self.diff(p1, n, changes[3]) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | yield self.t('changeset', | ||
mpm@selenic.com
|
r142 | header = self.header(), | ||
footer = self.footer(), | ||||
repo = self.reponame, | ||||
mpm@selenic.com
|
r138 | diff = diff, | ||
rev = cl.rev(n), | ||||
node = nodeid, | ||||
shortdesc = cgi.escape(changes[4].splitlines()[0]), | ||||
mpm@selenic.com
|
r142 | parent1 = self.parent("changesetparent", | ||
hex(p1), cl.rev(p1)), | ||||
parent2 = self.parent("changesetparent", | ||||
hex(p2), cl.rev(p2)), | ||||
mpm@selenic.com
|
r138 | p1 = hex(p1), p2 = hex(p2), | ||
p1rev = cl.rev(p1), p2rev = cl.rev(p2), | ||||
manifest = hex(changes[0]), | ||||
author = obfuscate(changes[1]), | ||||
desc = nl2br(cgi.escape(changes[4])), | ||||
date = time.asctime(time.gmtime(t)), | ||||
files = files) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | def filelog(self, f, filenode): | ||
cl = self.repo.changelog | ||||
fl = self.repo.file(f) | ||||
count = fl.count() | ||||
def entries(): | ||||
l = [] | ||||
mpm@selenic.com
|
r142 | parity = (count - 1) & 1 | ||
mpm@selenic.com
|
r138 | for i in range(count): | ||
n = fl.node(i) | ||||
lr = fl.linkrev(n) | ||||
cn = cl.node(lr) | ||||
cs = cl.read(cl.node(lr)) | ||||
p1, p2 = fl.parents(n) | ||||
t = float(cs[2].split(' ')[0]) | ||||
jake@edge2.net
|
r133 | |||
mpm@selenic.com
|
r138 | l.insert(0, self.t("filelogentry", | ||
mpm@selenic.com
|
r142 | parity = parity, | ||
mpm@selenic.com
|
r138 | filenode = hex(n), | ||
filerev = i, | ||||
file = f, | ||||
node = hex(cn), | ||||
author = obfuscate(cs[1]), | ||||
age = age(t), | ||||
date = time.asctime(time.gmtime(t)), | ||||
shortdesc = cgi.escape(cs[4].splitlines()[0]), | ||||
p1 = hex(p1), p2 = hex(p2), | ||||
p1rev = fl.rev(p1), p2rev = fl.rev(p2))) | ||||
mpm@selenic.com
|
r142 | parity = 1 - parity | ||
mpm@selenic.com
|
r138 | |||
yield l | ||||
yield self.t("filelog", | ||||
mpm@selenic.com
|
r142 | header = self.header(), | ||
footer = self.footer(), | ||||
repo = self.reponame, | ||||
mpm@selenic.com
|
r138 | file = f, | ||
filenode = filenode, | ||||
entries = entries) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | def filerevision(self, f, node): | ||
fl = self.repo.file(f) | ||||
n = bin(node) | ||||
text = cgi.escape(fl.read(n)) | ||||
changerev = fl.linkrev(n) | ||||
cl = self.repo.changelog | ||||
cn = cl.node(changerev) | ||||
cs = cl.read(cn) | ||||
p1, p2 = fl.parents(n) | ||||
t = float(cs[2].split(' ')[0]) | ||||
mfn = cs[0] | ||||
mpm@selenic.com
|
r142 | |||
def lines(): | ||||
for l, t in enumerate(text.splitlines(1)): | ||||
yield self.t("fileline", | ||||
line = t, | ||||
linenumber = "% 6d" % (l + 1), | ||||
parity = l & 1) | ||||
mpm@selenic.com
|
r138 | |||
yield self.t("filerevision", file = f, | ||||
mpm@selenic.com
|
r142 | header = self.header(), | ||
footer = self.footer(), | ||||
repo = self.reponame, | ||||
mpm@selenic.com
|
r138 | filenode = node, | ||
path = up(f), | ||||
mpm@selenic.com
|
r142 | text = lines(), | ||
mpm@selenic.com
|
r138 | rev = changerev, | ||
node = hex(cn), | ||||
manifest = hex(mfn), | ||||
author = obfuscate(cs[1]), | ||||
age = age(t), | ||||
date = time.asctime(time.gmtime(t)), | ||||
shortdesc = cgi.escape(cs[4].splitlines()[0]), | ||||
mpm@selenic.com
|
r142 | parent1 = self.parent("filerevparent", | ||
mpm@selenic.com
|
r156 | hex(p1), fl.rev(p1), file=f), | ||
mpm@selenic.com
|
r142 | parent2 = self.parent("filerevparent", | ||
mpm@selenic.com
|
r156 | hex(p2), fl.rev(p2), file=f), | ||
mpm@selenic.com
|
r138 | p1 = hex(p1), p2 = hex(p2), | ||
p1rev = fl.rev(p1), p2rev = fl.rev(p2)) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | def fileannotate(self, f, node): | ||
bcache = {} | ||||
ncache = {} | ||||
fl = self.repo.file(f) | ||||
n = bin(node) | ||||
changerev = fl.linkrev(n) | ||||
cl = self.repo.changelog | ||||
cn = cl.node(changerev) | ||||
cs = cl.read(cn) | ||||
p1, p2 = fl.parents(n) | ||||
t = float(cs[2].split(' ')[0]) | ||||
mfn = cs[0] | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | def annotate(): | ||
mpm@selenic.com
|
r142 | parity = 1 | ||
last = None | ||||
mpm@selenic.com
|
r138 | for r, l in fl.annotate(n): | ||
try: | ||||
cnode = ncache[r] | ||||
except KeyError: | ||||
cnode = ncache[r] = self.repo.changelog.node(r) | ||||
try: | ||||
name = bcache[r] | ||||
except KeyError: | ||||
cl = self.repo.changelog.read(cnode) | ||||
name = cl[1] | ||||
f = name.find('@') | ||||
if f >= 0: | ||||
name = name[:f] | ||||
bcache[r] = name | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r142 | if last != cnode: | ||
parity = 1 - parity | ||||
last = cnode | ||||
mpm@selenic.com
|
r138 | yield self.t("annotateline", | ||
mpm@selenic.com
|
r142 | parity = parity, | ||
mpm@selenic.com
|
r138 | node = hex(cnode), | ||
rev = r, | ||||
author = name, | ||||
file = f, | ||||
line = cgi.escape(l)) | ||||
yield self.t("fileannotate", | ||||
mpm@selenic.com
|
r142 | header = self.header(), | ||
footer = self.footer(), | ||||
repo = self.reponame, | ||||
mpm@selenic.com
|
r138 | file = f, | ||
filenode = node, | ||||
annotate = annotate, | ||||
path = up(f), | ||||
rev = changerev, | ||||
node = hex(cn), | ||||
manifest = hex(mfn), | ||||
author = obfuscate(cs[1]), | ||||
age = age(t), | ||||
date = time.asctime(time.gmtime(t)), | ||||
shortdesc = cgi.escape(cs[4].splitlines()[0]), | ||||
mpm@selenic.com
|
r156 | parent1 = self.parent("fileannotateparent", | ||
hex(p1), fl.rev(p1), file=f), | ||||
parent2 = self.parent("fileannotateparent", | ||||
hex(p2), fl.rev(p2), file=f), | ||||
mpm@selenic.com
|
r138 | p1 = hex(p1), p2 = hex(p2), | ||
p1rev = fl.rev(p1), p2rev = fl.rev(p2)) | ||||
jake@edge2.net
|
r136 | |||
mpm@selenic.com
|
r138 | def manifest(self, mnode, path): | ||
mf = self.repo.manifest.read(bin(mnode)) | ||||
rev = self.repo.manifest.rev(bin(mnode)) | ||||
node = self.repo.changelog.node(rev) | ||||
files = {} | ||||
mpm@selenic.com
|
r142 | |||
mpm@selenic.com
|
r138 | p = path[1:] | ||
l = len(p) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | for f,n in mf.items(): | ||
if f[:l] != p: | ||||
continue | ||||
remain = f[l:] | ||||
if "/" in remain: | ||||
short = remain[:remain.find("/") + 1] # bleah | ||||
mpm@selenic.com
|
r142 | files[short] = (f, None) | ||
mpm@selenic.com
|
r138 | else: | ||
short = os.path.basename(remain) | ||||
files[short] = (f, n) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | def filelist(): | ||
mpm@selenic.com
|
r142 | parity = 0 | ||
mpm@selenic.com
|
r138 | fl = files.keys() | ||
fl.sort() | ||||
for f in fl: | ||||
full, fnode = files[f] | ||||
mpm@selenic.com
|
r142 | if fnode: | ||
yield self.t("manifestfileentry", | ||||
file = full, | ||||
manifest = mnode, | ||||
filenode = hex(fnode), | ||||
parity = parity, | ||||
basename = f) | ||||
else: | ||||
yield self.t("manifestdirentry", | ||||
parity = parity, | ||||
path = os.path.join(path, f), | ||||
manifest = mnode, basename = f[:-1]) | ||||
parity = 1 - parity | ||||
mpm@selenic.com
|
r138 | |||
yield self.t("manifest", | ||||
mpm@selenic.com
|
r142 | header = self.header(), | ||
footer = self.footer(), | ||||
repo = self.reponame, | ||||
mpm@selenic.com
|
r138 | manifest = mnode, | ||
rev = rev, | ||||
node = hex(node), | ||||
path = path, | ||||
up = up(path), | ||||
mpm@selenic.com
|
r142 | entries = filelist) | ||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r168 | def tags(self): | ||
cl = self.repo.changelog | ||||
mf = cl.read(cl.tip())[0] | ||||
self.repo.lookup(0) # prime the cache | ||||
i = self.repo.tags.items() | ||||
mpm@selenic.com
|
r183 | n = [ (cl.rev(e[1]), e) for e in i ] # sort by revision | ||
n.sort() | ||||
n.reverse() | ||||
i = [ e[1] for e in n ] | ||||
mpm@selenic.com
|
r168 | |||
def entries(): | ||||
parity = 0 | ||||
for k,n in i: | ||||
yield self.t("tagentry", | ||||
parity = parity, | ||||
tag = k, | ||||
node = hex(n)) | ||||
parity = 1 - parity | ||||
yield self.t("tags", | ||||
header = self.header(), | ||||
footer = self.footer(), | ||||
repo = self.reponame, | ||||
manifest = hex(mf), | ||||
entries = entries) | ||||
mpm@selenic.com
|
r138 | def filediff(self, file, changeset): | ||
n = bin(changeset) | ||||
cl = self.repo.changelog | ||||
p1 = cl.parents(n)[0] | ||||
cs = cl.read(n) | ||||
mf = self.repo.manifest.read(cs[0]) | ||||
def diff(): | ||||
yield self.diff(p1, n, file) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | yield self.t("filediff", | ||
mpm@selenic.com
|
r142 | header = self.header(), | ||
footer = self.footer(), | ||||
repo = self.reponame, | ||||
mpm@selenic.com
|
r138 | file = file, | ||
filenode = hex(mf[file]), | ||||
node = changeset, | ||||
rev = self.repo.changelog.rev(n), | ||||
p1 = hex(p1), | ||||
p1rev = self.repo.changelog.rev(p1), | ||||
diff = diff) | ||||
# add tags to things | ||||
# tags -> list of changesets corresponding to tags | ||||
# find tag, changeset, file | ||||
jake@edge2.net
|
r131 | |||
jake@edge2.net
|
r132 | def run(self): | ||
args = cgi.parse() | ||||
mpm@selenic.com
|
r138 | if not args.has_key('cmd') or args['cmd'][0] == 'changelog': | ||
hi = self.repo.changelog.count() | ||||
jake@edge2.net
|
r153 | if args.has_key('rev'): | ||
mpm@selenic.com
|
r165 | hi = args['rev'][0] | ||
mpm@selenic.com
|
r166 | try: | ||
hi = self.repo.changelog.rev(self.repo.lookup(hi)) | ||||
except KeyError: | ||||
hi = self.repo.changelog.count() | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | write(self.changelog(hi)) | ||
jake@edge2.net
|
r132 | |||
mpm@selenic.com
|
r138 | elif args['cmd'][0] == 'changeset': | ||
write(self.changeset(args['node'][0])) | ||||
elif args['cmd'][0] == 'manifest': | ||||
write(self.manifest(args['manifest'][0], args['path'][0])) | ||||
mpm@selenic.com
|
r168 | elif args['cmd'][0] == 'tags': | ||
write(self.tags()) | ||||
mpm@selenic.com
|
r138 | elif args['cmd'][0] == 'filediff': | ||
write(self.filediff(args['file'][0], args['node'][0])) | ||||
jake@edge2.net
|
r131 | |||
jake@edge2.net
|
r132 | elif args['cmd'][0] == 'file': | ||
mpm@selenic.com
|
r138 | write(self.filerevision(args['file'][0], args['filenode'][0])) | ||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | elif args['cmd'][0] == 'annotate': | ||
write(self.fileannotate(args['file'][0], args['filenode'][0])) | ||||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r138 | elif args['cmd'][0] == 'filelog': | ||
write(self.filelog(args['file'][0], args['filenode'][0])) | ||||
jake@edge2.net
|
r136 | |||
jake@edge2.net
|
r132 | elif args['cmd'][0] == 'branches': | ||
httphdr("text/plain") | ||||
nodes = [] | ||||
if args.has_key('nodes'): | ||||
mpm@selenic.com
|
r138 | nodes = map(bin, args['nodes'][0].split(" ")) | ||
for b in self.repo.branches(nodes): | ||||
sys.stdout.write(" ".join(map(hex, b)) + "\n") | ||||
jake@edge2.net
|
r131 | |||
jake@edge2.net
|
r132 | elif args['cmd'][0] == 'between': | ||
httphdr("text/plain") | ||||
nodes = [] | ||||
if args.has_key('pairs'): | ||||
mpm@selenic.com
|
r138 | pairs = [ map(bin, p.split("-")) | ||
jake@edge2.net
|
r132 | for p in args['pairs'][0].split(" ") ] | ||
mpm@selenic.com
|
r138 | for b in self.repo.between(pairs): | ||
sys.stdout.write(" ".join(map(hex, b)) + "\n") | ||||
jake@edge2.net
|
r132 | |||
elif args['cmd'][0] == 'changegroup': | ||||
httphdr("application/hg-changegroup") | ||||
nodes = [] | ||||
if args.has_key('roots'): | ||||
mpm@selenic.com
|
r138 | nodes = map(bin, args['roots'][0].split(" ")) | ||
jake@edge2.net
|
r131 | |||
jake@edge2.net
|
r132 | z = zlib.compressobj() | ||
mpm@selenic.com
|
r138 | for chunk in self.repo.changegroup(nodes): | ||
jake@edge2.net
|
r132 | sys.stdout.write(z.compress(chunk)) | ||
sys.stdout.write(z.flush()) | ||||
jake@edge2.net
|
r131 | |||
jake@edge2.net
|
r132 | else: | ||
mpm@selenic.com
|
r138 | write(self.t("error")) | ||
jake@edge2.net
|
r131 | |||
mpm@selenic.com
|
r158 | def server(path, name, templates, address, port): | ||
import BaseHTTPServer | ||||
import sys, os | ||||
class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler): | ||||
def do_POST(self): | ||||
self.do_hgweb() | ||||
def do_GET(self): | ||||
self.do_hgweb() | ||||
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 | ||||
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) | ||||
httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler) | ||||
httpd.serve_forever() | ||||