mirror of
https://github.com/facebook/sapling.git
synced 2024-10-08 15:57:43 +03:00
ef568e7358
Remove the lambda used to wrap webutil.diffstat because: - functions passed to the templater must accept keyword arguments - webutil.diffstat is a generator, so already calculates the diffstat lazily This reverts the changes made to a4067e29e29d after it was submitted to the mailing list but before it was queued.
837 lines
28 KiB
Python
837 lines
28 KiB
Python
#
|
|
# 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 version 2 or any later version.
|
|
|
|
import os, mimetypes, re, cgi, copy
|
|
import webutil
|
|
from mercurial import error, encoding, archival, templater, templatefilters
|
|
from mercurial.node import short, hex
|
|
from mercurial.util import binary
|
|
from common import paritygen, staticfile, get_contact, ErrorResponse
|
|
from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
|
|
from mercurial import graphmod
|
|
from mercurial import help as helpmod
|
|
from mercurial.i18n import _
|
|
|
|
# __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.
|
|
|
|
__all__ = [
|
|
'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
|
|
'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
|
|
'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
|
|
]
|
|
|
|
def log(web, req, tmpl):
|
|
if 'file' in req.form and req.form['file'][0]:
|
|
return filelog(web, req, tmpl)
|
|
else:
|
|
return changelog(web, req, tmpl)
|
|
|
|
def rawfile(web, req, tmpl):
|
|
path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
|
|
if not path:
|
|
content = manifest(web, req, tmpl)
|
|
req.respond(HTTP_OK, web.ctype)
|
|
return content
|
|
|
|
try:
|
|
fctx = webutil.filectx(web.repo, req)
|
|
except error.LookupError, inst:
|
|
try:
|
|
content = manifest(web, req, tmpl)
|
|
req.respond(HTTP_OK, web.ctype)
|
|
return content
|
|
except ErrorResponse:
|
|
raise inst
|
|
|
|
path = fctx.path()
|
|
text = fctx.data()
|
|
mt = mimetypes.guess_type(path)[0]
|
|
if mt is None:
|
|
mt = binary(text) and 'application/octet-stream' or 'text/plain'
|
|
if mt.startswith('text/'):
|
|
mt += '; charset="%s"' % encoding.encoding
|
|
|
|
req.respond(HTTP_OK, mt, path, len(text))
|
|
return [text]
|
|
|
|
def _filerevision(web, tmpl, fctx):
|
|
f = fctx.path()
|
|
text = fctx.data()
|
|
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(True)):
|
|
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=fctx.hex(),
|
|
author=fctx.user(),
|
|
date=fctx.date(),
|
|
desc=fctx.description(),
|
|
branch=webutil.nodebranchnodefault(fctx),
|
|
parent=webutil.parents(fctx),
|
|
child=webutil.children(fctx),
|
|
rename=webutil.renamelink(fctx),
|
|
permissions=fctx.manifest().flags(f))
|
|
|
|
def file(web, req, tmpl):
|
|
path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
|
|
if not path:
|
|
return manifest(web, req, tmpl)
|
|
try:
|
|
return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
|
|
except error.LookupError, inst:
|
|
try:
|
|
return manifest(web, req, tmpl)
|
|
except ErrorResponse:
|
|
raise inst
|
|
|
|
def _search(web, req, tmpl):
|
|
|
|
query = req.form['rev'][0]
|
|
revcount = web.maxchanges
|
|
if 'revcount' in req.form:
|
|
revcount = int(req.form.get('revcount', [revcount])[0])
|
|
revcount = max(revcount, 1)
|
|
tmpl.defaults['sessionvars']['revcount'] = revcount
|
|
|
|
lessvars = copy.copy(tmpl.defaults['sessionvars'])
|
|
lessvars['revcount'] = max(revcount / 2, 1)
|
|
lessvars['rev'] = query
|
|
morevars = copy.copy(tmpl.defaults['sessionvars'])
|
|
morevars['revcount'] = revcount * 2
|
|
morevars['rev'] = query
|
|
|
|
def changelist(**map):
|
|
count = 0
|
|
qw = query.lower().split()
|
|
|
|
def revgen():
|
|
for i in xrange(len(web.repo) - 1, 0, -100):
|
|
l = []
|
|
for j in xrange(max(0, i - 100), i + 1):
|
|
ctx = web.repo[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)
|
|
files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
|
|
|
|
yield tmpl('searchentry',
|
|
parity=parity.next(),
|
|
author=ctx.user(),
|
|
parent=webutil.parents(ctx),
|
|
child=webutil.children(ctx),
|
|
changelogtag=showtags,
|
|
desc=ctx.description(),
|
|
date=ctx.date(),
|
|
files=files,
|
|
rev=ctx.rev(),
|
|
node=hex(n),
|
|
tags=webutil.nodetagsdict(web.repo, n),
|
|
bookmarks=webutil.nodebookmarksdict(web.repo, n),
|
|
inbranch=webutil.nodeinbranch(web.repo, ctx),
|
|
branches=webutil.nodebranchdict(web.repo, ctx))
|
|
|
|
if count >= revcount:
|
|
break
|
|
|
|
tip = web.repo['tip']
|
|
parity = paritygen(web.stripecount)
|
|
|
|
return tmpl('search', query=query, node=tip.hex(),
|
|
entries=changelist, archives=web.archivelist("tip"),
|
|
morevars=morevars, lessvars=lessvars)
|
|
|
|
def changelog(web, req, tmpl, shortlog=False):
|
|
|
|
if 'node' in req.form:
|
|
ctx = webutil.changectx(web.repo, req)
|
|
else:
|
|
if 'rev' in req.form:
|
|
hi = req.form['rev'][0]
|
|
else:
|
|
hi = len(web.repo) - 1
|
|
try:
|
|
ctx = web.repo[hi]
|
|
except error.RepoError:
|
|
return _search(web, req, tmpl) # XXX redirect to 404 page?
|
|
|
|
def changelist(limit=0, **map):
|
|
l = [] # build a list in forward order for efficiency
|
|
for i in xrange(start, end):
|
|
ctx = web.repo[i]
|
|
n = ctx.node()
|
|
showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
|
|
files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
|
|
|
|
l.insert(0, {"parity": parity.next(),
|
|
"author": ctx.user(),
|
|
"parent": webutil.parents(ctx, i - 1),
|
|
"child": webutil.children(ctx, i + 1),
|
|
"changelogtag": showtags,
|
|
"desc": ctx.description(),
|
|
"date": ctx.date(),
|
|
"files": files,
|
|
"rev": i,
|
|
"node": hex(n),
|
|
"tags": webutil.nodetagsdict(web.repo, n),
|
|
"bookmarks": webutil.nodebookmarksdict(web.repo, n),
|
|
"inbranch": webutil.nodeinbranch(web.repo, ctx),
|
|
"branches": webutil.nodebranchdict(web.repo, ctx)
|
|
})
|
|
|
|
if limit > 0:
|
|
l = l[:limit]
|
|
|
|
for e in l:
|
|
yield e
|
|
|
|
revcount = shortlog and web.maxshortchanges or web.maxchanges
|
|
if 'revcount' in req.form:
|
|
revcount = int(req.form.get('revcount', [revcount])[0])
|
|
revcount = max(revcount, 1)
|
|
tmpl.defaults['sessionvars']['revcount'] = revcount
|
|
|
|
lessvars = copy.copy(tmpl.defaults['sessionvars'])
|
|
lessvars['revcount'] = max(revcount / 2, 1)
|
|
morevars = copy.copy(tmpl.defaults['sessionvars'])
|
|
morevars['revcount'] = revcount * 2
|
|
|
|
count = len(web.repo)
|
|
pos = ctx.rev()
|
|
start = max(0, pos - revcount + 1)
|
|
end = min(count, start + revcount)
|
|
pos = end - 1
|
|
parity = paritygen(web.stripecount, offset=start - end)
|
|
|
|
changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx)
|
|
|
|
return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
|
|
node=ctx.hex(), rev=pos, changesets=count,
|
|
entries=lambda **x: changelist(limit=0,**x),
|
|
latestentry=lambda **x: changelist(limit=1,**x),
|
|
archives=web.archivelist("tip"), revcount=revcount,
|
|
morevars=morevars, lessvars=lessvars)
|
|
|
|
def shortlog(web, req, tmpl):
|
|
return changelog(web, req, tmpl, shortlog = True)
|
|
|
|
def changeset(web, req, tmpl):
|
|
ctx = webutil.changectx(web.repo, req)
|
|
showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
|
|
showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
|
|
ctx.node())
|
|
showbranch = webutil.nodebranchnodefault(ctx)
|
|
|
|
files = []
|
|
parity = paritygen(web.stripecount)
|
|
for f in ctx.files():
|
|
template = f in ctx and 'filenodelink' or 'filenolink'
|
|
files.append(tmpl(template,
|
|
node=ctx.hex(), file=f,
|
|
parity=parity.next()))
|
|
|
|
style = web.config('web', 'style', 'paper')
|
|
if 'style' in req.form:
|
|
style = req.form['style'][0]
|
|
|
|
parity = paritygen(web.stripecount)
|
|
diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style)
|
|
|
|
parity = paritygen(web.stripecount)
|
|
diffstat = webutil.diffstat(tmpl, ctx, parity)
|
|
|
|
return tmpl('changeset',
|
|
diff=diffs,
|
|
rev=ctx.rev(),
|
|
node=ctx.hex(),
|
|
parent=webutil.parents(ctx),
|
|
child=webutil.children(ctx),
|
|
changesettag=showtags,
|
|
changesetbookmark=showbookmarks,
|
|
changesetbranch=showbranch,
|
|
author=ctx.user(),
|
|
desc=ctx.description(),
|
|
date=ctx.date(),
|
|
files=files,
|
|
diffstat=diffstat,
|
|
archives=web.archivelist(ctx.hex()),
|
|
tags=webutil.nodetagsdict(web.repo, ctx.node()),
|
|
bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
|
|
branch=webutil.nodebranchnodefault(ctx),
|
|
inbranch=webutil.nodeinbranch(web.repo, ctx),
|
|
branches=webutil.nodebranchdict(web.repo, ctx))
|
|
|
|
rev = changeset
|
|
|
|
def manifest(web, req, tmpl):
|
|
ctx = webutil.changectx(web.repo, req)
|
|
path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
|
|
mf = ctx.manifest()
|
|
node = ctx.node()
|
|
|
|
files = {}
|
|
dirs = {}
|
|
parity = paritygen(web.stripecount)
|
|
|
|
if path and path[-1] != "/":
|
|
path += "/"
|
|
l = len(path)
|
|
abspath = "/" + path
|
|
|
|
for f, n in mf.iteritems():
|
|
if f[:l] != path:
|
|
continue
|
|
remain = f[l:]
|
|
elements = remain.split('/')
|
|
if len(elements) == 1:
|
|
files[remain] = f
|
|
else:
|
|
h = dirs # need to retain ref to dirs (root)
|
|
for elem in elements[0:-1]:
|
|
if elem not in h:
|
|
h[elem] = {}
|
|
h = h[elem]
|
|
if len(h) > 1:
|
|
break
|
|
h[None] = None # denotes files present
|
|
|
|
if mf and not files and not dirs:
|
|
raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
|
|
|
|
def filelist(**map):
|
|
for f in sorted(files):
|
|
full = files[f]
|
|
|
|
fctx = ctx.filectx(full)
|
|
yield {"file": full,
|
|
"parity": parity.next(),
|
|
"basename": f,
|
|
"date": fctx.date(),
|
|
"size": fctx.size(),
|
|
"permissions": mf.flags(full)}
|
|
|
|
def dirlist(**map):
|
|
for d in sorted(dirs):
|
|
|
|
emptydirs = []
|
|
h = dirs[d]
|
|
while isinstance(h, dict) and len(h) == 1:
|
|
k, v = h.items()[0]
|
|
if v:
|
|
emptydirs.append(k)
|
|
h = v
|
|
|
|
path = "%s%s" % (abspath, d)
|
|
yield {"parity": parity.next(),
|
|
"path": path,
|
|
"emptydirs": "/".join(emptydirs),
|
|
"basename": d}
|
|
|
|
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),
|
|
bookmarks=webutil.nodebookmarksdict(web.repo, node),
|
|
inbranch=webutil.nodeinbranch(web.repo, ctx),
|
|
branches=webutil.nodebranchdict(web.repo, ctx))
|
|
|
|
def tags(web, req, 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[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 bookmarks(web, req, tmpl):
|
|
i = web.repo._bookmarks.items()
|
|
parity = paritygen(web.stripecount)
|
|
|
|
def entries(limit=0, **map):
|
|
count = 0
|
|
for k, n in sorted(i):
|
|
if limit > 0 and count >= limit:
|
|
continue
|
|
count = count + 1
|
|
yield {"parity": parity.next(),
|
|
"bookmark": k,
|
|
"date": web.repo[n].date(),
|
|
"node": hex(n)}
|
|
|
|
return tmpl("bookmarks",
|
|
node=hex(web.repo.changelog.tip()),
|
|
entries=lambda **x: entries(0, **x),
|
|
latestentry=lambda **x: entries(1, **x))
|
|
|
|
def branches(web, req, tmpl):
|
|
tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
|
|
heads = web.repo.heads()
|
|
parity = paritygen(web.stripecount)
|
|
sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev())
|
|
|
|
def entries(limit, **map):
|
|
count = 0
|
|
for ctx in sorted(tips, key=sortkey, reverse=True):
|
|
if limit > 0 and count >= limit:
|
|
return
|
|
count += 1
|
|
if ctx.node() not in heads:
|
|
status = 'inactive'
|
|
elif not web.repo.branchheads(ctx.branch()):
|
|
status = 'closed'
|
|
else:
|
|
status = 'open'
|
|
yield {'parity': parity.next(),
|
|
'branch': ctx.branch(),
|
|
'status': status,
|
|
'node': ctx.hex(),
|
|
'date': ctx.date()}
|
|
|
|
return tmpl('branches', node=hex(web.repo.changelog.tip()),
|
|
entries=lambda **x: entries(0, **x),
|
|
latestentry=lambda **x: entries(1, **x))
|
|
|
|
def summary(web, req, 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[n].date())
|
|
|
|
def bookmarks(**map):
|
|
parity = paritygen(web.stripecount)
|
|
b = web.repo._bookmarks.items()
|
|
for k, n in sorted(b)[:10]: # limit to 10 bookmarks
|
|
yield {'parity': parity.next(),
|
|
'bookmark': k,
|
|
'date': web.repo[n].date(),
|
|
'node': hex(n)}
|
|
|
|
def branches(**map):
|
|
parity = paritygen(web.stripecount)
|
|
|
|
b = web.repo.branchtags()
|
|
l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
|
|
for r, n, t in sorted(l):
|
|
yield {'parity': parity.next(),
|
|
'branch': t,
|
|
'node': hex(n),
|
|
'date': web.repo[n].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[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),
|
|
bookmarks=webutil.nodebookmarksdict(web.repo, n),
|
|
inbranch=webutil.nodeinbranch(web.repo, ctx),
|
|
branches=webutil.nodebranchdict(web.repo, ctx)))
|
|
|
|
yield l
|
|
|
|
tip = web.repo['tip']
|
|
count = len(web.repo)
|
|
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=tip.date(),
|
|
tags=tagentries,
|
|
bookmarks=bookmarks,
|
|
branches=branches,
|
|
shortlog=changelist,
|
|
node=tip.hex(),
|
|
archives=web.archivelist("tip"))
|
|
|
|
def filediff(web, req, tmpl):
|
|
fctx, ctx = None, None
|
|
try:
|
|
fctx = webutil.filectx(web.repo, req)
|
|
except LookupError:
|
|
ctx = webutil.changectx(web.repo, req)
|
|
path = webutil.cleanpath(web.repo, req.form['file'][0])
|
|
if path not in ctx.files():
|
|
raise
|
|
|
|
if fctx is not None:
|
|
n = fctx.node()
|
|
path = fctx.path()
|
|
else:
|
|
n = ctx.node()
|
|
# path already defined in except clause
|
|
|
|
parity = paritygen(web.stripecount)
|
|
style = web.config('web', 'style', 'paper')
|
|
if 'style' in req.form:
|
|
style = req.form['style'][0]
|
|
|
|
diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity, style)
|
|
rename = fctx and webutil.renamelink(fctx) or []
|
|
ctx = fctx and fctx or ctx
|
|
return tmpl("filediff",
|
|
file=path,
|
|
node=hex(n),
|
|
rev=ctx.rev(),
|
|
date=ctx.date(),
|
|
desc=ctx.description(),
|
|
author=ctx.user(),
|
|
rename=rename,
|
|
branch=webutil.nodebranchnodefault(ctx),
|
|
parent=webutil.parents(ctx),
|
|
child=webutil.children(ctx),
|
|
diff=diffs)
|
|
|
|
diff = filediff
|
|
|
|
def annotate(web, req, tmpl):
|
|
fctx = webutil.filectx(web.repo, req)
|
|
f = fctx.path()
|
|
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()
|
|
|
|
if last != fnode:
|
|
last = fnode
|
|
|
|
yield {"parity": parity.next(),
|
|
"node": f.hex(),
|
|
"rev": f.rev(),
|
|
"author": f.user(),
|
|
"desc": f.description(),
|
|
"file": f.path(),
|
|
"targetline": targetline,
|
|
"line": l,
|
|
"lineid": "l%d" % (lineno + 1),
|
|
"linenumber": "% 6d" % (lineno + 1),
|
|
"revdate": f.date()}
|
|
|
|
return tmpl("fileannotate",
|
|
file=f,
|
|
annotate=annotate,
|
|
path=webutil.up(f),
|
|
rev=fctx.rev(),
|
|
node=fctx.hex(),
|
|
author=fctx.user(),
|
|
date=fctx.date(),
|
|
desc=fctx.description(),
|
|
rename=webutil.renamelink(fctx),
|
|
branch=webutil.nodebranchnodefault(fctx),
|
|
parent=webutil.parents(fctx),
|
|
child=webutil.children(fctx),
|
|
permissions=fctx.manifest().flags(f))
|
|
|
|
def filelog(web, req, tmpl):
|
|
|
|
try:
|
|
fctx = webutil.filectx(web.repo, req)
|
|
f = fctx.path()
|
|
fl = fctx.filelog()
|
|
except error.LookupError:
|
|
f = webutil.cleanpath(web.repo, req.form['file'][0])
|
|
fl = web.repo.file(f)
|
|
numrevs = len(fl)
|
|
if not numrevs: # file doesn't exist at all
|
|
raise
|
|
rev = webutil.changectx(web.repo, req).rev()
|
|
first = fl.linkrev(0)
|
|
if rev < first: # current rev is from before file existed
|
|
raise
|
|
frev = numrevs - 1
|
|
while fl.linkrev(frev) > rev:
|
|
frev -= 1
|
|
fctx = web.repo.filectx(f, fl.linkrev(frev))
|
|
|
|
revcount = web.maxshortchanges
|
|
if 'revcount' in req.form:
|
|
revcount = int(req.form.get('revcount', [revcount])[0])
|
|
revcount = max(revcount, 1)
|
|
tmpl.defaults['sessionvars']['revcount'] = revcount
|
|
|
|
lessvars = copy.copy(tmpl.defaults['sessionvars'])
|
|
lessvars['revcount'] = max(revcount / 2, 1)
|
|
morevars = copy.copy(tmpl.defaults['sessionvars'])
|
|
morevars['revcount'] = revcount * 2
|
|
|
|
count = fctx.filerev() + 1
|
|
start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
|
|
end = min(count, start + revcount) # last rev on this page
|
|
parity = paritygen(web.stripecount, offset=start - end)
|
|
|
|
def entries(limit=0, **map):
|
|
l = []
|
|
|
|
repo = web.repo
|
|
for i in xrange(start, end):
|
|
iterfctx = fctx.filectx(i)
|
|
|
|
l.insert(0, {"parity": parity.next(),
|
|
"filerev": i,
|
|
"file": f,
|
|
"node": iterfctx.hex(),
|
|
"author": iterfctx.user(),
|
|
"date": iterfctx.date(),
|
|
"rename": webutil.renamelink(iterfctx),
|
|
"parent": webutil.parents(iterfctx),
|
|
"child": webutil.children(iterfctx),
|
|
"desc": iterfctx.description(),
|
|
"tags": webutil.nodetagsdict(repo, iterfctx.node()),
|
|
"bookmarks": webutil.nodebookmarksdict(
|
|
repo, iterfctx.node()),
|
|
"branch": webutil.nodebranchnodefault(iterfctx),
|
|
"inbranch": webutil.nodeinbranch(repo, iterfctx),
|
|
"branches": webutil.nodebranchdict(repo, iterfctx)})
|
|
|
|
if limit > 0:
|
|
l = l[:limit]
|
|
|
|
for e in l:
|
|
yield e
|
|
|
|
nodefunc = lambda x: fctx.filectx(fileid=x)
|
|
nav = webutil.revnavgen(end - 1, revcount, count, nodefunc)
|
|
return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
|
|
entries=lambda **x: entries(limit=0, **x),
|
|
latestentry=lambda **x: entries(limit=1, **x),
|
|
revcount=revcount, morevars=morevars, lessvars=lessvars)
|
|
|
|
def archive(web, req, tmpl):
|
|
type_ = req.form.get('type', [None])[0]
|
|
allowed = web.configlist("web", "allow_archive")
|
|
key = req.form['node'][0]
|
|
|
|
if type_ not in web.archives:
|
|
msg = 'Unsupported archive type: %s' % type_
|
|
raise ErrorResponse(HTTP_NOT_FOUND, msg)
|
|
|
|
if not ((type_ in allowed or
|
|
web.configbool("web", "allow" + type_, False))):
|
|
msg = 'Archive type not allowed: %s' % type_
|
|
raise ErrorResponse(HTTP_FORBIDDEN, 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]
|
|
# a repo owner may set web.static in .hg/hgrc to get any file
|
|
# readable by the user running the CGI script
|
|
static = web.config("web", "static", None, untrusted=False)
|
|
if not static:
|
|
tp = web.templatepath or templater.templatepath()
|
|
if isinstance(tp, str):
|
|
tp = [tp]
|
|
static = [os.path.join(p, 'static') for p in tp]
|
|
return [staticfile(static, fname, req)]
|
|
|
|
def graph(web, req, tmpl):
|
|
|
|
rev = webutil.changectx(web.repo, req).rev()
|
|
bg_height = 39
|
|
revcount = web.maxshortchanges
|
|
if 'revcount' in req.form:
|
|
revcount = int(req.form.get('revcount', [revcount])[0])
|
|
revcount = max(revcount, 1)
|
|
tmpl.defaults['sessionvars']['revcount'] = revcount
|
|
|
|
lessvars = copy.copy(tmpl.defaults['sessionvars'])
|
|
lessvars['revcount'] = max(revcount / 2, 1)
|
|
morevars = copy.copy(tmpl.defaults['sessionvars'])
|
|
morevars['revcount'] = revcount * 2
|
|
|
|
max_rev = len(web.repo) - 1
|
|
revcount = min(max_rev, revcount)
|
|
revnode = web.repo.changelog.node(rev)
|
|
revnode_hex = hex(revnode)
|
|
uprev = min(max_rev, rev + revcount)
|
|
downrev = max(0, rev - revcount)
|
|
count = len(web.repo)
|
|
changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
|
|
startrev = rev
|
|
# if starting revision is less than 60 set it to uprev
|
|
if rev < web.maxshortchanges:
|
|
startrev = uprev
|
|
|
|
dag = graphmod.dagwalker(web.repo, range(startrev, downrev - 1, -1))
|
|
tree = list(graphmod.colored(dag))
|
|
canvasheight = (len(tree) + 1) * bg_height - 27
|
|
data = []
|
|
for (id, type, ctx, vtx, edges) in tree:
|
|
if type != graphmod.CHANGESET:
|
|
continue
|
|
node = str(ctx)
|
|
age = templatefilters.age(ctx.date())
|
|
desc = templatefilters.firstline(ctx.description())
|
|
desc = cgi.escape(templatefilters.nonempty(desc))
|
|
user = cgi.escape(templatefilters.person(ctx.user()))
|
|
branch = ctx.branch()
|
|
branch = branch, web.repo.branchtags().get(branch) == ctx.node()
|
|
data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(),
|
|
ctx.bookmarks()))
|
|
|
|
return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
|
|
lessvars=lessvars, morevars=morevars, downrev=downrev,
|
|
canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
|
|
node=revnode_hex, changenav=changenav)
|
|
|
|
def _getdoc(e):
|
|
doc = e[0].__doc__
|
|
if doc:
|
|
doc = doc.split('\n')[0]
|
|
else:
|
|
doc = _('(no help text available)')
|
|
return doc
|
|
|
|
def help(web, req, tmpl):
|
|
from mercurial import commands # avoid cycle
|
|
|
|
topicname = req.form.get('node', [None])[0]
|
|
if not topicname:
|
|
def topics(**map):
|
|
for entries, summary, _ in helpmod.helptable:
|
|
entries = sorted(entries, key=len)
|
|
yield {'topic': entries[-1], 'summary': summary}
|
|
|
|
early, other = [], []
|
|
primary = lambda s: s.split('|')[0]
|
|
for c, e in commands.table.iteritems():
|
|
doc = _getdoc(e)
|
|
if 'DEPRECATED' in doc or c.startswith('debug'):
|
|
continue
|
|
cmd = primary(c)
|
|
if cmd.startswith('^'):
|
|
early.append((cmd[1:], doc))
|
|
else:
|
|
other.append((cmd, doc))
|
|
|
|
early.sort()
|
|
other.sort()
|
|
|
|
def earlycommands(**map):
|
|
for c, doc in early:
|
|
yield {'topic': c, 'summary': doc}
|
|
|
|
def othercommands(**map):
|
|
for c, doc in other:
|
|
yield {'topic': c, 'summary': doc}
|
|
|
|
return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
|
|
othercommands=othercommands, title='Index')
|
|
|
|
u = webutil.wsgiui()
|
|
u.pushbuffer()
|
|
try:
|
|
commands.help_(u, topicname)
|
|
except error.UnknownCommand:
|
|
raise ErrorResponse(HTTP_NOT_FOUND)
|
|
doc = u.popbuffer()
|
|
return tmpl('help', topic=topicname, doc=doc)
|