--- a/contrib/mercurial.el Sun Mar 30 23:58:02 2008 +0200
+++ b/contrib/mercurial.el Mon Mar 31 00:01:27 2008 +0200
@@ -35,8 +35,10 @@
;; This code has been developed under XEmacs 21.5, and may not work as
;; well under GNU Emacs (albeit tested under 21.4). Patches to
;; enhance the portability of this code, fix bugs, and add features
-;; are most welcome. You can clone a Mercurial repository for this
-;; package from http://www.serpentine.com/hg/hg-emacs
+;; are most welcome.
+
+;; As of version 22.3, GNU Emacs's VC mode has direct support for
+;; Mercurial, so this package may not prove as useful there.
;; Please send problem reports and suggestions to bos@serpentine.com.
--- a/hgext/highlight.py Sun Mar 30 23:58:02 2008 +0200
+++ b/hgext/highlight.py Mon Mar 31 00:01:27 2008 +0200
@@ -20,18 +20,13 @@
# pygmentize -f html -S <newstyle>
-
-- Adam Hupp <adam@hupp.org>
-
-
"""
from mercurial import demandimport
-demandimport.ignore.extend(['pkgutil',
- 'pkg_resources',
- '__main__',])
+demandimport.ignore.extend(['pkgutil', 'pkg_resources', '__main__',])
-from mercurial.hgweb.hgweb_mod import hgweb
+from mercurial.hgweb import webcommands, webutil
from mercurial import util
from mercurial.templatefilters import filters
@@ -43,7 +38,8 @@
SYNTAX_CSS = ('\n<link rel="stylesheet" href="#staticurl#highlight.css" '
'type="text/css" />')
-def pygmentize(self, tmpl, fctx, field):
+def pygmentize(field, fctx, style, tmpl):
+
# append a <link ...> to the syntax highlighting css
old_header = ''.join(tmpl('header'))
if SYNTAX_CSS not in old_header:
@@ -54,7 +50,6 @@
if util.binary(text):
return
- style = self.config("web", "pygments_style", "colorful")
# To get multi-line strings right, we can't format line-by-line
try:
lexer = guess_lexer_for_filename(fctx.path(), text,
@@ -79,20 +74,21 @@
newl = oldl.replace('line|escape', 'line|colorize')
tmpl.cache[field] = newl
-def filerevision_highlight(self, tmpl, fctx):
- pygmentize(self, tmpl, fctx, 'fileline')
-
- return realrevision(self, tmpl, fctx)
+web_filerevision = webcommands._filerevision
+web_annotate = webcommands.annotate
-def fileannotate_highlight(self, tmpl, fctx):
- pygmentize(self, tmpl, fctx, 'annotateline')
+def filerevision_highlight(web, tmpl, fctx):
+ style = web.config('web', 'pygments_style', 'colorful')
+ pygmentize('fileline', fctx, style, tmpl)
+ return web_filerevision(web, tmpl, fctx)
- return realannotate(self, tmpl, fctx)
+def annotate_highlight(web, req, tmpl):
+ fctx = webutil.filectx(web.repo, req)
+ style = web.config('web', 'pygments_style', 'colorful')
+ pygmentize('annotateline', fctx, style, tmpl)
+ return web_annotate(web, req, tmpl)
# monkeypatch in the new version
-# should be safer than overriding the method in a derived class
-# and then patching the class
-realrevision = hgweb.filerevision
-hgweb.filerevision = filerevision_highlight
-realannotate = hgweb.fileannotate
-hgweb.fileannotate = fileannotate_highlight
+
+webcommands._filerevision = filerevision_highlight
+webcommands.annotate = annotate_highlight
--- a/hgext/keyword.py Sun Mar 30 23:58:02 2008 +0200
+++ b/hgext/keyword.py Mon Mar 31 00:01:27 2008 +0200
@@ -128,15 +128,21 @@
_patch_diff(repo, node1=node1, node2=node2, files=files, match=match,
fp=fp, changes=changes, opts=opts)
+# monkeypatching hgweb functions changeset and filediff
+# actual monkeypatching is done at the bottom of reposetup()
+
+web_changeset = webcommands.changeset
+web_filediff = webcommands.filediff
+
def _kwweb_changeset(web, req, tmpl):
'''Wraps webcommands.changeset turning off keyword expansion.'''
kwtools['templater'].matcher = util.never
- return web.changeset(tmpl, web.changectx(req))
+ return web_changeset(web, req, tmpl)
def _kwweb_filediff(web, req, tmpl):
'''Wraps webcommands.filediff turning off keyword expansion.'''
kwtools['templater'].matcher = util.never
- return web.filediff(tmpl, web.filectx(req))
+ return web_filediff(web, req, tmpl)
def _kwdispatch_parse(ui, args):
'''Monkeypatch dispatch._parse to obtain running hg command.'''
--- a/mercurial/hgweb/hgweb_mod.py Sun Mar 30 23:58:02 2008 +0200
+++ b/mercurial/hgweb/hgweb_mod.py Mon Mar 31 00:01:27 2008 +0200
@@ -6,16 +6,15 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import os, mimetypes, re, mimetools, cStringIO
-from mercurial.node import hex, nullid, short
+import os, mimetypes
+from mercurial.node import hex, nullid
from mercurial.repo import RepoError
-from mercurial import mdiff, ui, hg, util, archival, patch, hook
+from mercurial import mdiff, ui, hg, util, patch, hook
from mercurial import revlog, templater, templatefilters, changegroup
-from common import get_mtime, style_map, paritygen, countgen, get_contact
-from common import ErrorResponse
+from common import get_mtime, style_map, paritygen, countgen, ErrorResponse
from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
from request import wsgirequest
-import webcommands, protocol
+import webcommands, protocol, webutil
shortcuts = {
'cl': [('cmd', ['changelog']), ('rev', None)],
@@ -32,54 +31,6 @@
'static': [('cmd', ['static']), ('file', None)]
}
-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 revnavgen(pos, pagelen, limit, nodefunc):
- def seq(factor, limit=None):
- if limit:
- yield limit
- if limit >= 20 and limit <= 40:
- yield 50
- else:
- yield 1 * factor
- yield 3 * factor
- for f in seq(factor * 10):
- yield f
-
- def nav(**map):
- l = []
- last = 0
- for f in seq(1, pagelen):
- if f < pagelen or f <= last:
- continue
- if f > limit:
- break
- last = f
- if pos + f < limit:
- l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
- if pos - f >= 0:
- l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
-
- try:
- yield {"label": "(0)", "node": hex(nodefunc('0').node())}
-
- for label, node in l:
- yield {"label": label, "node": node}
-
- yield {"label": "tip", "node": "tip"}
- except RepoError:
- pass
-
- return nav
-
class hgweb(object):
def __init__(self, repo, name=None):
if isinstance(repo, str):
@@ -226,17 +177,8 @@
try:
tmpl = self.templater(req)
- try:
- ctype = tmpl('mimetype', encoding=self.encoding)
- ctype = templater.stringify(ctype)
- except KeyError:
- # old templates with inline HTTP headers?
- if 'mimetype' in tmpl:
- raise
- header = tmpl('header', encoding=self.encoding)
- header_file = cStringIO.StringIO(templater.stringify(header))
- msg = mimetools.Message(header_file, 0)
- ctype = msg['content-type']
+ ctype = tmpl('mimetype', encoding=self.encoding)
+ ctype = templater.stringify(ctype)
if cmd == '':
req.form['cmd'] = [tmpl.cache['default']]
@@ -291,13 +233,7 @@
# some functions for the templater
def header(**map):
- header = tmpl('header', encoding=self.encoding, **map)
- if 'mimetype' not in tmpl:
- # old template with inline HTTP headers
- header_file = cStringIO.StringIO(templater.stringify(header))
- msg = mimetools.Message(header_file, 0)
- header = header_file.read()
- yield header
+ yield tmpl('header', encoding=self.encoding, **map)
def footer(**map):
yield tmpl("footer", **map)
@@ -355,54 +291,6 @@
if len(files) > self.maxfiles:
yield tmpl("fileellipses")
- def siblings(self, siblings=[], hiderev=None, **args):
- siblings = [s for s in siblings if s.node() != nullid]
- if len(siblings) == 1 and siblings[0].rev() == hiderev:
- return
- for s in siblings:
- d = {'node': hex(s.node()), 'rev': s.rev()}
- if hasattr(s, 'path'):
- d['file'] = s.path()
- d.update(args)
- yield d
-
- def renamelink(self, fl, node):
- r = fl.renamed(node)
- if r:
- return [dict(file=r[0], node=hex(r[1]))]
- return []
-
- def nodetagsdict(self, node):
- return [{"name": i} for i in self.repo.nodetags(node)]
-
- def nodebranchdict(self, ctx):
- branches = []
- branch = ctx.branch()
- # If this is an empty repo, ctx.node() == nullid,
- # ctx.branch() == 'default', but branchtags() is
- # an empty dict. Using dict.get avoids a traceback.
- if self.repo.branchtags().get(branch) == ctx.node():
- branches.append({"name": branch})
- return branches
-
- def nodeinbranch(self, ctx):
- branches = []
- branch = ctx.branch()
- if branch != 'default' and self.repo.branchtags().get(branch) != ctx.node():
- branches.append({"name": branch})
- return branches
-
- def nodebranchnodefault(self, ctx):
- branches = []
- branch = ctx.branch()
- if branch != 'default':
- branches.append({"name": branch})
- return branches
-
- def showtag(self, tmpl, t1, node=nullid, **args):
- for t in self.repo.nodetags(node):
- yield tmpl(t1, tag=t, **args)
-
def diff(self, tmpl, node1, node2, files):
def filterfiles(filters, files):
l = [x for x in files if x in filters]
@@ -470,514 +358,12 @@
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
opts=diffopts), f, tn)
- def changelog(self, tmpl, ctx, shortlog=False):
- def changelist(limit=0,**map):
- cl = self.repo.changelog
- l = [] # build a list in forward order for efficiency
- for i in xrange(start, end):
- ctx = self.repo.changectx(i)
- n = ctx.node()
- showtags = self.showtag(tmpl, 'changelogtag', n)
-
- l.insert(0, {"parity": parity.next(),
- "author": ctx.user(),
- "parent": self.siblings(ctx.parents(), i - 1),
- "child": self.siblings(ctx.children(), i + 1),
- "changelogtag": showtags,
- "desc": ctx.description(),
- "date": ctx.date(),
- "files": self.listfilediffs(tmpl, ctx.files(), n),
- "rev": i,
- "node": hex(n),
- "tags": self.nodetagsdict(n),
- "inbranch": self.nodeinbranch(ctx),
- "branches": self.nodebranchdict(ctx)})
-
- if limit > 0:
- l = l[:limit]
-
- for e in l:
- yield e
-
- maxchanges = shortlog and self.maxshortchanges or self.maxchanges
- cl = self.repo.changelog
- count = cl.count()
- pos = ctx.rev()
- start = max(0, pos - maxchanges + 1)
- end = min(count, start + maxchanges)
- pos = end - 1
- parity = paritygen(self.stripecount, offset=start-end)
-
- changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
-
- return tmpl(shortlog and 'shortlog' or 'changelog',
- changenav=changenav,
- node=hex(cl.tip()),
- rev=pos, changesets=count,
- entries=lambda **x: changelist(limit=0,**x),
- latestentry=lambda **x: changelist(limit=1,**x),
- archives=self.archivelist("tip"))
-
- def search(self, tmpl, query):
-
- def changelist(**map):
- cl = self.repo.changelog
- count = 0
- qw = query.lower().split()
-
- def revgen():
- for i in xrange(cl.count() - 1, 0, -100):
- l = []
- for j in xrange(max(0, i - 100), i + 1):
- ctx = self.repo.changectx(j)
- l.append(ctx)
- l.reverse()
- for e in l:
- yield e
-
- for ctx in revgen():
- miss = 0
- for q in qw:
- if not (q in ctx.user().lower() or
- q in ctx.description().lower() or
- q in " ".join(ctx.files()).lower()):
- miss = 1
- break
- if miss:
- continue
-
- count += 1
- n = ctx.node()
- showtags = self.showtag(tmpl, 'changelogtag', n)
-
- yield tmpl('searchentry',
- parity=parity.next(),
- author=ctx.user(),
- parent=self.siblings(ctx.parents()),
- child=self.siblings(ctx.children()),
- changelogtag=showtags,
- desc=ctx.description(),
- date=ctx.date(),
- files=self.listfilediffs(tmpl, ctx.files(), n),
- rev=ctx.rev(),
- node=hex(n),
- tags=self.nodetagsdict(n),
- inbranch=self.nodeinbranch(ctx),
- branches=self.nodebranchdict(ctx))
-
- if count >= self.maxchanges:
- break
-
- cl = self.repo.changelog
- parity = paritygen(self.stripecount)
-
- return tmpl('search',
- query=query,
- node=hex(cl.tip()),
- entries=changelist,
- archives=self.archivelist("tip"))
-
- def changeset(self, tmpl, ctx):
- n = ctx.node()
- showtags = self.showtag(tmpl, 'changesettag', n)
- parents = ctx.parents()
- p1 = parents[0].node()
-
- files = []
- parity = paritygen(self.stripecount)
- for f in ctx.files():
- files.append(tmpl("filenodelink",
- node=hex(n), file=f,
- parity=parity.next()))
-
- def diff(**map):
- yield self.diff(tmpl, p1, n, None)
-
- return tmpl('changeset',
- diff=diff,
- rev=ctx.rev(),
- node=hex(n),
- parent=self.siblings(parents),
- child=self.siblings(ctx.children()),
- changesettag=showtags,
- author=ctx.user(),
- desc=ctx.description(),
- date=ctx.date(),
- files=files,
- archives=self.archivelist(hex(n)),
- tags=self.nodetagsdict(n),
- branch=self.nodebranchnodefault(ctx),
- inbranch=self.nodeinbranch(ctx),
- branches=self.nodebranchdict(ctx))
-
- def filelog(self, tmpl, fctx):
- f = fctx.path()
- fl = fctx.filelog()
- count = fl.count()
- pagelen = self.maxshortchanges
- pos = fctx.filerev()
- start = max(0, pos - pagelen + 1)
- end = min(count, start + pagelen)
- pos = end - 1
- parity = paritygen(self.stripecount, offset=start-end)
-
- def entries(limit=0, **map):
- l = []
-
- for i in xrange(start, end):
- ctx = fctx.filectx(i)
- n = fl.node(i)
-
- l.insert(0, {"parity": parity.next(),
- "filerev": i,
- "file": f,
- "node": hex(ctx.node()),
- "author": ctx.user(),
- "date": ctx.date(),
- "rename": self.renamelink(fl, n),
- "parent": self.siblings(fctx.parents()),
- "child": self.siblings(fctx.children()),
- "desc": ctx.description()})
-
- if limit > 0:
- l = l[:limit]
-
- for e in l:
- yield e
-
- nodefunc = lambda x: fctx.filectx(fileid=x)
- nav = revnavgen(pos, pagelen, count, nodefunc)
- return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
- entries=lambda **x: entries(limit=0, **x),
- latestentry=lambda **x: entries(limit=1, **x))
-
- def filerevision(self, tmpl, fctx):
- f = fctx.path()
- text = fctx.data()
- fl = fctx.filelog()
- n = fctx.filenode()
- parity = paritygen(self.stripecount)
-
- if util.binary(text):
- mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
- text = '(binary:%s)' % mt
-
- def lines():
- for lineno, t in enumerate(text.splitlines(1)):
- yield {"line": t,
- "lineid": "l%d" % (lineno + 1),
- "linenumber": "% 6d" % (lineno + 1),
- "parity": parity.next()}
-
- return tmpl("filerevision",
- file=f,
- path=_up(f),
- text=lines(),
- rev=fctx.rev(),
- node=hex(fctx.node()),
- author=fctx.user(),
- date=fctx.date(),
- desc=fctx.description(),
- branch=self.nodebranchnodefault(fctx),
- parent=self.siblings(fctx.parents()),
- child=self.siblings(fctx.children()),
- rename=self.renamelink(fl, n),
- permissions=fctx.manifest().flags(f))
-
- def fileannotate(self, tmpl, fctx):
- f = fctx.path()
- n = fctx.filenode()
- fl = fctx.filelog()
- parity = paritygen(self.stripecount)
-
- def annotate(**map):
- last = None
- if util.binary(fctx.data()):
- mt = (mimetypes.guess_type(fctx.path())[0]
- or 'application/octet-stream')
- lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
- '(binary:%s)' % mt)])
- else:
- lines = enumerate(fctx.annotate(follow=True, linenumber=True))
- for lineno, ((f, targetline), l) in lines:
- fnode = f.filenode()
- name = self.repo.ui.shortuser(f.user())
-
- if last != fnode:
- last = fnode
-
- yield {"parity": parity.next(),
- "node": hex(f.node()),
- "rev": f.rev(),
- "author": name,
- "file": f.path(),
- "targetline": targetline,
- "line": l,
- "lineid": "l%d" % (lineno + 1),
- "linenumber": "% 6d" % (lineno + 1)}
-
- return tmpl("fileannotate",
- file=f,
- annotate=annotate,
- path=_up(f),
- rev=fctx.rev(),
- node=hex(fctx.node()),
- author=fctx.user(),
- date=fctx.date(),
- desc=fctx.description(),
- rename=self.renamelink(fl, n),
- branch=self.nodebranchnodefault(fctx),
- parent=self.siblings(fctx.parents()),
- child=self.siblings(fctx.children()),
- permissions=fctx.manifest().flags(f))
-
- def manifest(self, tmpl, ctx, path):
- mf = ctx.manifest()
- node = ctx.node()
-
- files = {}
- parity = paritygen(self.stripecount)
-
- if path and path[-1] != "/":
- path += "/"
- l = len(path)
- abspath = "/" + path
-
- for f, n in mf.items():
- if f[:l] != path:
- continue
- remain = f[l:]
- if "/" in remain:
- short = remain[:remain.index("/") + 1] # bleah
- files[short] = (f, None)
- else:
- short = os.path.basename(remain)
- files[short] = (f, n)
-
- if not files:
- raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
-
- def filelist(**map):
- fl = files.keys()
- fl.sort()
- for f in fl:
- full, fnode = files[f]
- if not fnode:
- continue
-
- fctx = ctx.filectx(full)
- yield {"file": full,
- "parity": parity.next(),
- "basename": f,
- "date": fctx.changectx().date(),
- "size": fctx.size(),
- "permissions": mf.flags(full)}
-
- def dirlist(**map):
- fl = files.keys()
- fl.sort()
- for f in fl:
- full, fnode = files[f]
- if fnode:
- continue
-
- yield {"parity": parity.next(),
- "path": "%s%s" % (abspath, f),
- "basename": f[:-1]}
-
- return tmpl("manifest",
- rev=ctx.rev(),
- node=hex(node),
- path=abspath,
- up=_up(abspath),
- upparity=parity.next(),
- fentries=filelist,
- dentries=dirlist,
- archives=self.archivelist(hex(node)),
- tags=self.nodetagsdict(node),
- inbranch=self.nodeinbranch(ctx),
- branches=self.nodebranchdict(ctx))
-
- def tags(self, tmpl):
- i = self.repo.tagslist()
- i.reverse()
- parity = paritygen(self.stripecount)
-
- def entries(notip=False,limit=0, **map):
- count = 0
- for k, n in i:
- if notip and k == "tip":
- continue
- if limit > 0 and count >= limit:
- continue
- count = count + 1
- yield {"parity": parity.next(),
- "tag": k,
- "date": self.repo.changectx(n).date(),
- "node": hex(n)}
-
- return tmpl("tags",
- node=hex(self.repo.changelog.tip()),
- entries=lambda **x: entries(False,0, **x),
- entriesnotip=lambda **x: entries(True,0, **x),
- latestentry=lambda **x: entries(True,1, **x))
-
- def summary(self, tmpl):
- i = self.repo.tagslist()
- i.reverse()
-
- def tagentries(**map):
- parity = paritygen(self.stripecount)
- count = 0
- for k, n in i:
- if k == "tip": # skip tip
- continue;
-
- count += 1
- if count > 10: # limit to 10 tags
- break;
-
- yield tmpl("tagentry",
- parity=parity.next(),
- tag=k,
- node=hex(n),
- date=self.repo.changectx(n).date())
-
-
- def branches(**map):
- parity = paritygen(self.stripecount)
-
- b = self.repo.branchtags()
- l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
- l.sort()
-
- for r,n,t in l:
- ctx = self.repo.changectx(n)
-
- yield {'parity': parity.next(),
- 'branch': t,
- 'node': hex(n),
- 'date': ctx.date()}
-
- def changelist(**map):
- parity = paritygen(self.stripecount, offset=start-end)
- l = [] # build a list in forward order for efficiency
- for i in xrange(start, end):
- ctx = self.repo.changectx(i)
- n = ctx.node()
- hn = hex(n)
-
- l.insert(0, tmpl(
- 'shortlogentry',
- parity=parity.next(),
- author=ctx.user(),
- desc=ctx.description(),
- date=ctx.date(),
- rev=i,
- node=hn,
- tags=self.nodetagsdict(n),
- inbranch=self.nodeinbranch(ctx),
- branches=self.nodebranchdict(ctx)))
-
- yield l
-
- cl = self.repo.changelog
- count = cl.count()
- start = max(0, count - self.maxchanges)
- end = min(count, start + self.maxchanges)
-
- return tmpl("summary",
- desc=self.config("web", "description", "unknown"),
- owner=get_contact(self.config) or "unknown",
- lastchange=cl.read(cl.tip())[2],
- tags=tagentries,
- branches=branches,
- shortlog=changelist,
- node=hex(cl.tip()),
- archives=self.archivelist("tip"))
-
- def filediff(self, tmpl, fctx):
- n = fctx.node()
- path = fctx.path()
- parents = fctx.parents()
- p1 = parents and parents[0].node() or nullid
-
- def diff(**map):
- yield self.diff(tmpl, p1, n, [path])
-
- return tmpl("filediff",
- file=path,
- node=hex(n),
- rev=fctx.rev(),
- branch=self.nodebranchnodefault(fctx),
- parent=self.siblings(parents),
- child=self.siblings(fctx.children()),
- diff=diff)
-
archive_specs = {
'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
'zip': ('application/zip', 'zip', '.zip', None),
}
- def archive(self, tmpl, req, key, type_):
- reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
- cnode = self.repo.lookup(key)
- arch_version = key
- if cnode == key or key == 'tip':
- arch_version = short(cnode)
- name = "%s-%s" % (reponame, arch_version)
- mimetype, artype, extension, encoding = self.archive_specs[type_]
- headers = [
- ('Content-Type', mimetype),
- ('Content-Disposition', 'attachment; filename=%s%s' %
- (name, extension))
- ]
- if encoding:
- headers.append(('Content-Encoding', encoding))
- req.header(headers)
- req.respond(HTTP_OK)
- archival.archive(self.repo, req, cnode, artype, prefix=name)
-
- # add tags to things
- # tags -> list of changesets corresponding to tags
- # find tag, changeset, file
-
- def cleanpath(self, path):
- path = path.lstrip('/')
- return util.canonpath(self.repo.root, '', path)
-
- def changectx(self, req):
- if 'node' in req.form:
- changeid = req.form['node'][0]
- elif 'manifest' in req.form:
- changeid = req.form['manifest'][0]
- else:
- changeid = self.repo.changelog.count() - 1
-
- try:
- ctx = self.repo.changectx(changeid)
- except RepoError:
- man = self.repo.manifest
- mn = man.lookup(changeid)
- ctx = self.repo.changectx(man.linkrev(mn))
-
- return ctx
-
- def filectx(self, req):
- path = self.cleanpath(req.form['file'][0])
- if 'node' in req.form:
- changeid = req.form['node'][0]
- else:
- changeid = req.form['filenode'][0]
- try:
- ctx = self.repo.changectx(changeid)
- fctx = ctx.filectx(path)
- except RepoError:
- fctx = self.repo.filectx(path, fileid=changeid)
-
- return fctx
-
def check_perm(self, req, op, default):
'''check permission for operation based on user auth.
return true if op allowed, else false.
--- a/mercurial/hgweb/hgwebdir_mod.py Sun Mar 30 23:58:02 2008 +0200
+++ b/mercurial/hgweb/hgwebdir_mod.py Mon Mar 31 00:01:27 2008 +0200
@@ -6,7 +6,7 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import os, mimetools, cStringIO
+import os
from mercurial.i18n import gettext as _
from mercurial.repo import RepoError
from mercurial import ui, hg, util, templater, templatefilters
@@ -81,17 +81,8 @@
virtual = req.env.get("PATH_INFO", "").strip('/')
tmpl = self.templater(req)
- try:
- ctype = tmpl('mimetype', encoding=util._encoding)
- ctype = templater.stringify(ctype)
- except KeyError:
- # old templates with inline HTTP headers?
- if 'mimetype' in tmpl:
- raise
- header = tmpl('header', encoding=util._encoding)
- header_file = cStringIO.StringIO(templater.stringify(header))
- msg = mimetools.Message(header_file, 0)
- ctype = msg['content-type']
+ ctype = tmpl('mimetype', encoding=util._encoding)
+ ctype = templater.stringify(ctype)
# a static file
if virtual.startswith('static/') or 'static' in req.form:
@@ -255,13 +246,7 @@
def templater(self, req):
def header(**map):
- header = tmpl('header', encoding=util._encoding, **map)
- if 'mimetype' not in tmpl:
- # old template with inline HTTP headers
- header_file = cStringIO.StringIO(templater.stringify(header))
- msg = mimetools.Message(header_file, 0)
- header = header_file.read()
- yield header
+ yield tmpl('header', encoding=util._encoding, **map)
def footer(**map):
yield tmpl("footer", **map)
--- a/mercurial/hgweb/server.py Sun Mar 30 23:58:02 2008 +0200
+++ b/mercurial/hgweb/server.py Mon Mar 31 00:01:27 2008 +0200
@@ -268,12 +268,7 @@
self.addr, self.port = self.socket.getsockname()[0:2]
self.prefix = prefix
-
self.fqaddr = socket.getfqdn(address)
- try:
- socket.getaddrbyhost(self.fqaddr)
- except:
- fqaddr = address
class IPv6HTTPServer(MercurialHTTPServer):
address_family = getattr(socket, 'AF_INET6', None)
--- a/mercurial/hgweb/webcommands.py Sun Mar 30 23:58:02 2008 +0200
+++ b/mercurial/hgweb/webcommands.py Mon Mar 31 00:01:27 2008 +0200
@@ -5,10 +5,14 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import os, mimetypes
-from mercurial import revlog, util
+import os, mimetypes, re
+import webutil
+from mercurial import revlog, archival
+from mercurial.node import short, hex, nullid
+from mercurial.util import binary
from mercurial.repo import RepoError
-from common import staticfile, ErrorResponse, HTTP_OK, HTTP_NOT_FOUND
+from common import paritygen, staticfile, get_contact, ErrorResponse
+from common import HTTP_OK, HTTP_NOT_FOUND
# __all__ is populated with the allowed commands. Be sure to add to it if
# you're adding a new command, or the new command won't work.
@@ -26,17 +30,17 @@
return changelog(web, req, tmpl)
def rawfile(web, req, tmpl):
- path = web.cleanpath(req.form.get('file', [''])[0])
+ path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
if not path:
- content = web.manifest(tmpl, web.changectx(req), path)
+ content = manifest(web, req, tmpl)
req.respond(HTTP_OK, web.ctype)
return content
try:
- fctx = web.filectx(req)
+ fctx = webutil.filectx(web.repo, req)
except revlog.LookupError, inst:
try:
- content = web.manifest(tmpl, web.changectx(req), path)
+ content = manifest(web, req, tmpl)
req.respond(HTTP_OK, web.ctype)
return content
except ErrorResponse:
@@ -45,28 +49,120 @@
path = fctx.path()
text = fctx.data()
mt = mimetypes.guess_type(path)[0]
- if mt is None or util.binary(text):
+ if mt is None or binary(text):
mt = mt or 'application/octet-stream'
req.respond(HTTP_OK, mt, path, len(text))
return [text]
+def _filerevision(web, tmpl, fctx):
+ f = fctx.path()
+ text = fctx.data()
+ fl = fctx.filelog()
+ n = fctx.filenode()
+ parity = paritygen(web.stripecount)
+
+ if binary(text):
+ mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
+ text = '(binary:%s)' % mt
+
+ def lines():
+ for lineno, t in enumerate(text.splitlines(1)):
+ yield {"line": t,
+ "lineid": "l%d" % (lineno + 1),
+ "linenumber": "% 6d" % (lineno + 1),
+ "parity": parity.next()}
+
+ return tmpl("filerevision",
+ file=f,
+ path=webutil.up(f),
+ text=lines(),
+ rev=fctx.rev(),
+ node=hex(fctx.node()),
+ author=fctx.user(),
+ date=fctx.date(),
+ desc=fctx.description(),
+ branch=webutil.nodebranchnodefault(fctx),
+ parent=webutil.siblings(fctx.parents()),
+ child=webutil.siblings(fctx.children()),
+ rename=webutil.renamelink(fl, n),
+ permissions=fctx.manifest().flags(f))
+
def file(web, req, tmpl):
- path = web.cleanpath(req.form.get('file', [''])[0])
+ path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
if path:
try:
- return web.filerevision(tmpl, web.filectx(req))
+ return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
except revlog.LookupError, inst:
pass
try:
- return web.manifest(tmpl, web.changectx(req), path)
+ return manifest(web, req, tmpl)
except ErrorResponse:
raise inst
+def _search(web, tmpl, query):
+
+ def changelist(**map):
+ cl = web.repo.changelog
+ count = 0
+ qw = query.lower().split()
+
+ def revgen():
+ for i in xrange(cl.count() - 1, 0, -100):
+ l = []
+ for j in xrange(max(0, i - 100), i + 1):
+ ctx = web.repo.changectx(j)
+ l.append(ctx)
+ l.reverse()
+ for e in l:
+ yield e
+
+ for ctx in revgen():
+ miss = 0
+ for q in qw:
+ if not (q in ctx.user().lower() or
+ q in ctx.description().lower() or
+ q in " ".join(ctx.files()).lower()):
+ miss = 1
+ break
+ if miss:
+ continue
+
+ count = 1
+ n = ctx.node()
+ showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
+
+ yield tmpl('searchentry',
+ parity=parity.next(),
+ author=ctx.user(),
+ parent=webutil.siblings(ctx.parents()),
+ child=webutil.siblings(ctx.children()),
+ changelogtag=showtags,
+ desc=ctx.description(),
+ date=ctx.date(),
+ files=web.listfilediffs(tmpl, ctx.files(), n),
+ rev=ctx.rev(),
+ node=hex(n),
+ tags=webutil.nodetagsdict(web.repo, n),
+ inbranch=webutil.nodeinbranch(web.repo, ctx),
+ branches=webutil.nodebranchdict(web.repo, ctx))
+
+ if count >= web.maxchanges:
+ break
+
+ cl = web.repo.changelog
+ parity = paritygen(web.stripecount)
+
+ return tmpl('search',
+ query=query,
+ node=hex(cl.tip()),
+ entries=changelist,
+ archives=web.archivelist("tip"))
+
def changelog(web, req, tmpl, shortlog = False):
if 'node' in req.form:
- ctx = web.changectx(req)
+ ctx = webutil.changectx(web.repo, req)
else:
if 'rev' in req.form:
hi = req.form['rev'][0]
@@ -75,47 +171,396 @@
try:
ctx = web.repo.changectx(hi)
except RepoError:
- return web.search(tmpl, hi) # XXX redirect to 404 page?
+ return _search(web, tmpl, hi) # XXX redirect to 404 page?
+
+ def changelist(limit=0, **map):
+ cl = web.repo.changelog
+ l = [] # build a list in forward order for efficiency
+ for i in xrange(start, end):
+ ctx = web.repo.changectx(i)
+ n = ctx.node()
+ showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
+
+ l.insert(0, {"parity": parity.next(),
+ "author": ctx.user(),
+ "parent": webutil.siblings(ctx.parents(), i - 1),
+ "child": webutil.siblings(ctx.children(), i + 1),
+ "changelogtag": showtags,
+ "desc": ctx.description(),
+ "date": ctx.date(),
+ "files": web.listfilediffs(tmpl, ctx.files(), n),
+ "rev": i,
+ "node": hex(n),
+ "tags": webutil.nodetagsdict(web.repo, n),
+ "inbranch": webutil.nodeinbranch(web.repo, ctx),
+ "branches": webutil.nodebranchdict(web.repo, ctx)
+ })
- return web.changelog(tmpl, ctx, shortlog = shortlog)
+ if limit > 0:
+ l = l[:limit]
+
+ for e in l:
+ yield e
+
+ maxchanges = shortlog and web.maxshortchanges or web.maxchanges
+ cl = web.repo.changelog
+ count = cl.count()
+ pos = ctx.rev()
+ start = max(0, pos - maxchanges + 1)
+ end = min(count, start + maxchanges)
+ pos = end - 1
+ parity = paritygen(web.stripecount, offset=start-end)
+
+ changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx)
+
+ return tmpl(shortlog and 'shortlog' or 'changelog',
+ changenav=changenav,
+ node=hex(cl.tip()),
+ rev=pos, changesets=count,
+ entries=lambda **x: changelist(limit=0,**x),
+ latestentry=lambda **x: changelist(limit=1,**x),
+ archives=web.archivelist("tip"))
def shortlog(web, req, tmpl):
return changelog(web, req, tmpl, shortlog = True)
def changeset(web, req, tmpl):
- return web.changeset(tmpl, web.changectx(req))
+ ctx = webutil.changectx(web.repo, req)
+ n = ctx.node()
+ showtags = webutil.showtag(web.repo, tmpl, 'changesettag', n)
+ parents = ctx.parents()
+ p1 = parents[0].node()
+
+ files = []
+ parity = paritygen(web.stripecount)
+ for f in ctx.files():
+ files.append(tmpl("filenodelink",
+ node=hex(n), file=f,
+ parity=parity.next()))
+
+ diffs = web.diff(tmpl, p1, n, None)
+ return tmpl('changeset',
+ diff=diffs,
+ rev=ctx.rev(),
+ node=hex(n),
+ parent=webutil.siblings(parents),
+ child=webutil.siblings(ctx.children()),
+ changesettag=showtags,
+ author=ctx.user(),
+ desc=ctx.description(),
+ date=ctx.date(),
+ files=files,
+ archives=web.archivelist(hex(n)),
+ tags=webutil.nodetagsdict(web.repo, n),
+ branch=webutil.nodebranchnodefault(ctx),
+ inbranch=webutil.nodeinbranch(web.repo, ctx),
+ branches=webutil.nodebranchdict(web.repo, ctx))
rev = changeset
def manifest(web, req, tmpl):
- return web.manifest(tmpl, web.changectx(req),
- web.cleanpath(req.form['path'][0]))
+ ctx = webutil.changectx(web.repo, req)
+ path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
+ mf = ctx.manifest()
+ node = ctx.node()
+
+ files = {}
+ parity = paritygen(web.stripecount)
+
+ if path and path[-1] != "/":
+ path += "/"
+ l = len(path)
+ abspath = "/" + path
+
+ for f, n in mf.items():
+ if f[:l] != path:
+ continue
+ remain = f[l:]
+ if "/" in remain:
+ short = remain[:remain.index("/") + 1] # bleah
+ files[short] = (f, None)
+ else:
+ short = os.path.basename(remain)
+ files[short] = (f, n)
+
+ if not files:
+ raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
+
+ def filelist(**map):
+ fl = files.keys()
+ fl.sort()
+ for f in fl:
+ full, fnode = files[f]
+ if not fnode:
+ continue
+
+ fctx = ctx.filectx(full)
+ yield {"file": full,
+ "parity": parity.next(),
+ "basename": f,
+ "date": fctx.changectx().date(),
+ "size": fctx.size(),
+ "permissions": mf.flags(full)}
+
+ def dirlist(**map):
+ fl = files.keys()
+ fl.sort()
+ for f in fl:
+ full, fnode = files[f]
+ if fnode:
+ continue
+
+ yield {"parity": parity.next(),
+ "path": "%s%s" % (abspath, f),
+ "basename": f[:-1]}
+
+ return tmpl("manifest",
+ rev=ctx.rev(),
+ node=hex(node),
+ path=abspath,
+ up=webutil.up(abspath),
+ upparity=parity.next(),
+ fentries=filelist,
+ dentries=dirlist,
+ archives=web.archivelist(hex(node)),
+ tags=webutil.nodetagsdict(web.repo, node),
+ inbranch=webutil.nodeinbranch(web.repo, ctx),
+ branches=webutil.nodebranchdict(web.repo, ctx))
def tags(web, req, tmpl):
- return web.tags(tmpl)
+ i = web.repo.tagslist()
+ i.reverse()
+ parity = paritygen(web.stripecount)
+
+ def entries(notip=False,limit=0, **map):
+ count = 0
+ for k, n in i:
+ if notip and k == "tip":
+ continue
+ if limit > 0 and count >= limit:
+ continue
+ count = count + 1
+ yield {"parity": parity.next(),
+ "tag": k,
+ "date": web.repo.changectx(n).date(),
+ "node": hex(n)}
+
+ return tmpl("tags",
+ node=hex(web.repo.changelog.tip()),
+ entries=lambda **x: entries(False,0, **x),
+ entriesnotip=lambda **x: entries(True,0, **x),
+ latestentry=lambda **x: entries(True,1, **x))
def summary(web, req, tmpl):
- return web.summary(tmpl)
+ i = web.repo.tagslist()
+ i.reverse()
+
+ def tagentries(**map):
+ parity = paritygen(web.stripecount)
+ count = 0
+ for k, n in i:
+ if k == "tip": # skip tip
+ continue
+
+ count = 1
+ if count > 10: # limit to 10 tags
+ break
+
+ yield tmpl("tagentry",
+ parity=parity.next(),
+ tag=k,
+ node=hex(n),
+ date=web.repo.changectx(n).date())
+
+ def branches(**map):
+ parity = paritygen(web.stripecount)
+
+ b = web.repo.branchtags()
+ l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
+ l.sort()
+
+ for r,n,t in l:
+ ctx = web.repo.changectx(n)
+ yield {'parity': parity.next(),
+ 'branch': t,
+ 'node': hex(n),
+ 'date': ctx.date()}
+
+ def changelist(**map):
+ parity = paritygen(web.stripecount, offset=start-end)
+ l = [] # build a list in forward order for efficiency
+ for i in xrange(start, end):
+ ctx = web.repo.changectx(i)
+ n = ctx.node()
+ hn = hex(n)
+
+ l.insert(0, tmpl(
+ 'shortlogentry',
+ parity=parity.next(),
+ author=ctx.user(),
+ desc=ctx.description(),
+ date=ctx.date(),
+ rev=i,
+ node=hn,
+ tags=webutil.nodetagsdict(web.repo, n),
+ inbranch=webutil.nodeinbranch(web.repo, ctx),
+ branches=webutil.nodebranchdict(web.repo, ctx)))
+
+ yield l
+
+ cl = web.repo.changelog
+ count = cl.count()
+ start = max(0, count - web.maxchanges)
+ end = min(count, start + web.maxchanges)
+
+ return tmpl("summary",
+ desc=web.config("web", "description", "unknown"),
+ owner=get_contact(web.config) or "unknown",
+ lastchange=cl.read(cl.tip())[2],
+ tags=tagentries,
+ branches=branches,
+ shortlog=changelist,
+ node=hex(cl.tip()),
+ archives=web.archivelist("tip"))
def filediff(web, req, tmpl):
- return web.filediff(tmpl, web.filectx(req))
+ fctx = webutil.filectx(web.repo, req)
+ n = fctx.node()
+ path = fctx.path()
+ parents = fctx.parents()
+ p1 = parents and parents[0].node() or nullid
+
+ diffs = web.diff(tmpl, p1, n, [path])
+ return tmpl("filediff",
+ file=path,
+ node=hex(n),
+ rev=fctx.rev(),
+ branch=webutil.nodebranchnodefault(fctx),
+ parent=webutil.siblings(parents),
+ child=webutil.siblings(fctx.children()),
+ diff=diffs)
diff = filediff
def annotate(web, req, tmpl):
- return web.fileannotate(tmpl, web.filectx(req))
+ fctx = webutil.filectx(web.repo, req)
+ f = fctx.path()
+ n = fctx.filenode()
+ fl = fctx.filelog()
+ parity = paritygen(web.stripecount)
+
+ def annotate(**map):
+ last = None
+ if binary(fctx.data()):
+ mt = (mimetypes.guess_type(fctx.path())[0]
+ or 'application/octet-stream')
+ lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
+ '(binary:%s)' % mt)])
+ else:
+ lines = enumerate(fctx.annotate(follow=True, linenumber=True))
+ for lineno, ((f, targetline), l) in lines:
+ fnode = f.filenode()
+ name = web.repo.ui.shortuser(f.user())
+
+ if last != fnode:
+ last = fnode
+
+ yield {"parity": parity.next(),
+ "node": hex(f.node()),
+ "rev": f.rev(),
+ "author": name,
+ "file": f.path(),
+ "targetline": targetline,
+ "line": l,
+ "lineid": "l%d" % (lineno + 1),
+ "linenumber": "% 6d" % (lineno + 1)}
+
+ return tmpl("fileannotate",
+ file=f,
+ annotate=annotate,
+ path=webutil.up(f),
+ rev=fctx.rev(),
+ node=hex(fctx.node()),
+ author=fctx.user(),
+ date=fctx.date(),
+ desc=fctx.description(),
+ rename=webutil.renamelink(fl, n),
+ branch=webutil.nodebranchnodefault(fctx),
+ parent=webutil.siblings(fctx.parents()),
+ child=webutil.siblings(fctx.children()),
+ permissions=fctx.manifest().flags(f))
def filelog(web, req, tmpl):
- return web.filelog(tmpl, web.filectx(req))
+ fctx = webutil.filectx(web.repo, req)
+ f = fctx.path()
+ fl = fctx.filelog()
+ count = fl.count()
+ pagelen = web.maxshortchanges
+ pos = fctx.filerev()
+ start = max(0, pos - pagelen + 1)
+ end = min(count, start + pagelen)
+ pos = end - 1
+ parity = paritygen(web.stripecount, offset=start-end)
+
+ def entries(limit=0, **map):
+ l = []
+
+ for i in xrange(start, end):
+ ctx = fctx.filectx(i)
+ n = fl.node(i)
+
+ l.insert(0, {"parity": parity.next(),
+ "filerev": i,
+ "file": f,
+ "node": hex(ctx.node()),
+ "author": ctx.user(),
+ "date": ctx.date(),
+ "rename": webutil.renamelink(fl, n),
+ "parent": webutil.siblings(fctx.parents()),
+ "child": webutil.siblings(fctx.children()),
+ "desc": ctx.description()})
+
+ if limit > 0:
+ l = l[:limit]
+
+ for e in l:
+ yield e
+
+ nodefunc = lambda x: fctx.filectx(fileid=x)
+ nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
+ return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
+ entries=lambda **x: entries(limit=0, **x),
+ latestentry=lambda **x: entries(limit=1, **x))
+
def archive(web, req, tmpl):
type_ = req.form['type'][0]
allowed = web.configlist("web", "allow_archive")
- if (type_ in web.archives and (type_ in allowed or
+ key = req.form['node'][0]
+
+ if not (type_ in web.archives and (type_ in allowed or
web.configbool("web", "allow" + type_, False))):
- web.archive(tmpl, req, req.form['node'][0], type_)
- return []
- raise ErrorResponse(HTTP_NOT_FOUND, 'unsupported archive type: %s' % type_)
+ msg = 'Unsupported archive type: %s' % type_
+ raise ErrorResponse(HTTP_NOT_FOUND, msg)
+
+ reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
+ cnode = web.repo.lookup(key)
+ arch_version = key
+ if cnode == key or key == 'tip':
+ arch_version = short(cnode)
+ name = "%s-%s" % (reponame, arch_version)
+ mimetype, artype, extension, encoding = web.archive_specs[type_]
+ headers = [
+ ('Content-Type', mimetype),
+ ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
+ ]
+ if encoding:
+ headers.append(('Content-Encoding', encoding))
+ req.header(headers)
+ req.respond(HTTP_OK)
+ archival.archive(web.repo, req, cnode, artype, prefix=name)
+ return []
+
def static(web, req, tmpl):
fname = req.form['file'][0]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/hgweb/webutil.py Mon Mar 31 00:01:27 2008 +0200
@@ -0,0 +1,143 @@
+# hgweb/webutil.py - utility library for the web interface.
+#
+# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
+# Copyright 2005-2007 Matt Mackall <mpm@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
+from mercurial.node import hex, nullid
+from mercurial.repo import RepoError
+from mercurial import util
+
+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 revnavgen(pos, pagelen, limit, nodefunc):
+ def seq(factor, limit=None):
+ if limit:
+ yield limit
+ if limit >= 20 and limit <= 40:
+ yield 50
+ else:
+ yield 1 * factor
+ yield 3 * factor
+ for f in seq(factor * 10):
+ yield f
+
+ def nav(**map):
+ l = []
+ last = 0
+ for f in seq(1, pagelen):
+ if f < pagelen or f <= last:
+ continue
+ if f > limit:
+ break
+ last = f
+ if pos + f < limit:
+ l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
+ if pos - f >= 0:
+ l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
+
+ try:
+ yield {"label": "(0)", "node": hex(nodefunc('0').node())}
+
+ for label, node in l:
+ yield {"label": label, "node": node}
+
+ yield {"label": "tip", "node": "tip"}
+ except RepoError:
+ pass
+
+ return nav
+
+def siblings(siblings=[], hiderev=None, **args):
+ siblings = [s for s in siblings if s.node() != nullid]
+ if len(siblings) == 1 and siblings[0].rev() == hiderev:
+ return
+ for s in siblings:
+ d = {'node': hex(s.node()), 'rev': s.rev()}
+ if hasattr(s, 'path'):
+ d['file'] = s.path()
+ d.update(args)
+ yield d
+
+def renamelink(fl, node):
+ r = fl.renamed(node)
+ if r:
+ return [dict(file=r[0], node=hex(r[1]))]
+ return []
+
+def nodetagsdict(repo, node):
+ return [{"name": i} for i in repo.nodetags(node)]
+
+def nodebranchdict(repo, ctx):
+ branches = []
+ branch = ctx.branch()
+ # If this is an empty repo, ctx.node() == nullid,
+ # ctx.branch() == 'default', but branchtags() is
+ # an empty dict. Using dict.get avoids a traceback.
+ if repo.branchtags().get(branch) == ctx.node():
+ branches.append({"name": branch})
+ return branches
+
+def nodeinbranch(repo, ctx):
+ branches = []
+ branch = ctx.branch()
+ if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
+ branches.append({"name": branch})
+ return branches
+
+def nodebranchnodefault(ctx):
+ branches = []
+ branch = ctx.branch()
+ if branch != 'default':
+ branches.append({"name": branch})
+ return branches
+
+def showtag(repo, tmpl, t1, node=nullid, **args):
+ for t in repo.nodetags(node):
+ yield tmpl(t1, tag=t, **args)
+
+def cleanpath(repo, path):
+ path = path.lstrip('/')
+ return util.canonpath(repo.root, '', path)
+
+def changectx(repo, req):
+ if 'node' in req.form:
+ changeid = req.form['node'][0]
+ elif 'manifest' in req.form:
+ changeid = req.form['manifest'][0]
+ else:
+ changeid = repo.changelog.count() - 1
+
+ try:
+ ctx = repo.changectx(changeid)
+ except RepoError:
+ man = repo.manifest
+ mn = man.lookup(changeid)
+ ctx = repo.changectx(man.linkrev(mn))
+
+ return ctx
+
+def filectx(repo, req):
+ path = cleanpath(repo, req.form['file'][0])
+ if 'node' in req.form:
+ changeid = req.form['node'][0]
+ else:
+ changeid = req.form['filenode'][0]
+ try:
+ ctx = repo.changectx(changeid)
+ fctx = ctx.filectx(path)
+ except RepoError:
+ fctx = repo.filectx(path, fileid=changeid)
+
+ return fctx
--- a/mercurial/manifest.py Sun Mar 30 23:58:02 2008 +0200
+++ b/mercurial/manifest.py Mon Mar 31 00:01:27 2008 +0200
@@ -8,7 +8,7 @@
from node import bin, hex, nullid
from revlog import revlog, RevlogError
from i18n import _
-import array, struct, mdiff
+import array, struct, mdiff, parsers
class manifestdict(dict):
def __init__(self, mapping=None, flags=None):
@@ -39,14 +39,7 @@
def parse(self, lines):
mfdict = manifestdict()
- fdict = mfdict._flags
- for l in lines.splitlines():
- f, n = l.split('\0')
- if len(n) > 40:
- fdict[f] = n[40:]
- mfdict[f] = bin(n[:40])
- else:
- mfdict[f] = bin(n)
+ parsers.parse_manifest(mfdict, mfdict._flags, lines)
return mfdict
def readdelta(self, node):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/parsers.c Mon Mar 31 00:01:27 2008 +0200
@@ -0,0 +1,169 @@
+/*
+ parsers.c - efficient content parsing
+
+ Copyright 2008 Matt Mackall <mpm@selenic.com> and others
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License, incorporated herein by reference.
+*/
+
+#include <Python.h>
+#include <ctype.h>
+#include <string.h>
+
+static int hexdigit(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ return -1;
+}
+
+/*
+ * Turn a hex-encoded string into binary.
+ */
+static PyObject *unhexlify(const char *str, int len)
+{
+ PyObject *ret = NULL;
+ const char *c;
+ char *d;
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_ValueError,
+ "input is not even in length");
+ goto bail;
+ }
+
+ ret = PyString_FromStringAndSize(NULL, len / 2);
+ if (!ret)
+ goto bail;
+
+ d = PyString_AsString(ret);
+ if (!d)
+ goto bail;
+
+ for (c = str; c < str + len;) {
+ int hi = hexdigit(*c++);
+ int lo = hexdigit(*c++);
+
+ if (hi == -1 || lo == -1) {
+ PyErr_SetString(PyExc_ValueError,
+ "input contains non-hex character");
+ goto bail;
+ }
+
+ *d++ = (hi << 4) | lo;
+ }
+
+ goto done;
+
+bail:
+ Py_XDECREF(ret);
+ ret = NULL;
+done:
+ return ret;
+}
+
+/*
+ * This code assumes that a manifest is stitched together with newline
+ * ('\n') characters.
+ */
+static PyObject *parse_manifest(PyObject *self, PyObject *args)
+{
+ PyObject *mfdict, *fdict;
+ char *str, *cur, *start, *zero;
+ int len;
+
+ if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
+ &PyDict_Type, &mfdict,
+ &PyDict_Type, &fdict,
+ &str, &len))
+ goto quit;
+
+ for (start = cur = str, zero = NULL; cur < str + len; cur++) {
+ PyObject *file = NULL, *node = NULL;
+ PyObject *flags = NULL;
+ int nlen;
+
+ if (!*cur) {
+ zero = cur;
+ continue;
+ }
+ else if (*cur != '\n')
+ continue;
+
+ if (!zero) {
+ PyErr_SetString(PyExc_ValueError,
+ "manifest entry has no separator");
+ goto quit;
+ }
+
+ file = PyString_FromStringAndSize(start, zero - start);
+ if (!file)
+ goto bail;
+
+ nlen = cur - zero - 1;
+
+ node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
+ if (!node)
+ goto bail;
+
+ if (nlen > 40) {
+ PyObject *flags;
+
+ flags = PyString_FromStringAndSize(zero + 41,
+ nlen - 40);
+ if (!flags)
+ goto bail;
+
+ if (PyDict_SetItem(fdict, file, flags) == -1)
+ goto bail;
+ }
+
+ if (PyDict_SetItem(mfdict, file, node) == -1)
+ goto bail;
+
+ start = cur + 1;
+ zero = NULL;
+
+ Py_XDECREF(flags);
+ Py_XDECREF(node);
+ Py_XDECREF(file);
+ continue;
+ bail:
+ Py_XDECREF(flags);
+ Py_XDECREF(node);
+ Py_XDECREF(file);
+ goto quit;
+ }
+
+ if (len > 0 && *(cur - 1) != '\n') {
+ PyErr_SetString(PyExc_ValueError,
+ "manifest contains trailing garbage");
+ goto quit;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+
+quit:
+ return NULL;
+}
+
+static char parsers_doc[] = "Efficient content parsing.";
+
+static PyMethodDef methods[] = {
+ {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
+ {NULL, NULL}
+};
+
+PyMODINIT_FUNC initparsers(void)
+{
+ Py_InitModule3("parsers", methods, parsers_doc);
+}
--- a/mercurial/repair.py Sun Mar 30 23:58:02 2008 +0200
+++ b/mercurial/repair.py Mon Mar 31 00:01:27 2008 +0200
@@ -72,7 +72,6 @@
def strip(ui, repo, node, backup="all"):
cl = repo.changelog
# TODO delete the undo files, and handle undo of merge sets
- pp = cl.parents(node)
striprev = cl.rev(node)
# Some revisions with rev > striprev may not be descendants of striprev.
--- a/setup.py Sun Mar 30 23:58:02 2008 +0200
+++ b/setup.py Mon Mar 31 00:01:27 2008 +0200
@@ -88,10 +88,11 @@
cmdclass = {'install_data': install_package_data}
ext_modules=[
- Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
+ Extension('mercurial.base85', ['mercurial/base85.c']),
Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
- Extension('mercurial.base85', ['mercurial/base85.c']),
- Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'])
+ Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
+ Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
+ Extension('mercurial.parsers', ['mercurial/parsers.c']),
]
packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert']