hgext/fastannotate/support.py
changeset 39210 1ddb296e0dee
child 39213 303dae0136b0
equal deleted inserted replaced
39209:1af95139e5ec 39210:1ddb296e0dee
       
     1 # Copyright 2016-present Facebook. All Rights Reserved.
       
     2 #
       
     3 # support: fastannotate support for hgweb, and filectx
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 from __future__ import absolute_import
       
     9 
       
    10 from mercurial import (
       
    11     context as hgcontext,
       
    12     dagop,
       
    13     extensions,
       
    14     hgweb,
       
    15     patch,
       
    16     util,
       
    17 )
       
    18 
       
    19 from . import (
       
    20     context,
       
    21     revmap,
       
    22 )
       
    23 
       
    24 class _lazyfctx(object):
       
    25     """delegates to fctx but do not construct fctx when unnecessary"""
       
    26 
       
    27     def __init__(self, repo, node, path):
       
    28         self._node = node
       
    29         self._path = path
       
    30         self._repo = repo
       
    31 
       
    32     def node(self):
       
    33         return self._node
       
    34 
       
    35     def path(self):
       
    36         return self._path
       
    37 
       
    38     @util.propertycache
       
    39     def _fctx(self):
       
    40         return context.resolvefctx(self._repo, self._node, self._path)
       
    41 
       
    42     def __getattr__(self, name):
       
    43         return getattr(self._fctx, name)
       
    44 
       
    45 def _convertoutputs(repo, annotated, contents):
       
    46     """convert fastannotate outputs to vanilla annotate format"""
       
    47     # fastannotate returns: [(nodeid, linenum, path)], [linecontent]
       
    48     # convert to what fctx.annotate returns: [annotateline]
       
    49     results = []
       
    50     fctxmap = {}
       
    51     annotateline = dagop.annotateline
       
    52     for i, (hsh, linenum, path) in enumerate(annotated):
       
    53         if (hsh, path) not in fctxmap:
       
    54             fctxmap[(hsh, path)] = _lazyfctx(repo, hsh, path)
       
    55         # linenum: the user wants 1-based, we have 0-based.
       
    56         lineno = linenum + 1
       
    57         fctx = fctxmap[(hsh, path)]
       
    58         line = contents[i]
       
    59         results.append(annotateline(fctx=fctx, lineno=lineno, text=line))
       
    60     return results
       
    61 
       
    62 def _getmaster(fctx):
       
    63     """(fctx) -> str"""
       
    64     return fctx._repo.ui.config('fastannotate', 'mainbranch') or 'default'
       
    65 
       
    66 def _doannotate(fctx, follow=True, diffopts=None):
       
    67     """like the vanilla fctx.annotate, but do it via fastannotate, and make
       
    68     the output format compatible with the vanilla fctx.annotate.
       
    69     may raise Exception, and always return line numbers.
       
    70     """
       
    71     master = _getmaster(fctx)
       
    72     annotated = contents = None
       
    73 
       
    74     with context.fctxannotatecontext(fctx, follow, diffopts) as ac:
       
    75         try:
       
    76             annotated, contents = ac.annotate(fctx.rev(), master=master,
       
    77                                               showpath=True, showlines=True)
       
    78         except Exception:
       
    79             ac.rebuild() # try rebuild once
       
    80             fctx._repo.ui.debug('fastannotate: %s: rebuilding broken cache\n'
       
    81                                 % fctx._path)
       
    82             try:
       
    83                 annotated, contents = ac.annotate(fctx.rev(), master=master,
       
    84                                                   showpath=True, showlines=True)
       
    85             except Exception:
       
    86                 raise
       
    87 
       
    88     assert annotated and contents
       
    89     return _convertoutputs(fctx._repo, annotated, contents)
       
    90 
       
    91 def _hgwebannotate(orig, fctx, ui):
       
    92     diffopts = patch.difffeatureopts(ui, untrusted=True,
       
    93                                      section='annotate', whitespace=True)
       
    94     return _doannotate(fctx, diffopts=diffopts)
       
    95 
       
    96 def _fctxannotate(orig, self, follow=False, linenumber=False, skiprevs=None,
       
    97                   diffopts=None):
       
    98     if skiprevs:
       
    99         # skiprevs is not supported yet
       
   100         return orig(self, follow, linenumber, skiprevs=skiprevs,
       
   101                     diffopts=diffopts)
       
   102     try:
       
   103         return _doannotate(self, follow, diffopts)
       
   104     except Exception as ex:
       
   105         self._repo.ui.debug('fastannotate: falling back to the vanilla '
       
   106                             'annotate: %r\n' % ex)
       
   107         return orig(self, follow=follow, skiprevs=skiprevs,
       
   108                     diffopts=diffopts)
       
   109 
       
   110 def _remotefctxannotate(orig, self, follow=False, skiprevs=None, diffopts=None):
       
   111     # skipset: a set-like used to test if a fctx needs to be downloaded
       
   112     skipset = None
       
   113     with context.fctxannotatecontext(self, follow, diffopts) as ac:
       
   114         skipset = revmap.revmap(ac.revmappath)
       
   115     return orig(self, follow, skiprevs=skiprevs, diffopts=diffopts,
       
   116                 prefetchskip=skipset)
       
   117 
       
   118 def replacehgwebannotate():
       
   119     extensions.wrapfunction(hgweb.webutil, 'annotate', _hgwebannotate)
       
   120 
       
   121 def replacefctxannotate():
       
   122     extensions.wrapfunction(hgcontext.basefilectx, 'annotate', _fctxannotate)
       
   123 
       
   124 def replaceremotefctxannotate():
       
   125     try:
       
   126         r = extensions.find('remotefilelog')
       
   127     except KeyError:
       
   128         return
       
   129     else:
       
   130         extensions.wrapfunction(r.remotefilectx.remotefilectx, 'annotate',
       
   131                                 _remotefctxannotate)