##// END OF EJS Templates
bookmarks: check HG_PENDING strictly...
bookmarks: check HG_PENDING strictly Before this patch, checking HG_PENDING in bookmarks.py might cause unintentional reading unrelated '.hg/bookmarks.pending' in, because it just examines existence of HG_PENDING environment variable. This patch uses txnutil.trypending() to check HG_PENDING strictly. This patch also changes share extension. Enabling share extension (+ bookmark sharing) makes bookmarks._getbkfile() receive repo to be shared (= "srcrepo"). On the other hand, HG_PENDING always refers current working repo (= "currepo"), and bookmarks.pending is written only into currepo. Therefore, we should try to read .hg/bookmarks.pending of currepo in at first. If it doesn't exist, we try to read .hg/bookmarks of srcrepo in. Even after this patch, an external hook spawned in currepo can't see pending changes in currepo via srcrepo, even though such changes become visible after closing transaction, because there is no easy and cheap way to know existence of pending changes in currepo via srcrepo. Please see https://www.mercurial-scm.org/wiki/SharedRepository, too. BTW, this patch may cause failure of bisect in the repository of Mercurial itself, if examination at bisecting assumes that an external hook can see all pending changes while nested transactions across repositories. This invisibility issue will be fixed by subsequent patch, which allows HG_PENDING to refer multiple repositories.

File last commit:

r30375:11b8b740 default
r31052:0332b8fa default
Show More
webutil.py
613 lines | 19.2 KiB | text/x-python | PythonLexer
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 # 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>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Yuya Nishihara
hgweb: use absolute_import
r27046 from __future__ import absolute_import
import copy
import difflib
import os
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 import re
Yuya Nishihara
hgweb: use absolute_import
r27046
from ..i18n import _
from ..node import hex, nullid, short
from .common import (
ErrorResponse,
HTTP_NOT_FOUND,
paritygen,
)
from .. import (
context,
error,
match,
patch,
pathutil,
templatefilters,
ui as uimod,
util,
)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 def up(p):
if p[0] != "/":
p = "/" + p
if p[-1] == "/":
p = p[:-1]
up = os.path.dirname(p)
if up == "/":
return "/"
return up + "/"
Pierre-Yves David
hgweb: better names for _navseq arguments...
r18391 def _navseq(step, firststep=None):
if firststep:
yield firststep
if firststep >= 20 and firststep <= 40:
Pierre-Yves David
hgweb: ensure _navseq yield strictly increasing numbers...
r18392 firststep = 50
yield firststep
assert step > 0
assert firststep > 0
while step <= firststep:
step *= 10
Pierre-Yves David
hgweb: drop recursivity in _navseq...
r18390 while True:
Pierre-Yves David
hgweb: better names for _navseq arguments...
r18391 yield 1 * step
yield 3 * step
step *= 10
Pierre-Yves David
hgweb: move the `seq` function out of the revnavgen scope...
r18389
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 class revnav(object):
Pierre-Yves David
hgweb: document the revnavgen function
r18320
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 def __init__(self, repo):
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 """Navigation generation object
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 :repo: repo object we generate nav for
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 """
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 # used for hex generation
self._revlog = repo.changelog
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406 def __nonzero__(self):
"""return True if any revision to navigate over"""
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 return self._first() is not None
def _first(self):
"""return the minimum non-filtered changeset or None"""
try:
timeless
py3: convert to next() function...
r29216 return next(iter(self._revlog))
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 except StopIteration:
return None
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406
Pierre-Yves David
hgweb: move hex creation into an object method...
r18405 def hex(self, rev):
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 return hex(self._revlog.node(rev))
Pierre-Yves David
hgweb: move hex creation into an object method...
r18405
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 def gen(self, pos, pagelen, limit):
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 """computes label and revision id for navigation link
Pierre-Yves David
hgweb: document the revnavgen function
r18320
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 :pos: is the revision relative to which we generate navigation.
:pagelen: the size of each navigation page
:limit: how far shall we link
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 The return is:
- a single element tuple
- containing a dictionary with a `before` and `after` key
- values are generator functions taking arbitrary number of kwargs
- yield items are dictionaries with `label` and `node` keys
"""
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406 if not self:
# empty repo
return ({'before': (), 'after': ()},)
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 targets = []
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 for f in _navseq(1, pagelen):
if f > limit:
break
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 targets.append(pos + f)
targets.append(pos - f)
targets.sort()
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 first = self._first()
navbefore = [("(%i)" % first, self.hex(first))]
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 navafter = []
for rev in targets:
Pierre-Yves David
hgweb: ignore filtered revision in revnav...
r18426 if rev not in self._revlog:
continue
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 if pos < rev < limit:
Pierre-Yves David
hgweb: fix navigation label (issue3792)...
r18503 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 if 0 < rev < pos:
Pierre-Yves David
hgweb: fix navigation label (issue3792)...
r18503 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425
Nicolas Dumazet
hgweb: changenav: separate pages before and after the current position...
r10254
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 navafter.append(("tip", "tip"))
data = lambda i: {"label": i[0], "node": i[1]}
return ({'before': lambda **map: (data(i) for i in navbefore),
'after': lambda **map: (data(i) for i in navafter)},)
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: introduction a filerevnav subclass...
r18408 class filerevnav(revnav):
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409
def __init__(self, repo, path):
"""Navigation generation object
:repo: repo object we generate nav for
:path: path of the file we generate nav for
"""
# used for iteration
self._changelog = repo.unfiltered().changelog
# used for hex generation
self._revlog = repo.file(path)
def hex(self, rev):
return hex(self._changelog.node(self._revlog.linkrev(rev)))
av6
webutil: make _siblings into an object with __iter__ and __len__...
r27023 class _siblings(object):
def __init__(self, siblings=[], hiderev=None):
self.siblings = [s for s in siblings if s.node() != nullid]
if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
self.siblings = []
Pierre-Yves David
hgweb: introduction a filerevnav subclass...
r18408
av6
webutil: make _siblings into an object with __iter__ and __len__...
r27023 def __iter__(self):
for s in self.siblings:
d = {
'node': s.hex(),
'rev': s.rev(),
'user': s.user(),
'date': s.date(),
'description': s.description(),
'branch': s.branch(),
}
if util.safehasattr(s, 'path'):
d['file'] = s.path()
yield d
def __len__(self):
return len(self.siblings)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Jun Wu
hgweb: make fctx.annotate a separated function so it could be wrapped...
r30081 def annotate(fctx, ui):
diffopts = patch.difffeatureopts(ui, untrusted=True,
section='annotate', whitespace=True)
return fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)
Dirkjan Ochtman
hgweb: simplify parents/children generation code
r7671 def parents(ctx, hide=None):
Anton Shestakov
hgweb: use introrev() for finding parents (issue4506)...
r24136 if isinstance(ctx, context.basefilectx):
introrev = ctx.introrev()
if ctx.changectx().rev() != introrev:
Matt Harbison
hgweb: replace 'ctx._repo' with 'ctx.repo()'
r24340 return _siblings([ctx.repo()[introrev]], hide)
Dirkjan Ochtman
hgweb: simplify parents/children generation code
r7671 return _siblings(ctx.parents(), hide)
def children(ctx, hide=None):
return _siblings(ctx.children(), hide)
Matt Mackall
hgweb: minor improvements for new web style...
r6434 def renamelink(fctx):
Matt Mackall
hgweb: fix merge breakage
r6437 r = fctx.renamed()
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 if r:
Augie Fackler
webutil: move from dict() construction to {} literals...
r20681 return [{'file': r[0], 'node': hex(r[1])}]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 return []
def nodetagsdict(repo, node):
return [{"name": i} for i in repo.nodetags(node)]
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596 def nodebookmarksdict(repo, node):
return [{"name": i} for i in repo.nodebookmarks(node)]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def nodebranchdict(repo, ctx):
branches = []
branch = ctx.branch()
# If this is an empty repo, ctx.node() == nullid,
Brodie Rao
localrepo: add branchtip() method for faster single-branch lookups...
r16719 # ctx.branch() == 'default'.
try:
branchnode = repo.branchtip(branch)
except error.RepoLookupError:
branchnode = None
if branchnode == ctx.node():
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 branches.append({"name": branch})
return branches
def nodeinbranch(repo, ctx):
branches = []
branch = ctx.branch()
Brodie Rao
localrepo: add branchtip() method for faster single-branch lookups...
r16719 try:
branchnode = repo.branchtip(branch)
except error.RepoLookupError:
branchnode = None
if branch != 'default' and branchnode != ctx.node():
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 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)
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596 def showbookmark(repo, tmpl, t1, node=nullid, **args):
for t in repo.nodebookmarks(node):
yield tmpl(t1, bookmark=t, **args)
av6
hgweb: move branchentries code from webcommands to webutil
r26129 def branchentries(repo, stripecount, limit=0):
tips = []
heads = repo.heads()
parity = paritygen(stripecount)
sortkey = lambda item: (not item[1], item[0].rev())
def entries(**map):
count = 0
if not tips:
for tag, hs, tip, closed in repo.branchmap().iterbranches():
tips.append((repo[tip], closed))
for ctx, closed in sorted(tips, key=sortkey, reverse=True):
if limit > 0 and count >= limit:
return
count += 1
if closed:
status = 'closed'
elif ctx.node() not in heads:
status = 'inactive'
else:
status = 'open'
yield {
timeless
py3: convert to next() function...
r29216 'parity': next(parity),
av6
hgweb: move branchentries code from webcommands to webutil
r26129 'branch': ctx.branch(),
'status': status,
'node': ctx.hex(),
'date': ctx.date()
}
return entries
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def cleanpath(repo, path):
path = path.lstrip('/')
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 return pathutil.canonpath(repo.root, '', path)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
av6
style: adjust whitespaces in webutil.py...
r25999 def changeidctx(repo, changeid):
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 try:
Matt Mackall
use repo[changeid] to get a changectx
r6747 ctx = repo[changeid]
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Durham Goode
manifest: remove last uses of repo.manifest...
r30375 man = repo.manifestlog._revlog
Matt Mackall
linkrev: take a revision number rather than a hash
r7361 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
return ctx
av6
style: adjust whitespaces in webutil.py...
r25999 def changectx(repo, req):
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 changeid = "tip"
if 'node' in req.form:
changeid = req.form['node'][0]
av6
style: adjust whitespaces in webutil.py...
r25999 ipos = changeid.find(':')
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 if ipos != -1:
changeid = changeid[(ipos + 1):]
elif 'manifest' in req.form:
changeid = req.form['manifest'][0]
return changeidctx(repo, changeid)
def basechangectx(repo, req):
if 'node' in req.form:
changeid = req.form['node'][0]
av6
style: adjust whitespaces in webutil.py...
r25999 ipos = changeid.find(':')
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 if ipos != -1:
changeid = changeid[:ipos]
return changeidctx(repo, changeid)
return None
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def filectx(repo, req):
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 if 'file' not in req.form:
raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 path = cleanpath(repo, req.form['file'][0])
if 'node' in req.form:
changeid = req.form['node'][0]
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 elif 'filenode' in req.form:
changeid = req.form['filenode'][0]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 else:
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 try:
Matt Mackall
use repo[changeid] to get a changectx
r6747 fctx = repo[changeid][path]
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 fctx = repo.filectx(path, fileid=changeid)
return fctx
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 def commonentry(repo, ctx):
node = ctx.node()
return {
'rev': ctx.rev(),
'node': hex(node),
'author': ctx.user(),
'desc': ctx.description(),
'date': ctx.date(),
'extra': ctx.extra(),
'phase': ctx.phasestr(),
'branch': nodebranchnodefault(ctx),
'inbranch': nodeinbranch(repo, ctx),
'branches': nodebranchdict(repo, ctx),
'tags': nodetagsdict(repo, node),
'bookmarks': nodebookmarksdict(repo, node),
'parent': lambda **x: parents(ctx),
'child': lambda **x: children(ctx),
}
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745 def changelistentry(web, ctx, tmpl):
'''Obtain a dictionary to be used for entries in a changelist.
This function is called when producing items for the "entries" list passed
to the "shortlog" and "changelog" templates.
'''
repo = web.repo
rev = ctx.rev()
n = ctx.node()
showtags = showtag(repo, tmpl, 'changelogtag', n)
files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 entry = commonentry(repo, ctx)
entry.update(
av6
hgweb: add parents to json-log (issue5074)...
r28709 allparents=lambda **x: parents(ctx),
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 parent=lambda **x: parents(ctx, rev - 1),
child=lambda **x: children(ctx, rev + 1),
changelogtag=showtags,
files=files,
)
return entry
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 def symrevorshortnode(req, ctx):
if 'node' in req.form:
Yuya Nishihara
hgweb: do not import templatefilters.revescape and websub as symbol...
r27008 return templatefilters.revescape(req.form['node'][0])
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 else:
return short(ctx.node())
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 def changesetentry(web, req, tmpl, ctx):
'''Obtain a dictionary to be used to render the "changeset" template.'''
showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
ctx.node())
showbranch = nodebranchnodefault(ctx)
files = []
parity = paritygen(web.stripecount)
for blockno, f in enumerate(ctx.files()):
template = f in ctx and 'filenodelink' or 'filenolink'
files.append(tmpl(template,
node=ctx.hex(), file=f, blockno=blockno + 1,
timeless
py3: convert to next() function...
r29216 parity=next(parity)))
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
basectx = basechangectx(web.repo, req)
if basectx is None:
basectx = ctx.p1()
style = web.config('web', 'style', 'paper')
if 'style' in req.form:
style = req.form['style'][0]
parity = paritygen(web.stripecount)
diff = diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
parity = paritygen(web.stripecount)
diffstatsgen = diffstatgen(ctx, basectx)
diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
return dict(
diff=diff,
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 symrev=symrevorshortnode(req, ctx),
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 basenode=basectx.hex(),
changesettag=showtags,
changesetbookmark=showbookmarks,
changesetbranch=showbranch,
files=files,
diffsummary=lambda **x: diffsummary(diffstatsgen),
diffstat=diffstats,
archives=web.archivelist(ctx.hex()),
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 **commonentry(web.repo, ctx))
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
Dirkjan Ochtman
hgweb: move another utility function into the webutil module
r7311 def listfilediffs(tmpl, files, node, max):
for f in files[:max]:
yield tmpl('filedifflink', node=hex(node), file=f)
if len(files) > max:
yield tmpl('fileellipses')
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
def countgen():
start = 1
while True:
yield start
start += 1
blockcount = countgen()
Paul Boddie
hgweb: add block numbers to diff regions and related links...
r16308 def prettyprintlines(diff, blockno):
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 for lineno, l in enumerate(diff.splitlines(True)):
Gregory Szorc
hgweb: expose raw line numbers to templates...
r24712 difflineno = "%d.%d" % (blockno, lineno + 1)
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 if l.startswith('+'):
ltype = "difflineplus"
elif l.startswith('-'):
ltype = "difflineminus"
elif l.startswith('@'):
ltype = "difflineat"
else:
ltype = "diffline"
yield tmpl(ltype,
line=l,
Gregory Szorc
hgweb: expose raw line numbers to templates...
r24712 lineno=lineno + 1,
lineid="l%s" % difflineno,
linenumber="% 8s" % difflineno)
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
if files:
m = match.exact(repo.root, repo.getcwd(), files)
else:
m = match.always(repo.root, repo.getcwd())
diffopts = patch.diffopts(repo.ui, untrusted=True)
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 if basectx is None:
parents = ctx.parents()
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if parents:
node1 = parents[0].node()
else:
node1 = nullid
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 else:
node1 = basectx.node()
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 node2 = ctx.node()
block = []
for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
if chunk.startswith('diff') and block:
timeless
py3: convert to next() function...
r29216 blockno = next(blockcount)
yield tmpl('diffblock', parity=next(parity), blockno=blockno,
Paul Boddie
hgweb: add block numbers to diff regions and related links...
r16308 lines=prettyprintlines(''.join(block), blockno))
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 block = []
Dirkjan Ochtman
hgweb: show diff header line in raw diffs
r9402 if chunk.startswith('diff') and style != 'raw':
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 chunk = ''.join(chunk.splitlines(True)[1:])
block.append(chunk)
timeless
py3: convert to next() function...
r29216 blockno = next(blockcount)
yield tmpl('diffblock', parity=next(parity), blockno=blockno,
Paul Boddie
hgweb: add block numbers to diff regions and related links...
r16308 lines=prettyprintlines(''.join(block), blockno))
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345
wujek srujek
hgweb: fixes traceback for invalid files by removing top-level template...
r17302 def compare(tmpl, context, leftlines, rightlines):
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 '''Generator function that provides side-by-side comparison data.'''
def compline(type, leftlineno, leftline, rightlineno, rightline):
lineid = leftlineno and ("l%s" % leftlineno) or ''
lineid += rightlineno and ("r%s" % rightlineno) or ''
return tmpl('comparisonline',
type=type,
lineid=lineid,
Gregory Szorc
hgweb: expose raw line numbers to templates...
r24712 leftlineno=leftlineno,
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 leftlinenumber="% 6s" % (leftlineno or ''),
leftline=leftline or '',
Gregory Szorc
hgweb: expose raw line numbers to templates...
r24712 rightlineno=rightlineno,
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 rightlinenumber="% 6s" % (rightlineno or ''),
rightline=rightline or '')
def getblock(opcodes):
for type, llo, lhi, rlo, rhi in opcodes:
len1 = lhi - llo
len2 = rhi - rlo
count = min(len1, len2)
for i in xrange(count):
yield compline(type=type,
leftlineno=llo + i + 1,
leftline=leftlines[llo + i],
rightlineno=rlo + i + 1,
rightline=rightlines[rlo + i])
if len1 > len2:
for i in xrange(llo + count, lhi):
yield compline(type=type,
leftlineno=i + 1,
leftline=leftlines[i],
rightlineno=None,
rightline=None)
elif len2 > len1:
for i in xrange(rlo + count, rhi):
yield compline(type=type,
leftlineno=None,
leftline=None,
rightlineno=i + 1,
rightline=rightlines[i])
s = difflib.SequenceMatcher(None, leftlines, rightlines)
if context < 0:
wujek srujek
hgweb: fixes traceback for invalid files by removing top-level template...
r17302 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 else:
wujek srujek
hgweb: fixes traceback for invalid files by removing top-level template...
r17302 for oc in s.get_grouped_opcodes(n=context):
yield tmpl('comparisonblock', lines=getblock(oc))
wujek srujek
hgweb: side-by-side comparison functionality...
r17202
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 def diffstatgen(ctx, basectx):
Steven Brown
web: provide diff summary to the changeset page...
r14570 '''Generator function that provides the diffstat data.'''
Steven Brown
web: provide diffstat to the changeset page...
r14490
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
Steven Brown
web: provide diffstat to the changeset page...
r14490 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
Steven Brown
web: provide diff summary to the changeset page...
r14570 while True:
yield stats, maxname, maxtotal, addtotal, removetotal, binary
def diffsummary(statgen):
'''Return a short summary of the diff.'''
timeless
py3: convert to next() function...
r29216 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
Steven Brown
web: provide diff summary to the changeset page...
r14570 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
len(stats), addtotal, removetotal)
def diffstat(tmpl, ctx, statgen, parity):
'''Return a diffstat template for each file in the diff.'''
timeless
py3: convert to next() function...
r29216 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
Steven Brown
web: include all files in the diffstat...
r14561 files = ctx.files()
Steven Brown
web: provide diffstat to the changeset page...
r14490
Steven Brown
web: include all files in the diffstat...
r14561 def pct(i):
if maxtotal == 0:
return 0
return (float(i) / maxtotal) * 100
Steven Brown
web: provide diffstat to the changeset page...
r14490
Steven Brown
web: provide the file number to the diffstat templates...
r14562 fileno = 0
Steven Brown
web: include all files in the diffstat...
r14561 for filename, adds, removes, isbinary in stats:
template = filename in files and 'diffstatlink' or 'diffstatnolink'
total = adds + removes
Steven Brown
web: provide the file number to the diffstat templates...
r14562 fileno += 1
yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
Steven Brown
web: include all files in the diffstat...
r14561 total=total, addpct=pct(adds), removepct=pct(removes),
timeless
py3: convert to next() function...
r29216 parity=next(parity))
Steven Brown
web: provide diffstat to the changeset page...
r14490
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 class sessionvars(object):
def __init__(self, vars, start='?'):
self.start = start
self.vars = vars
def __getitem__(self, key):
return self.vars[key]
def __setitem__(self, key, value):
self.vars[key] = value
def __copy__(self):
return sessionvars(copy.copy(self.vars), self.start)
def __iter__(self):
separator = self.start
Mads Kiilerich
hgweb: generate query strings with parameters sorted by key
r18367 for key, value in sorted(self.vars.iteritems()):
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 yield {'name': key, 'value': str(value), 'separator': separator}
separator = '&'
Augie Fackler
hgweb: fix hgweb_mod as well as hgwebdir_mod
r12691
Yuya Nishihara
hgweb: alias ui module as uimod...
r27007 class wsgiui(uimod.ui):
Augie Fackler
hgweb: fix hgweb_mod as well as hgwebdir_mod
r12691 # default termwidth breaks under mod_wsgi
def termwidth(self):
return 80
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162
def getwebsubs(repo):
websubtable = []
websubdefs = repo.ui.configitems('websub')
# we must maintain interhg backwards compatibility
websubdefs += repo.ui.configitems('interhg')
for key, pattern in websubdefs:
# grab the delimiter from the character after the "s"
unesc = pattern[1]
delim = re.escape(unesc)
# identify portions of the pattern, taking care to avoid escaped
# delimiters. the replace format and flags are optional, but
# delimiters are required.
match = re.match(
r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
% (delim, delim, delim), pattern)
if not match:
repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
% (key, pattern))
continue
# we need to unescape the delimiter for regexp and format
delim_re = re.compile(r'(?<!\\)\\%s' % delim)
regexp = delim_re.sub(unesc, match.group(1))
format = delim_re.sub(unesc, match.group(2))
# the pattern allows for 6 regexp flags, so set them if necessary
flagin = match.group(3)
flags = 0
if flagin:
for flag in flagin.upper():
flags |= re.__dict__[flag]
try:
regexp = re.compile(regexp, flags)
websubtable.append((regexp, format))
except re.error:
repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
% (key, regexp))
return websubtable