Mercurial > hg > gitweb
view hgweb.py @ 110:c37c7f784ee3
Move hg from storing files in data with base64 encoding to full
pathnames with .i and .d extensions. This means we naturally get good
FS layout, and cp and tar fix things up nicely rather than pessimizing
layout.
author | mpm@selenic.com |
---|---|
date | Fri, 20 May 2005 17:27:21 -0800 |
parents | 58039eddbdda |
children |
line wrap: on
line source
#!/usr/bin/env python # # hgweb.py - 0.1 - 9 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> # - 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 from mercurial import hg, mdiff repo_path = "." # change as needed def nl2br(text): return re.sub('\n', '<br />', text) def obfuscate(text): l = [] for c in text: l.append('&#%d;' % ord(c)) return ''.join(l) def httphdr(type): print 'Content-type: %s\n' % type class page: def __init__(self, type="text/html", title="Mercurial Web", charset="ISO-8859-1"): print 'Content-type: %s; charset=%s\n' % (type, charset) print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' print '<HTML>' print '<!-- created by hgweb 0.1 - jake@edge2.net -->' print '<HEAD><TITLE>%s</TITLE>' % title print '<style type="text/css">' print 'body { font-family: sans-serif; font-size: 12px; }' print 'table { font-size: 12px; }' print '.errmsg { font-size: 200%; color: red; }' print '.filename { font-size: 150%; color: purple; }' print '.manifest { font-size: 150%; color: purple; }' print '.filehist { font-size: 150%; color: purple; }' print '.plusline { color: green; }' print '.minusline { color: red; }' print '.atline { color: purple; }' print '</style>' print '</HEAD>' print '<BODY>' def endpage(self): print '</BODY>' print '</HTML>' def show_diff(self, a, b, fn): a = a.splitlines(1) b = b.splitlines(1) l = difflib.unified_diff(a, b, fn, fn) print '<pre>' for line in l: line = cgi.escape(line[:-1]) if line.startswith('+'): print '<span class="plusline">%s</span>' % (line, ) elif line.startswith('-'): print '<span class="minusline">%s</span>' % (line, ) elif line.startswith('@'): print '<span class="atline">%s</span>' % (line, ) else: print line print '</pre>' class errpage(page): def __init__(self): page.__init__(self, title="Mercurial Web Error Page") class change_list(page): numchanges = 50 # number of changes to show def __init__(self, repo, reponame): page.__init__(self) self.repo = repo print '<h3>Changes For: %s</h3>' % reponame def content(self, hi=None): cl = [] count = self.repo.changelog.count() if not hi: hi = count elif hi < self.numchanges: hi = self.numchanges start = 0 if hi - self.numchanges >= 0: start = hi - self.numchanges nav = "Displaying Revisions: %d-%d" % (start, hi-1) if start != 0: nav = ('<a href="?cmd=changes;hi=%d">Previous %d</a> ' \ % (start, self.numchanges)) + nav if hi != count: if hi + self.numchanges <= count: nav += ' <a href="?cmd=changes;hi=%d">Next %d</a>' \ % (hi + self.numchanges, self.numchanges) else: nav += ' <a href="?cmd=changes">Next %d</a>' % \ self.numchanges print '<center>%s</center>' % nav for i in xrange(start, hi): n = self.repo.changelog.node(i) cl.append((n, self.repo.changelog.read(n))) cl.reverse() print '<table summary="" width="100%" align="center">' for n, ch in cl: print '<tr><td>' self.change_table(n, ch) print '</td></tr>' print '</table>' print '<center>%s</center>' % nav def change_table(self, nodeid, changes): hn = hg.hex(nodeid) i = self.repo.changelog.rev(nodeid) (h1, h2) = [ hg.hex(x) for x in self.repo.changelog.parents(nodeid) ] datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) print '<table summary="" width="100%" border="1">' print '\t<tr><td valign="top" width="10%">author:</td>' + \ '<td valign="top" width="20%%">%s</td>' % \ (obfuscate(changes[1]), ) print '\t\t<td valign="top" width="10%">description:</td>' + \ '<td width="60%">' + \ '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \ (hn, nl2br(cgi.escape(changes[4])), ) print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, ) print '\t\t<td valign="top">files:</td><td valign="top">' for f in changes[3]: print '\t\t<a href="?cmd=file;cs=%s;fn=%s">%s</a> ' % \ (hn, f, cgi.escape(f), ) print '\t</td></tr>' print '\t<tr><td>revision:</td><td colspan="3">%d:<a ' % (i, ) + \ 'href="?cmd=chkin;nd=%s">%s</a></td></tr>' % (hn, hn, ) print '</table><br />' class checkin(page): def __init__(self, repo, nodestr): page.__init__(self) self.repo = repo self.node = hg.bin(nodestr) self.nodestr = nodestr print '<h3>Checkin: %s</h3>' % nodestr def content(self): changes = self.repo.changelog.read(self.node) i = self.repo.changelog.rev(self.node) parents = self.repo.changelog.parents(self.node) (h1, h2) = [ hg.hex(x) for x in parents ] (i1, i2) = [ self.repo.changelog.rev(x) for x in parents ] datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) mf = self.repo.manifest.read(changes[0]) print '<table summary="" width="100%" border="1">' print '\t<tr><td>revision:</td><td colspan="3">%d:' % (i, ), print '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \ (self.nodestr, self.nodestr, ) print '\t<tr><td>parent(s):</td><td colspan="3">%d:' % (i1, ) print '<a href="?cmd=chkin;nd=%s">%s</a>' % (h1, h1, ), if i2 != -1: print ' %d:<a href="?cmd=chkin;nd=%s">%s</a>' % \ (i2, h2, h2, ), else: print ' %d:%s' % (i2, h2, ), print '</td></tr>' print '\t<tr><td>manifest:</td><td colspan="3">%d:' % \ (self.repo.manifest.rev(changes[0]), ), print '<a href="?cmd=mf;nd=%s">%s</a></td></tr>' % \ (hg.hex(changes[0]), hg.hex(changes[0]), ) print '\t<tr><td valign="top" width="10%">author:</td>' + \ '<td valign="top" width="20%%">%s</td>' % \ (obfuscate(changes[1]), ) print '\t\t<td valign="top" width="10%">description:</td>' + \ '<td width="60%">' + \ '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \ (self.nodestr, nl2br(cgi.escape(changes[4])), ) print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, ) print '\t\t<td valign="top">files:</td><td valign="top">' for f in changes[3]: print '\t\t<a href="?cmd=file;nd=%s&fn=%s">%s</a>' % \ (hg.hex(mf[f]), f, cgi.escape(f), ), print ' ' print '\t</td></tr>' print '</table><br />' (c, a, d) = self.repo.diffrevs(parents[0], self.node) change = self.repo.changelog.read(parents[0]) mf2 = self.repo.manifest.read(change[0]) for f in c: self.show_diff(self.repo.file(f).read(mf2[f]), \ self.repo.file(f).read(mf[f]), f) for f in a: self.show_diff('', self.repo.file(f).read(mf[f]), f) for f in d: self.show_diff(self.repo.file(f).read(mf2[f]), '', f) class filepage(page): def __init__(self, repo, fn, node=None, cs=None): page.__init__(self) self.repo = repo self.fn = fn if cs: chng = self.repo.changelog.read(hg.bin(cs)) mf = self.repo.manifest.read(chng[0]) self.node = mf[self.fn] self.nodestr = hg.hex(self.node) else: self.nodestr = node self.node = hg.bin(node) print '<div class="filename">%s (%s)</div>' % \ (cgi.escape(self.fn), self.nodestr, ) print '<a href="?cmd=hist;fn=%s">history</a><br />' % self.fn def content(self): print '<pre>' print cgi.escape(self.repo.file(self.fn).read(self.node)) print '</pre>' class mfpage(page): def __init__(self, repo, node): page.__init__(self) self.repo = repo self.nodestr = node self.node = hg.bin(node) def content(self): mf = self.repo.manifest.read(self.node) fns = mf.keys() fns.sort() print '<div class="manifest">Manifest (%s)</div>' % self.nodestr for f in fns: print '<a href="?cmd=file;fn=%s;nd=%s">%s</a><br />' % \ (f, hg.hex(mf[f]), f) class histpage(page): def __init__(self, repo, fn): page.__init__(self) self.repo = repo self.fn = fn def content(self): print '<div class="filehist">File History: %s</div>' % self.fn r = self.repo.file(self.fn) print '<br />' print '<table summary="" width="100%" align="center">' for i in xrange(r.count()-1, -1, -1): n = r.node(i) (p1, p2) = r.parents(n) (h, h1, h2) = map(hg.hex, (n, p1, p2)) (i1, i2) = map(r.rev, (p1, p2)) ci = r.linkrev(n) cn = self.repo.changelog.node(ci) cs = hg.hex(cn) changes = self.repo.changelog.read(cn) print '<tr><td>' self.hist_ent(i, h, i1, h1, i2, h2, ci, cs, changes) print '</tr></td>' print '</table>' def hist_ent(self, revi, revs, p1i, p1s, p2i, p2s, ci, cs, changes): datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0]))) print '<table summary="" width="100%" border="1">' print '\t<tr><td valign="top" width="10%">author:</td>' + \ '<td valign="top" width="20%%">%s</td>' % \ (obfuscate(changes[1]), ) print '\t\t<td valign="top" width="10%">description:</td>' + \ '<td width="60%">' + \ '<a href="?cmd=chkin;nd=%s">%s</a></td></tr>' % \ (cs, nl2br(cgi.escape(changes[4])), ) print '\t<tr><td>date:</td><td>%s UTC</td>' % (datestr, ) print '\t\t<td>revision:</td><td>%d:<a ' % (revi, ) + \ 'href="?cmd=file;cs=%s;fn=%s">%s</a></td></tr>' % \ (cs, self.fn, revs ) print '\t<tr><td>parent(s):</td><td colspan="3">%d:' % (p1i, ) print '<a href="?cmd=file;nd=%s;fn=%s">%s</a>' % (p1s, self.fn, p1s, ), if p2i != -1: print ' %d:<a href="?cmd=file;nd=%s;fn=%s">%s</a>' % \ (p2i, p2s, self.fn, p2s ), print '</td></tr>' print '</table><br />' args = cgi.parse() ui = hg.ui() repo = hg.repository(ui, repo_path) if not args.has_key('cmd') or args['cmd'][0] == 'changes': page = change_list(repo, 'Mercurial') hi = args.get('hi', ( repo.changelog.count(), )) page.content(hi = int(hi[0])) page.endpage() elif args['cmd'][0] == 'chkin': if not args.has_key('nd'): page = errpage() print '<div class="errmsg">No Node!</div>' else: page = checkin(repo, args['nd'][0]) page.content() page.endpage() elif args['cmd'][0] == 'file': if not (args.has_key('nd') and args.has_key('fn')) and \ not (args.has_key('cs') and args.has_key('fn')): page = errpage() print '<div class="errmsg">Invalid Args!</div>' else: if args.has_key('nd'): page = filepage(repo, args['fn'][0], node=args['nd'][0]) else: page = filepage(repo, args['fn'][0], cs=args['cs'][0]) page.content() page.endpage() elif args['cmd'][0] == 'mf': if not args.has_key('nd'): page = errpage() print '<div class="errmsg">No Node!</div>' else: page = mfpage(repo, args['nd'][0]) page.content() page.endpage() elif args['cmd'][0] == 'hist': if not args.has_key('fn'): page = errpage() print '<div class="errmsg">No Filename!</div>' else: page = histpage(repo, args['fn'][0]) page.content() page.endpage() elif args['cmd'][0] == 'branches': httphdr("text/plain") nodes = [] if args.has_key('nodes'): nodes = map(hg.bin, args['nodes'][0].split(" ")) for b in repo.branches(nodes): print " ".join(map(hg.hex, b)) elif args['cmd'][0] == 'between': httphdr("text/plain") nodes = [] if args.has_key('pairs'): pairs = [ map(hg.bin, p.split("-")) for p in args['pairs'][0].split(" ") ] for b in repo.between(pairs): print " ".join(map(hg.hex, b)) elif args['cmd'][0] == 'changegroup': httphdr("application/hg-changegroup") nodes = [] if args.has_key('roots'): nodes = map(hg.bin, args['roots'][0].split(" ")) z = zlib.compressobj() for chunk in repo.changegroup(nodes): sys.stdout.write(z.compress(chunk)) sys.stdout.write(z.flush()) else: page = errpage() print '<div class="errmsg">unknown command: %s</div>' % \ cgi.escape(args['cmd'][0]) page.endpage()