##// END OF EJS Templates
changegroup: remove reordering control (BC)...
changegroup: remove reordering control (BC) This logic - including the experimental bundle.reorder option - was originally added in a8e3931e3fb5 in 2011 and then later ported to changegroup.py. The intent of this option and associated logic is to control the ordering of revisions in deltagroups in changegroups. At the time it was implemented, only changegroup version 1 existed and generaldelta revlogs were just coming into the world. Changegroup version 1 requires that deltas be made against the last revision sent over the wire. Used with generaldelta, this created an impedance mismatch of sorts and resulted in changegroup producers spending a lot of time recomputing deltas. Revision reordering was introduced so outgoing revisions would be sent in "generaldelta order" and producers would be able to reuse internal deltas from storage. Later on, we introduced changegroup version 2. It supported denoting which revision a delta was against. So we no longer needed to sort outgoing revisions to ensure optimal delta generation from the producer. So, subsequent changegroup versions disabled reordering. We also later made the changelog not store deltas by default. And we also made the changelog send out deltas in storage order. Why we do this for changelog, I'm not sure. Maybe we want to preserve revision order across clones? It doesn't really matter for this commit. Fast forward to 2018. We want to abstract storage backends. And having changegroup code require knowledge about how deltas are stored internally interferes with that goal. This commit removes reordering control from changegroup generation. After this commit, the reordering behavior is: * The changelog is always sent out in storage order (no behavior change). * Non-changelog generaldelta revlogs are reordered to always be in DAG topological order (previously, generaldelta revlogs would be emitted in storage order for version 2 and 3 changegroups). * Non-changelog non-generaldelta revlogs are sent in storage order (no behavior change). * There exists no config option to override behavior. The big difference here is that generaldelta revlogs now *always* have their revisions sorted in DAG order before going out over the wire. This behavior was previously only done for changegroup version 1. Version 2 and version 3 changegroups disabled reordering because the interchange format supported encoding arbitrary delta parents, so reordering wasn't strictly necessary. I can think of a few significant implications for this change. Because changegroup receivers will now see non-changelog revisions in DAG order instead of storage order, the internal storage order of manifests and files may differ substantially between producer and consumer. I don't think this matters that much, since the storage order of manifests and files is largely hidden from users. Only the storage order of changelog matters (because `hg log` shows the changelog in storage order). I don't think there should be any controversy here. The reordering of revisions has implications for changegroup producers. Previously, generaldelta revlogs would be emitted in storage order. And in the common case, the internally-stored delta could effectively be copied from disk into the deltagroup delta. This meant that emitting delta groups for generaldelta revlogs would be mostly linear read I/O. This is desirable for performance. With us now reordering generaldelta revlog revisions in DAG order, the read operations may use more random I/O instead of sequential I/O. This could result in performance loss. But with the prevalence of SSDs and fast random I/O, I'm not too worried. (Note: the optimal emission order for revlogs is actually delta encoding order. But the changegroup code wasn't doing that before or after this change. We could potentially implement that in a later commit.) Changegroups in DAG order will have implications for receivers. Previously, receiving storage order might mean seeing a number of interleaved branches. This would mean long delta chains, sparse I/O, and possibly more fulltext revisions instead of deltas, blowing up storage storage. (This is the same set of problems that sparse revlogs aims to address.) With the producer now sending revisions in DAG order, the receiver also stores revisions in DAG order. That means revisions for the same DAG branch are all grouped together. And this should yield better storage outcomes. In other words, sending the reordered changegroup allows the receiver to have better storage order and for the producer to not propagate its (possibly sub-optimal) internal storage order. On the mozilla-unified repository, this change influences bundle generation: $ hg bundle -t none-v2 -a before: time: real 355.680 secs (user 256.790+0.000 sys 16.820+0.000) after: time: real 382.950 secs (user 281.700+0.000 sys 17.690+0.000) before: 7,150,228,967 bytes (uncompressed) after: 7,041,556,273 bytes (uncompressed) before: 1,669,063,234 bytes (zstd l=3) after: 1,628,598,830 bytes (zstd l=3) $ hg unbundle before: time: real 511.910 secs (user 466.750+0.000 sys 32.680+0.000) after: time: real 487.790 secs (user 443.940+0.000 sys 30.840+0.000) 00manifest.d size: source: 274,924,292 bytes before: 304,741,626 bytes after: 245,252,087 bytes .hg/store total file size: source: 2,649,133,490 before: 2,680,888,130 after: 2,627,875,673 We see the bundle size drop. That's probably because if a revlog internally isn't storing a delta, it will choose to delta against the last emitted revision. And on repos with interleaved branches (like mozilla-unified), the previous revision could be an unrelated branch and therefore be a large delta. But with this patch, the previous revision is likely p1 or p2 and a delta should be small. We also see the manifest size drop by ~50 MB. It's worth noting that the manifest actually *increased* in size by ~25 MB in the old strategy and decreased ~25 MB from its source in the new strategy. Again, my explanation for this is that the DAG ordering in the changegroup is resulting in better grouping of revisions in the receiver, which results in more compact delta chains and higher storage efficiency. Unbundle time also dropped. I suspect this is due to the revlog having to work less to compute deltas since the incoming deltas are more optimal. i.e. the receiver spends less time resolving fulltext revisions as incoming deltas bounce around between DAG branches and delta chains. We also see bundle generation time increase. This is not desirable. However, the regression is only significant on the original repository: if we generate a bundle from the repository created from the new, always reordered bundles, we're close to baseline (if not at it with expected noise): $ hg bundle -t none-v2 -a before (original): time: real 355.680 secs (user 256.790+0.000 sys 16.820+0.000) after (original): time: real 382.950 secs (user 281.700+0.000 sys 17.690+0.000) after (new repo): time: real 362.280 secs (user 260.300+0.000 sys 17.700+0.000) This regression is a bit worrying because it will impact serving canonical repositories (that don't have optimal internal storage unless they are reordered - possibly as part of running `hg debugupgraderepo`). However, this regression will only be noticed by very large changegroups. And I'm guessing/hoping that any repository that large is using clonebundles to mitigate server load. Again, sending DAG order isn't the optimal send order for servers: sending in storage-delta order is. But in order to enable storage-optimal send order, we'll need a storage API that handles sorting. Future commits will introduce such an API. Differential Revision: https://phab.mercurial-scm.org/D4721

File last commit:

r39830:4f44f747 default
r39897:db5501d9 default
Show More
webutil.py
814 lines | 26.4 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,
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665 HTTP_BAD_REQUEST,
Yuya Nishihara
hgweb: use absolute_import
r27046 HTTP_NOT_FOUND,
paritygen,
)
from .. import (
context,
Yuya Nishihara
diffutil: move the module out of utils package...
r38607 diffutil,
Yuya Nishihara
hgweb: use absolute_import
r27046 error,
match,
Denis Laxalde
mdiff: add a hunkinrange helper function...
r31808 mdiff,
av6
hgweb: explain instabilities of unstable changesets
r36973 obsutil,
Yuya Nishihara
hgweb: use absolute_import
r27046 patch,
pathutil,
Augie Fackler
webutil: use pycompat.bytestr() instead of str()...
r34808 pycompat,
Martin von Zweigbergk
hgweb: use revsymbol() for creating context from changeid...
r37351 scmutil,
Yuya Nishihara
hgweb: use absolute_import
r27046 templatefilters,
av6
hgweb: display fate of obsolete changesets...
r35501 templatekw,
Yuya Nishihara
hgweb: wrap {archives} with mappinglist...
r37533 templateutil,
Yuya Nishihara
hgweb: use absolute_import
r27046 ui as uimod,
util,
)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 from ..utils import (
stringutil,
)
Yuya Nishihara
hgweb: move archivespecs to webutil...
r37529 archivespecs = util.sortdict((
('zip', ('application/zip', 'zip', '.zip', None)),
('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
))
Yuya Nishihara
hgweb: forward archivelist() of hgweb to webutil...
r37532 def archivelist(ui, nodeid, url=None):
David Demelier
config: rename allow_archive to allow-archive...
r38215 allowed = ui.configlist('web', 'allow-archive', untrusted=True)
Yuya Nishihara
hgweb: move archivelist() of hgwebdir to webutil
r37531 archives = []
for typ, spec in archivespecs.iteritems():
if typ in allowed or ui.configbool('web', 'allow' + typ,
untrusted=True):
archives.append({
'type': typ,
'extension': spec[2],
'node': nodeid,
'url': url,
})
Yuya Nishihara
hgweb: wrap {archives} with mappinglist...
r37533 return templateutil.mappinglist(archives)
Yuya Nishihara
hgweb: move archivelist() of hgwebdir to webutil
r37531
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 def up(p):
Augie Fackler
hgweb: fix up trailing slash detection on Python 3...
r36731 if p[0:1] != "/":
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 p = "/" + p
Augie Fackler
hgweb: fix up trailing slash detection on Python 3...
r36731 if p[-1:] == "/":
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 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
Gregory Szorc
py3: add __bool__ to every class defining __nonzero__...
r31476 __bool__ = __nonzero__
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 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:
Yuya Nishihara
hgweb: wrap {changenav} and {nav} with mappinglist
r37716 - a single element mappinglist
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 - containing a dictionary with a `before` and `after` key
Yuya Nishihara
hgweb: make revnav.gen() simply build a list of mappings by one pass...
r37715 - values are dictionaries with `label` and `node` keys
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 """
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406 if not self:
# empty repo
Yuya Nishihara
hgweb: wrap {changenav} and {nav} with mappinglist
r37716 return templateutil.mappinglist([
{'before': templateutil.mappinglist([]),
'after': templateutil.mappinglist([])},
])
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()
Yuya Nishihara
hgweb: make revnav.gen() simply build a list of mappings by one pass...
r37715 navbefore = [{'label': '(%i)' % first, 'node': 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:
Yuya Nishihara
hgweb: make revnav.gen() simply build a list of mappings by one pass...
r37715 navafter.append({'label': '+%d' % abs(rev - pos),
'node': self.hex(rev)})
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 if 0 < rev < pos:
Yuya Nishihara
hgweb: make revnav.gen() simply build a list of mappings by one pass...
r37715 navbefore.append({'label': '-%d' % abs(rev - pos),
'node': self.hex(rev)})
Nicolas Dumazet
hgweb: changenav: separate pages before and after the current position...
r10254
Yuya Nishihara
hgweb: make revnav.gen() simply build a list of mappings by one pass...
r37715 navafter.append({'label': 'tip', 'node': 'tip'})
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403
Yuya Nishihara
hgweb: wrap {changenav} and {nav} with mappinglist
r37716 # TODO: maybe this can be a scalar object supporting tomap()
return templateutil.mappinglist([
{'before': templateutil.mappinglist(navbefore),
'after': templateutil.mappinglist(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)))
Yuya Nishihara
hgweb: extract a generator function of _siblings class...
r37717 # TODO: maybe this can be a wrapper class for changectx/filectx list, which
# yields {'ctx': ctx}
Yuya Nishihara
hgweb: convert _siblings to a factory function of mappinggenerator...
r37718 def _ctxsgen(context, ctxs):
Yuya Nishihara
hgweb: extract a generator function of _siblings class...
r37717 for s in ctxs:
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
Yuya Nishihara
hgweb: convert _siblings to a factory function of mappinggenerator...
r37718 def _siblings(siblings=None, hiderev=None):
if siblings is None:
siblings = []
siblings = [s for s in siblings if s.node() != nullid]
if len(siblings) == 1 and siblings[0].rev() == hiderev:
siblings = []
return templateutil.mappinggenerator(_ctxsgen, args=(siblings,))
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391 def difffeatureopts(req, ui, section):
Boris Feld
webutil: pass a diffopts object to context.diff
r38585 diffopts = diffutil.difffeatureopts(ui, untrusted=True,
section=section, whitespace=True)
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391
for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 v = req.qsparams.get(k)
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391 if v is not None:
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 v = stringutil.parsebool(v)
Gregory Szorc
hgweb: use parsebool for parsing diff query string options...
r34404 setattr(diffopts, k, v if v is not None else True)
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391
return diffopts
def annotate(req, fctx, ui):
diffopts = difffeatureopts(req, ui, 'annotate')
Yuya Nishihara
annotate: drop linenumber flag from fctx.annotate() (API)...
r37083 return fctx.annotate(follow=True, diffopts=diffopts)
Jun Wu
hgweb: make fctx.annotate a separated function so it could be wrapped...
r30081
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:
Yuya Nishihara
hgweb: wrap {rename} with mappinglist...
r37921 return templateutil.mappinglist([{'file': r[0], 'node': hex(r[1])}])
return templateutil.mappinglist([])
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
def nodetagsdict(repo, node):
Yuya Nishihara
hgweb: wrap {tags} by hybridlist()...
r37922 return templateutil.hybridlist(repo.nodetags(node), name='name')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596 def nodebookmarksdict(repo, node):
Yuya Nishihara
hgweb: wrap {bookmarks} by hybridlist()...
r37923 return templateutil.hybridlist(repo.nodebookmarks(node), name='name')
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596
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():
Yuya Nishihara
hgweb: wrap {branches} by hybridlist()...
r37924 branches.append(branch)
return templateutil.hybridlist(branches, name='name')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
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():
Yuya Nishihara
hgweb: wrap {inbranch} by hybridlist()...
r37925 branches.append(branch)
return templateutil.hybridlist(branches, name='name')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
def nodebranchnodefault(ctx):
branches = []
branch = ctx.branch()
if branch != 'default':
Yuya Nishihara
hgweb: wrap {branch} and {changesetbranch} by hybridlist()...
r37926 branches.append(branch)
return templateutil.hybridlist(branches, name='name')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Yuya Nishihara
hgweb: wrap {changelogtag}, {changesettag}, and {changesetbookmark}...
r37930 def _nodenamesgen(context, f, node, name):
for t in f(node):
yield {name: t}
Yuya Nishihara
hgweb: drop tmpl argument from webutil.showtag() and showbookmark()...
r37931 def showtag(repo, t1, node=nullid):
Yuya Nishihara
hgweb: wrap {changelogtag}, {changesettag}, and {changesetbookmark}...
r37930 args = (repo.nodetags, node, 'tag')
return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Yuya Nishihara
hgweb: drop tmpl argument from webutil.showtag() and showbookmark()...
r37931 def showbookmark(repo, t1, node=nullid):
Yuya Nishihara
hgweb: wrap {changelogtag}, {changesettag}, and {changesetbookmark}...
r37930 args = (repo.nodebookmarks, node, 'bookmark')
return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1)
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596
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())
Yuya Nishihara
hgweb: wrap {branches} and {entries} of branches with mappinggenerator...
r37932 def entries(context):
av6
hgweb: move branchentries code from webcommands to webutil
r26129 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()
}
Yuya Nishihara
hgweb: wrap {branches} and {entries} of branches with mappinggenerator...
r37932 return templateutil.mappinggenerator(entries)
av6
hgweb: move branchentries code from webcommands to webutil
r26129
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def cleanpath(repo, path):
path = path.lstrip('/')
Yuya Nishihara
hgweb: do not audit URL path as working-directory path...
r39507 auditor = pathutil.pathauditor(repo.root, realfs=False)
return pathutil.canonpath(repo.root, '', path, auditor=auditor)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
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"
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'node' in req.qsparams:
changeid = req.qsparams['node']
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):]
Martin von Zweigbergk
hgweb: use revsymbol() for creating context from changeid...
r37351 return scmutil.revsymbol(repo, changeid)
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991
def basechangectx(repo, req):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'node' in req.qsparams:
changeid = req.qsparams['node']
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]
Martin von Zweigbergk
hgweb: use revsymbol() for creating context from changeid...
r37351 return scmutil.revsymbol(repo, changeid)
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991
return None
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def filectx(repo, req):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'file' not in req.qsparams:
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 path = cleanpath(repo, req.qsparams['file'])
if 'node' in req.qsparams:
changeid = req.qsparams['node']
elif 'filenode' in req.qsparams:
changeid = req.qsparams['filenode']
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:
Martin von Zweigbergk
hgweb: use revsymbol() for creating context from changeid...
r37351 fctx = scmutil.revsymbol(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
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665 def linerange(req):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 linerange = req.qsparams.getall('linerange')
Gregory Szorc
hgweb: perform all parameter lookup via qsparams...
r36881 if not linerange:
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665 return None
if len(linerange) > 1:
raise ErrorResponse(HTTP_BAD_REQUEST,
'redundant linerange parameter')
try:
fromline, toline = map(int, linerange[0].split(':', 1))
except ValueError:
raise ErrorResponse(HTTP_BAD_REQUEST,
'invalid linerange parameter')
try:
return util.processlinerange(fromline, toline)
except error.ParseError as exc:
Augie Fackler
py3: get bytes-repr of network errors portably...
r36272 raise ErrorResponse(HTTP_BAD_REQUEST, pycompat.bytestr(exc))
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665
def formatlinerange(fromline, toline):
return '%d:%d' % (fromline + 1, toline)
Yuya Nishihara
hgweb: wrap {succsandmarkers} with mappinggenerator...
r37933 def _succsandmarkersgen(context, mapping):
Yuya Nishihara
templatekw: switch obsfate-related template keywords to new API
r36612 repo = context.resource(mapping, 'repo')
Yuya Nishihara
templatekw: fix return type of {succsandmarkers} (BC)...
r37521 itemmappings = templatekw.showsuccsandmarkers(context, mapping)
for item in itemmappings.tovalue(context, mapping):
av6
hgweb: link to successors of obsoleted changesets...
r35502 item['successors'] = _siblings(repo[successor]
for successor in item['successors'])
yield item
av6
hgweb: display fate of obsolete changesets...
r35501
Yuya Nishihara
hgweb: wrap {succsandmarkers} with mappinggenerator...
r37933 def succsandmarkers(context, mapping):
return templateutil.mappinggenerator(_succsandmarkersgen, args=(mapping,))
Yuya Nishihara
templatekw: switch obsfate-related template keywords to new API
r36612 # teach templater succsandmarkers is switched to (context, mapping) API
Yuya Nishihara
templatekw: stop using _showlist() which is about to be deprecated...
r37087 succsandmarkers._requires = {'repo', 'ctx'}
Yuya Nishihara
templatekw: switch obsfate-related template keywords to new API
r36612
Yuya Nishihara
hgweb: wrap {whyunstable} with mappinggenerator...
r37934 def _whyunstablegen(context, mapping):
av6
hgweb: explain instabilities of unstable changesets
r36973 repo = context.resource(mapping, 'repo')
ctx = context.resource(mapping, 'ctx')
entries = obsutil.whyunstable(repo, ctx)
for entry in entries:
if entry.get('divergentnodes'):
entry['divergentnodes'] = _siblings(entry['divergentnodes'])
yield entry
Yuya Nishihara
hgweb: wrap {whyunstable} with mappinggenerator...
r37934 def whyunstable(context, mapping):
return templateutil.mappinggenerator(_whyunstablegen, args=(mapping,))
Yuya Nishihara
templatekw: stop using _showlist() which is about to be deprecated...
r37087 whyunstable._requires = {'repo', 'ctx'}
av6
hgweb: explain instabilities of unstable changesets
r36973
Yuya Nishihara
hgweb: mark all lambda template keywords as new-style function...
r38965 # helper to mark a function as a new-style template keyword; can be removed
# once old-style function gets unsupported and new-style becomes the default
def _kwfunc(f):
f._requires = ()
return f
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 def commonentry(repo, ctx):
Yuya Nishihara
hgweb: use scmutil.binnode() to translate None to wdir hash (issue5988)...
r39830 node = scmutil.binnode(ctx)
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 return {
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 # TODO: perhaps ctx.changectx() should be assigned if ctx is a
# filectx, but I'm not pretty sure if that would always work because
# fctx.parents() != fctx.changectx.parents() for example.
'ctx': ctx,
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 'rev': ctx.rev(),
'node': hex(node),
'author': ctx.user(),
'desc': ctx.description(),
'date': ctx.date(),
'extra': ctx.extra(),
'phase': ctx.phasestr(),
av6
context: add obsolete() method to basefilectx...
r35087 'obsolete': ctx.obsolete(),
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 'succsandmarkers': succsandmarkers,
Yuya Nishihara
hgweb: wrap {instabilities} by hybridlist()...
r37935 'instabilities': templateutil.hybridlist(ctx.instabilities(),
name='instability'),
av6
hgweb: explain instabilities of unstable changesets
r36973 'whyunstable': whyunstable,
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 'branch': nodebranchnodefault(ctx),
'inbranch': nodeinbranch(repo, ctx),
'branches': nodebranchdict(repo, ctx),
'tags': nodetagsdict(repo, node),
'bookmarks': nodebookmarksdict(repo, node),
Yuya Nishihara
hgweb: mark all lambda template keywords as new-style function...
r38965 'parent': _kwfunc(lambda context, mapping: parents(ctx)),
'child': _kwfunc(lambda context, mapping: children(ctx)),
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 }
Gregory Szorc
hgweb: don't redundantly pass templater with requestcontext (API)...
r36901 def changelistentry(web, ctx):
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745 '''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()
Yuya Nishihara
hgweb: use scmutil.binnode() to translate None to wdir hash (issue5988)...
r39830 n = scmutil.binnode(ctx)
Yuya Nishihara
hgweb: drop tmpl argument from webutil.showtag() and showbookmark()...
r37931 showtags = showtag(repo, 'changelogtag', n)
Yuya Nishihara
hgweb: remove unused argument 'tmpl' from listfilediffs()
r37973 files = listfilediffs(ctx.files(), n, web.maxfiles)
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 entry = commonentry(repo, ctx)
entry.update(
Yuya Nishihara
hgweb: mark all lambda template keywords as new-style function...
r38965 allparents=_kwfunc(lambda context, mapping: parents(ctx)),
parent=_kwfunc(lambda context, mapping: parents(ctx, rev - 1)),
child=_kwfunc(lambda context, mapping: children(ctx, rev + 1)),
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 changelogtag=showtags,
files=files,
)
return entry
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745
Gregory Szorc
hgweb: extract code for emitting multiple changelist records...
r38054 def changelistentries(web, revs, maxcount, parityfn):
"""Emit up to N records for an iterable of revisions."""
repo = web.repo
count = 0
for rev in revs:
if count >= maxcount:
break
count += 1
entry = changelistentry(web, repo[rev])
entry['parity'] = next(parityfn)
yield entry
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 def symrevorshortnode(req, ctx):
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'node' in req.qsparams:
return templatefilters.revescape(req.qsparams['node'])
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 else:
Yuya Nishihara
hgweb: use scmutil.binnode() to translate None to wdir hash (issue5988)...
r39830 return short(scmutil.binnode(ctx))
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602
Yuya Nishihara
hgweb: use template context to render {files} of changesetentry()...
r37970 def _listfilesgen(context, ctx, stripecount):
Yuya Nishihara
hgweb: extract generator of {files} from changesetentry()...
r37968 parity = paritygen(stripecount)
for blockno, f in enumerate(ctx.files()):
template = 'filenodelink' if f in ctx else 'filenolink'
Yuya Nishihara
hgweb: use template context to render {files} of changesetentry()...
r37970 yield context.process(template, {
Yuya Nishihara
hgweb: extract generator of {files} from changesetentry()...
r37968 'node': ctx.hex(),
'file': f,
'blockno': blockno + 1,
'parity': next(parity),
})
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 def changesetentry(web, ctx):
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 '''Obtain a dictionary to be used to render the "changeset" template.'''
Yuya Nishihara
hgweb: use scmutil.binnode() to translate None to wdir hash (issue5988)...
r39830 showtags = showtag(web.repo, 'changesettag', scmutil.binnode(ctx))
showbookmarks = showbookmark(web.repo, 'changesetbookmark',
scmutil.binnode(ctx))
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 showbranch = nodebranchnodefault(ctx)
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 basectx = basechangectx(web.repo, web.req)
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 if basectx is None:
basectx = ctx.p1()
Boris Feld
configitems: register the 'web.style' config
r34243 style = web.config('web', 'style')
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 if 'style' in web.req.qsparams:
style = web.req.qsparams['style']
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
Gregory Szorc
hgweb: don't redundantly pass templater with requestcontext (API)...
r36901 diff = diffs(web, ctx, basectx, None, style)
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
parity = paritygen(web.stripecount)
Yuya Nishihara
hgweb: pass ui to diffstatgen() explicitly
r38604 diffstatsgen = diffstatgen(web.repo.ui, ctx, basectx)
Yuya Nishihara
hgweb: drop unused argument 'tmpl' from webutil.diffstat()
r38070 diffstats = diffstat(ctx, diffstatsgen, parity)
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
return dict(
diff=diff,
Gregory Szorc
hgweb: pass modern request type into various webutil functions (API)...
r36902 symrev=symrevorshortnode(web.req, ctx),
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 basenode=basectx.hex(),
changesettag=showtags,
changesetbookmark=showbookmarks,
changesetbranch=showbranch,
Yuya Nishihara
hgweb: wrap {files} of changesetentry() with mappedgenerator...
r37969 files=templateutil.mappedgenerator(_listfilesgen,
Yuya Nishihara
hgweb: use template context to render {files} of changesetentry()...
r37970 args=(ctx, web.stripecount)),
Yuya Nishihara
hgweb: mark all lambda template keywords as new-style function...
r38965 diffsummary=_kwfunc(lambda context, mapping: diffsummary(diffstatsgen)),
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 diffstat=diffstats,
archives=web.archivelist(ctx.hex()),
Pulkit Goyal
py3: use pycompat.strkwargs to convert kwargs keys to str...
r36452 **pycompat.strkwargs(commonentry(web.repo, ctx)))
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
Yuya Nishihara
hgweb: use template context to render {files} of changelist entries...
r37972 def _listfilediffsgen(context, files, node, max):
Dirkjan Ochtman
hgweb: move another utility function into the webutil module
r7311 for f in files[:max]:
Yuya Nishihara
hgweb: use template context to render {files} of changelist entries...
r37972 yield context.process('filedifflink', {'node': hex(node), 'file': f})
Dirkjan Ochtman
hgweb: move another utility function into the webutil module
r7311 if len(files) > max:
Yuya Nishihara
hgweb: use template context to render {files} of changelist entries...
r37972 yield context.process('fileellipses', {})
Dirkjan Ochtman
hgweb: move another utility function into the webutil module
r7311
Yuya Nishihara
hgweb: remove unused argument 'tmpl' from listfilediffs()
r37973 def listfilediffs(files, node, max):
Yuya Nishihara
hgweb: wrap {files} of changelist entries with mappedgenerator...
r37971 return templateutil.mappedgenerator(_listfilediffsgen,
Yuya Nishihara
hgweb: use template context to render {files} of changelist entries...
r37972 args=(files, node, max))
Yuya Nishihara
hgweb: wrap {files} of changelist entries with mappedgenerator...
r37971
Yuya Nishihara
hgweb: use template context to render {lines} of {diff}...
r38006 def _prettyprintdifflines(context, lines, blockno, lineidprefix):
Yuya Nishihara
hgweb: move prettyprintlines() closure out of diffs()...
r38004 for lineno, l in enumerate(lines, 1):
difflineno = "%d.%d" % (blockno, lineno)
if l.startswith('+'):
ltype = "difflineplus"
elif l.startswith('-'):
ltype = "difflineminus"
elif l.startswith('@'):
ltype = "difflineat"
else:
ltype = "diffline"
Yuya Nishihara
hgweb: use template context to render {lines} of {diff}...
r38006 yield context.process(ltype, {
Yuya Nishihara
hgweb: move prettyprintlines() closure out of diffs()...
r38004 'line': l,
'lineno': lineno,
'lineid': lineidprefix + "l%s" % difflineno,
'linenumber': "% 8s" % difflineno,
})
Yuya Nishihara
hgweb: convert {diff} to a mappinggenerator with named template...
r38007 def _diffsgen(context, repo, ctx, basectx, files, style, stripecount,
linerange, lineidprefix):
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)
Denis Laxalde
hgweb: explictly pass basectx in webutil.diffs...
r31082 node1 = basectx.node()
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 node2 = ctx.node()
Yuya Nishihara
hgweb: convert {diff} to a mappinggenerator with named template...
r38007 parity = paritygen(stripecount)
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
Denis Laxalde
diff: also yield file context objects in patch.trydiff() (API)...
r34856 for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 if style != 'raw':
header = header[1:]
lines = [h + '\n' for h in header]
for hunkrange, hunklines in hunks:
Denis Laxalde
hgweb: add a 'linerange' parameter to webutil.diffs()...
r31666 if linerange is not None and hunkrange is not None:
s1, l1, s2, l2 = hunkrange
Denis Laxalde
mdiff: add a hunkinrange helper function...
r31808 if not mdiff.hunkinrange((s2, l2), linerange):
Denis Laxalde
hgweb: add a 'linerange' parameter to webutil.diffs()...
r31666 continue
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 lines.extend(hunklines)
if lines:
Yuya Nishihara
hgweb: wrap {lines} of {diff} with mappedgenerator...
r38005 l = templateutil.mappedgenerator(_prettyprintdifflines,
Yuya Nishihara
hgweb: use template context to render {lines} of {diff}...
r38006 args=(lines, blockno,
Yuya Nishihara
hgweb: wrap {lines} of {diff} with mappedgenerator...
r38005 lineidprefix))
Yuya Nishihara
hgweb: convert {diff} to a mappinggenerator with named template...
r38007 yield {
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 'parity': next(parity),
'blockno': blockno,
Yuya Nishihara
hgweb: wrap {lines} of {diff} with mappedgenerator...
r38005 'lines': l,
Yuya Nishihara
hgweb: convert {diff} to a mappinggenerator with named template...
r38007 }
def diffs(web, ctx, basectx, files, style, linerange=None, lineidprefix=''):
args = (web.repo, ctx, basectx, files, style, web.stripecount,
linerange, lineidprefix)
return templateutil.mappinggenerator(_diffsgen, args=args, name='diffblock')
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 def _compline(type, leftlineno, leftline, rightlineno, rightline):
Yuya Nishihara
hgweb: move compline() closure out of compare()
r38008 lineid = leftlineno and ("l%d" % leftlineno) or ''
lineid += rightlineno and ("r%d" % rightlineno) or ''
llno = '%d' % leftlineno if leftlineno else ''
rlno = '%d' % rightlineno if rightlineno else ''
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 return {
Yuya Nishihara
hgweb: move compline() closure out of compare()
r38008 'type': type,
'lineid': lineid,
'leftlineno': leftlineno,
'leftlinenumber': "% 6s" % llno,
'leftline': leftline or '',
'rightlineno': rightlineno,
'rightlinenumber': "% 6s" % rlno,
'rightline': rightline or '',
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 }
Yuya Nishihara
hgweb: move compline() closure out of compare()
r38008
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 def _getcompblockgen(context, leftlines, rightlines, opcodes):
Yuya Nishihara
hgweb: move getblock() closure out of compare()...
r38009 for type, llo, lhi, rlo, rhi in opcodes:
len1 = lhi - llo
len2 = rhi - rlo
count = min(len1, len2)
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(count):
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 yield _compline(type=type,
Yuya Nishihara
hgweb: move getblock() closure out of compare()...
r38009 leftlineno=llo + i + 1,
leftline=leftlines[llo + i],
rightlineno=rlo + i + 1,
rightline=rightlines[rlo + i])
if len1 > len2:
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(llo + count, lhi):
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 yield _compline(type=type,
Yuya Nishihara
hgweb: move getblock() closure out of compare()...
r38009 leftlineno=i + 1,
leftline=leftlines[i],
rightlineno=None,
rightline=None)
elif len2 > len1:
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(rlo + count, rhi):
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 yield _compline(type=type,
Yuya Nishihara
hgweb: move getblock() closure out of compare()...
r38009 leftlineno=None,
leftline=None,
rightlineno=i + 1,
rightline=rightlines[i])
wujek srujek
hgweb: side-by-side comparison functionality...
r17202
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 def _getcompblock(leftlines, rightlines, opcodes):
args = (leftlines, rightlines, opcodes)
return templateutil.mappinggenerator(_getcompblockgen, args=args,
name='comparisonline')
Yuya Nishihara
hgweb: convert {comparison} to a mappinggenerator with named template...
r38012 def _comparegen(context, contextnum, leftlines, rightlines):
Yuya Nishihara
hgweb: move getblock() closure out of compare()...
r38009 '''Generator function that provides side-by-side comparison data.'''
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 s = difflib.SequenceMatcher(None, leftlines, rightlines)
Yuya Nishihara
hgweb: rename 'context' argument of webutil.compare() to avoid name conflicts...
r38011 if contextnum < 0:
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 l = _getcompblock(leftlines, rightlines, s.get_opcodes())
Yuya Nishihara
hgweb: convert {comparison} to a mappinggenerator with named template...
r38012 yield {'lines': l}
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 else:
Yuya Nishihara
hgweb: rename 'context' argument of webutil.compare() to avoid name conflicts...
r38011 for oc in s.get_grouped_opcodes(n=contextnum):
Yuya Nishihara
hgweb: convert comparison {lines} to a mappinggenerator with named template...
r38010 l = _getcompblock(leftlines, rightlines, oc)
Yuya Nishihara
hgweb: convert {comparison} to a mappinggenerator with named template...
r38012 yield {'lines': l}
Yuya Nishihara
hgweb: drop unused argument 'tmpl' from webutil.compare()
r38013 def compare(contextnum, leftlines, rightlines):
Yuya Nishihara
hgweb: convert {comparison} to a mappinggenerator with named template...
r38012 args = (contextnum, leftlines, rightlines)
return templateutil.mappinggenerator(_comparegen, args=args,
name='comparisonblock')
wujek srujek
hgweb: side-by-side comparison functionality...
r17202
Yuya Nishihara
hgweb: pass ui to diffstatgen() explicitly
r38604 def diffstatgen(ui, 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
Yuya Nishihara
hgweb: pass ui to diffstatgen() explicitly
r38604 diffopts = patch.diffopts(ui, {'noprefix': False})
Yuya Nishihara
hgweb: disable diff.noprefix option for diffstat...
r35445 stats = patch.diffstatdata(
Boris Feld
webutil: pass a diffopts object to context.diff
r38585 util.iterlines(ctx.diff(basectx, opts=diffopts)))
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)
Yuya Nishihara
hgweb: use template context to render {diffstat}...
r38069 def _diffstattmplgen(context, ctx, statgen, parity):
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:
av6
hgweb: rewrite `template = A and B or C` to be a proper ternary operator
r35315 template = 'diffstatlink' if filename in files else 'diffstatnolink'
Steven Brown
web: include all files in the diffstat...
r14561 total = adds + removes
Steven Brown
web: provide the file number to the diffstat templates...
r14562 fileno += 1
Yuya Nishihara
hgweb: use template context to render {diffstat}...
r38069 yield context.process(template, {
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 'node': ctx.hex(),
'file': filename,
'fileno': fileno,
'total': total,
'addpct': pct(adds),
'removepct': pct(removes),
'parity': next(parity),
})
Steven Brown
web: provide diffstat to the changeset page...
r14490
Yuya Nishihara
hgweb: drop unused argument 'tmpl' from webutil.diffstat()
r38070 def diffstat(ctx, statgen, parity):
Yuya Nishihara
hgweb: wrap {diffstat} with mappedgenerator...
r38068 '''Return a diffstat template for each file in the diff.'''
Yuya Nishihara
hgweb: use template context to render {diffstat}...
r38069 args = (ctx, statgen, parity)
Yuya Nishihara
hgweb: wrap {diffstat} with mappedgenerator...
r38068 return templateutil.mappedgenerator(_diffstattmplgen, args=args)
Yuya Nishihara
hgweb: lift {sessionvars} to a wrapped type...
r37714 class sessionvars(templateutil.wrapped):
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 def __init__(self, vars, start='?'):
Yuya Nishihara
hgweb: prefix private variables of sessionvars with '_'
r37712 self._start = start
self._vars = vars
Yuya Nishihara
hgweb: make sessionvars class less dense
r37713
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 def __getitem__(self, key):
Yuya Nishihara
hgweb: prefix private variables of sessionvars with '_'
r37712 return self._vars[key]
Yuya Nishihara
hgweb: make sessionvars class less dense
r37713
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 def __setitem__(self, key, value):
Yuya Nishihara
hgweb: prefix private variables of sessionvars with '_'
r37712 self._vars[key] = value
Yuya Nishihara
hgweb: make sessionvars class less dense
r37713
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 def __copy__(self):
Yuya Nishihara
hgweb: prefix private variables of sessionvars with '_'
r37712 return sessionvars(copy.copy(self._vars), self._start)
Yuya Nishihara
hgweb: make sessionvars class less dense
r37713
Yuya Nishihara
templater: abstract ifcontains() over wrapped types...
r38286 def contains(self, context, mapping, item):
item = templateutil.unwrapvalue(context, mapping, item)
return item in self._vars
Yuya Nishihara
templater: promote getmember() to an interface of wrapped types
r38261 def getmember(self, context, mapping, key):
Yuya Nishihara
templater: resolve type of dict key in getmember()...
r38262 key = templateutil.unwrapvalue(context, mapping, key)
Yuya Nishihara
templater: promote getmember() to an interface of wrapped types
r38261 return self._vars.get(key)
Yuya Nishihara
templater: abstract min/max away...
r38284 def getmin(self, context, mapping):
raise error.ParseError(_('not comparable'))
def getmax(self, context, mapping):
raise error.ParseError(_('not comparable'))
Yuya Nishihara
templater: introduce filter() function to remove empty items from list...
r38467 def filter(self, context, mapping, select):
# implement if necessary
raise error.ParseError(_('not filterable'))
Yuya Nishihara
hgweb: lift {sessionvars} to a wrapped type...
r37714 def itermaps(self, context):
Yuya Nishihara
hgweb: prefix private variables of sessionvars with '_'
r37712 separator = self._start
for key, value in sorted(self._vars.iteritems()):
Augie Fackler
webutil: use pycompat.bytestr() instead of str()...
r34808 yield {'name': key,
'value': pycompat.bytestr(value),
'separator': separator,
}
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 separator = '&'
Augie Fackler
hgweb: fix hgweb_mod as well as hgwebdir_mod
r12691
Yuya Nishihara
hgweb: lift {sessionvars} to a wrapped type...
r37714 def join(self, context, mapping, sep):
# could be '{separator}{name}={value|urlescape}'
raise error.ParseError(_('not displayable without template'))
def show(self, context, mapping):
return self.join(context, '')
Yuya Nishihara
templater: abstract truth testing to fix {if(list_of_empty_strings)}...
r38308 def tobool(self, context, mapping):
return bool(self._vars)
Yuya Nishihara
hgweb: lift {sessionvars} to a wrapped type...
r37714 def tovalue(self, context, mapping):
return self._vars
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"
Pulkit Goyal
py3: slice over bytes to prevent getting ascii values...
r36198 unesc = pattern[1:2]
Augie Fackler
cleanup: migrate from re.escape to stringutil.reescape...
r38494 delim = stringutil.reescape(unesc)
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162
# 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(
Pulkit Goyal
py3: add b'' to make sure regex pattern are bytes in hgweb/webutil.py...
r36201 br'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 % (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
Pulkit Goyal
py3: add b'' to make sure regex pattern are bytes in hgweb/webutil.py...
r36201 delim_re = re.compile(br'(?<!\\)\\%s' % delim)
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 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
av6
hgweb: reuse graph node-related functions from templates...
r37928
def getgraphnode(repo, ctx):
return (templatekw.getgraphnodecurrent(repo, ctx) +
templatekw.getgraphnodesymbol(ctx))