support.py
136 lines
| 4.0 KiB
| text/x-python
|
PythonLexer
|
r39243 | # Copyright 2016-present Facebook. All Rights Reserved. | ||
# | ||||
# support: fastannotate support for hgweb, and filectx | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
|
r43359 | from mercurial.pycompat import getattr | ||
|
r39243 | from mercurial import ( | ||
context as hgcontext, | ||||
dagop, | ||||
extensions, | ||||
hgweb, | ||||
patch, | ||||
util, | ||||
) | ||||
from . import ( | ||||
context, | ||||
revmap, | ||||
) | ||||
|
r43346 | |||
|
r39243 | class _lazyfctx(object): | ||
"""delegates to fctx but do not construct fctx when unnecessary""" | ||||
def __init__(self, repo, node, path): | ||||
self._node = node | ||||
self._path = path | ||||
self._repo = repo | ||||
def node(self): | ||||
return self._node | ||||
def path(self): | ||||
return self._path | ||||
@util.propertycache | ||||
def _fctx(self): | ||||
return context.resolvefctx(self._repo, self._node, self._path) | ||||
def __getattr__(self, name): | ||||
return getattr(self._fctx, name) | ||||
|
r43346 | |||
|
r39243 | def _convertoutputs(repo, annotated, contents): | ||
"""convert fastannotate outputs to vanilla annotate format""" | ||||
# fastannotate returns: [(nodeid, linenum, path)], [linecontent] | ||||
# convert to what fctx.annotate returns: [annotateline] | ||||
results = [] | ||||
fctxmap = {} | ||||
annotateline = dagop.annotateline | ||||
for i, (hsh, linenum, path) in enumerate(annotated): | ||||
if (hsh, path) not in fctxmap: | ||||
fctxmap[(hsh, path)] = _lazyfctx(repo, hsh, path) | ||||
# linenum: the user wants 1-based, we have 0-based. | ||||
lineno = linenum + 1 | ||||
fctx = fctxmap[(hsh, path)] | ||||
line = contents[i] | ||||
results.append(annotateline(fctx=fctx, lineno=lineno, text=line)) | ||||
return results | ||||
|
r43346 | |||
|
r39243 | def _getmaster(fctx): | ||
"""(fctx) -> str""" | ||||
|
r43347 | return fctx._repo.ui.config(b'fastannotate', b'mainbranch') or b'default' | ||
|
r39243 | |||
|
r43346 | |||
|
r39243 | def _doannotate(fctx, follow=True, diffopts=None): | ||
"""like the vanilla fctx.annotate, but do it via fastannotate, and make | ||||
the output format compatible with the vanilla fctx.annotate. | ||||
may raise Exception, and always return line numbers. | ||||
""" | ||||
master = _getmaster(fctx) | ||||
with context.fctxannotatecontext(fctx, follow, diffopts) as ac: | ||||
try: | ||||
|
r43346 | annotated, contents = ac.annotate( | ||
fctx.rev(), master=master, showpath=True, showlines=True | ||||
) | ||||
|
r39243 | except Exception: | ||
|
r43346 | ac.rebuild() # try rebuild once | ||
fctx._repo.ui.debug( | ||||
|
r43347 | b'fastannotate: %s: rebuilding broken cache\n' % fctx._path | ||
|
r43346 | ) | ||
|
r39243 | try: | ||
|
r43346 | annotated, contents = ac.annotate( | ||
fctx.rev(), master=master, showpath=True, showlines=True | ||||
) | ||||
|
r39243 | except Exception: | ||
raise | ||||
assert annotated and contents | ||||
return _convertoutputs(fctx._repo, annotated, contents) | ||||
|
r43346 | |||
|
r39243 | def _hgwebannotate(orig, fctx, ui): | ||
|
r43346 | diffopts = patch.difffeatureopts( | ||
|
r43347 | ui, untrusted=True, section=b'annotate', whitespace=True | ||
|
r43346 | ) | ||
|
r39243 | return _doannotate(fctx, diffopts=diffopts) | ||
|
r43346 | |||
def _fctxannotate( | ||||
orig, self, follow=False, linenumber=False, skiprevs=None, diffopts=None | ||||
): | ||||
|
r39243 | if skiprevs: | ||
# skiprevs is not supported yet | ||||
|
r43346 | return orig( | ||
self, follow, linenumber, skiprevs=skiprevs, diffopts=diffopts | ||||
) | ||||
|
r39243 | try: | ||
return _doannotate(self, follow, diffopts) | ||||
except Exception as ex: | ||||
|
r43346 | self._repo.ui.debug( | ||
|
r43387 | b'fastannotate: falling back to the vanilla annotate: %r\n' % ex | ||
|
r43346 | ) | ||
return orig(self, follow=follow, skiprevs=skiprevs, diffopts=diffopts) | ||||
|
r39243 | |||
def _remotefctxannotate(orig, self, follow=False, skiprevs=None, diffopts=None): | ||||
# skipset: a set-like used to test if a fctx needs to be downloaded | ||||
with context.fctxannotatecontext(self, follow, diffopts) as ac: | ||||
skipset = revmap.revmap(ac.revmappath) | ||||
|
r43346 | return orig( | ||
self, follow, skiprevs=skiprevs, diffopts=diffopts, prefetchskip=skipset | ||||
) | ||||
|
r39243 | |||
def replacehgwebannotate(): | ||||
|
r43347 | extensions.wrapfunction(hgweb.webutil, b'annotate', _hgwebannotate) | ||
|
r39243 | |||
|
r43346 | |||
|
r39243 | def replacefctxannotate(): | ||
|
r43347 | extensions.wrapfunction(hgcontext.basefilectx, b'annotate', _fctxannotate) | ||