##// END OF EJS Templates
merge: make in-memory changes visible to external update hooks...
merge: make in-memory changes visible to external update hooks 51844b8b5017 (while 3.4 code-freeze) made all 'update' hooks run after releasing wlock for visibility of in-memory dirstate changes. But this breaks paired invocation of 'preupdate' and 'update' hooks. For example, 'hg backout --merge' for TARGET revision, which isn't parent of CURRENT, consists of steps below: 1. update from CURRENT to TARGET 2. commit BACKOUT revision, which backs TARGET out 3. update from BACKOUT to CURRENT 4. merge TARGET into CURRENT Then, we expects hooks to run in the order below: - 'preupdate' on CURRENT for (1) - 'update' on TARGET for (1) - 'preupdate' on BACKOUT for (3) - 'update' on CURRENT for (3) - 'preupdate' on TARGET for (4) - 'update' on CURRENT/TARGET for (4) But hooks actually run in the order below: - 'preupdate' on CURRENT for (1) - 'preupdate' on BACKOUT for (3) - 'preupdate' on TARGET for (4) - 'update' on TARGET for (1), but actually on CURRENT/TARGET - 'update' on CURRENT for (3), but actually on CURRENT/TARGET - 'update' on CURRENT for (4), but actually on CURRENT/TARGET Root cause of the issue focused by 51844b8b5017 is that external 'update' hook process can't view in-memory changes (especially, of dirstate), because they aren't written out until the end of transaction (or wlock). Now, hooks can be invoked just after updating, because previous patches made in-memory changes visible to external process. This patch may break backward compatibility from the point of view of "scheduling hook execution", but should be reasonable because 'update' hooks had been executed in this order before 3.4. This patch tests "hg backout" and "hg unshelve", because the former activates the transaction before 'update' hook invocation, but the former doesn't.

File last commit:

r26162:268b3977 default
r26752:949e8c62 default
Show More
webutil.py
584 lines | 18.7 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
av6
templates: introduce revescape filter for escaping symbolic revisions...
r25778 import os, copy
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 import re
FUJIWARA Katsunori
hgweb: show as same parents as "hg parents -r REV FILE" in pages for file...
r21122 from mercurial import match, patch, error, ui, util, pathutil, context
Steven Brown
web: provide diff summary to the changeset page...
r14570 from mercurial.i18n import _
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 from mercurial.node import hex, nullid, short
av6
templates: introduce revescape filter for escaping symbolic revisions...
r25778 from mercurial.templatefilters import revescape
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 from common import ErrorResponse, paritygen
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 from common import HTTP_NOT_FOUND
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 import difflib
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:
return iter(self._revlog).next()
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)))
Pierre-Yves David
hgweb: introduction a filerevnav subclass...
r18408
Dirkjan Ochtman
hgweb: simplify parents/children generation code
r7671 def _siblings(siblings=[], hiderev=None):
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 siblings = [s for s in siblings if s.node() != nullid]
if len(siblings) == 1 and siblings[0].rev() == hiderev:
return
for s in siblings:
Alexander Solovyov
drop {short,hex}(ctx.node()) calls in favor of ctx methods
r14055 d = {'node': s.hex(), 'rev': s.rev()}
Dirkjan Ochtman
hgweb: pass more information about parent/child csets to templates
r7294 d['user'] = s.user()
d['date'] = s.date()
d['description'] = s.description()
Dirkjan Ochtman
hgweb: expose sibling branches to templater
r7717 d['branch'] = s.branch()
Augie Fackler
hgweb: move remaining hasattr calls to safehasattr
r14957 if util.safehasattr(s, 'path'):
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 d['file'] = s.path()
yield d
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 {
'parity': parity.next(),
'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:
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 man = repo.manifest
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
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)
return {
"author": ctx.user(),
"parent": parents(ctx, rev - 1),
"child": children(ctx, rev + 1),
"changelogtag": showtags,
"desc": ctx.description(),
"extra": ctx.extra(),
"date": ctx.date(),
"files": files,
"rev": rev,
"node": hex(n),
"tags": nodetagsdict(repo, n),
"bookmarks": nodebookmarksdict(repo, n),
"inbranch": nodeinbranch(repo, ctx),
"branches": nodebranchdict(repo, ctx)
}
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 def symrevorshortnode(req, ctx):
if 'node' in req.form:
av6
templates: introduce revescape filter for escaping symbolic revisions...
r25778 return 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,
parity=parity.next()))
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,
rev=ctx.rev(),
node=ctx.hex(),
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 parent=tuple(parents(ctx)),
child=children(ctx),
basenode=basectx.hex(),
changesettag=showtags,
changesetbookmark=showbookmarks,
changesetbranch=showbranch,
author=ctx.user(),
desc=ctx.description(),
extra=ctx.extra(),
date=ctx.date(),
Gregory Szorc
hgweb: add phase to {changeset} template...
r24564 phase=ctx.phasestr(),
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 files=files,
diffsummary=lambda **x: diffsummary(diffstatsgen),
diffstat=diffstats,
archives=web.archivelist(ctx.hex()),
tags=nodetagsdict(web.repo, ctx.node()),
bookmarks=nodebookmarksdict(web.repo, ctx.node()),
Anton Shestakov
hgweb: remove an extra call to nodebranchnodefault() in changesetentry()...
r25278 branch=showbranch,
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 inbranch=nodeinbranch(web.repo, ctx),
branches=nodebranchdict(web.repo, ctx))
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:
Paul Boddie
hgweb: add block numbers to diff regions and related links...
r16308 blockno = blockcount.next()
yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
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)
Paul Boddie
hgweb: add block numbers to diff regions and related links...
r16308 blockno = blockcount.next()
yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
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.'''
stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
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.'''
stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
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),
parity=parity.next())
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
class wsgiui(ui.ui):
# 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