--- a/hgext/fastannotate/context.py Sat Oct 05 10:29:34 2019 -0400
+++ b/hgext/fastannotate/context.py Sun Oct 06 09:45:02 2019 -0400
@@ -23,9 +23,7 @@
scmutil,
util,
)
-from mercurial.utils import (
- stringutil,
-)
+from mercurial.utils import stringutil
from . import (
error as faerror,
@@ -37,6 +35,7 @@
def _getflog(repo, path):
return repo.file(path)
+
# extracted from mercurial.context.basefilectx.annotate
def _parents(f, follow=True):
# Cut _descendantrev here to mitigate the penalty of lazy linkrev
@@ -58,6 +57,7 @@
return pl
+
# extracted from mercurial.context.basefilectx.annotate. slightly modified
# so it takes a fctx instead of a pair of text and fctx.
def _decorate(fctx):
@@ -67,6 +67,7 @@
linecount += 1
return ([(fctx, i) for i in pycompat.xrange(linecount)], text)
+
# extracted from mercurial.context.basefilectx.annotate. slightly modified
# so it takes an extra "blocks" parameter calculated elsewhere, instead of
# calculating diff here.
@@ -78,10 +79,12 @@
child[0][b1:b2] = parent[0][a1:a2]
return child
+
# like scmutil.revsingle, but with lru cache, so their states (like manifests)
# could be reused
_revsingle = util.lrucachefunc(scmutil.revsingle)
+
def resolvefctx(repo, rev, path, resolverev=False, adjustctx=None):
"""(repo, str, str) -> fctx
@@ -125,23 +128,27 @@
fctx._changectx = repo[introrev]
return fctx
+
# like mercurial.store.encodedir, but use linelog suffixes: .m, .l, .lock
def encodedir(path):
- return (path
- .replace('.hg/', '.hg.hg/')
- .replace('.l/', '.l.hg/')
- .replace('.m/', '.m.hg/')
- .replace('.lock/', '.lock.hg/'))
+ return (
+ path.replace('.hg/', '.hg.hg/')
+ .replace('.l/', '.l.hg/')
+ .replace('.m/', '.m.hg/')
+ .replace('.lock/', '.lock.hg/')
+ )
+
def hashdiffopts(diffopts):
- diffoptstr = stringutil.pprint(sorted(
- (k, getattr(diffopts, k))
- for k in mdiff.diffopts.defaults
- ))
+ diffoptstr = stringutil.pprint(
+ sorted((k, getattr(diffopts, k)) for k in mdiff.diffopts.defaults)
+ )
return node.hex(hashlib.sha1(diffoptstr).digest())[:6]
+
_defaultdiffopthash = hashdiffopts(mdiff.defaultopts)
+
class annotateopts(object):
"""like mercurial.mdiff.diffopts, but is for annotate
@@ -175,8 +182,10 @@
result += 'i' + diffopthash
return result or 'default'
+
defaultopts = annotateopts()
+
class _annotatecontext(object):
"""do not use this class directly as it does not use lock to protect
writes. use "with annotatecontext(...)" instead.
@@ -191,7 +200,7 @@
self.revmappath = revmappath
self._linelog = None
self._revmap = None
- self._node2path = {} # {str: str}
+ self._node2path = {} # {str: str}
@property
def linelog(self):
@@ -298,23 +307,27 @@
directly, revfctx = self.canannotatedirectly(rev)
if directly:
if self.ui.debugflag:
- self.ui.debug('fastannotate: %s: using fast path '
- '(resolved fctx: %s)\n'
- % (self.path,
- stringutil.pprint(util.safehasattr(revfctx,
- 'node'))))
+ self.ui.debug(
+ 'fastannotate: %s: using fast path '
+ '(resolved fctx: %s)\n'
+ % (
+ self.path,
+ stringutil.pprint(util.safehasattr(revfctx, 'node')),
+ )
+ )
return self.annotatedirectly(revfctx, showpath, showlines)
# resolve master
masterfctx = None
if master:
try:
- masterfctx = self._resolvefctx(master, resolverev=True,
- adjustctx=True)
- except LookupError: # master does not have the file
+ masterfctx = self._resolvefctx(
+ master, resolverev=True, adjustctx=True
+ )
+ except LookupError: # master does not have the file
pass
else:
- if masterfctx in self.revmap: # no need to update linelog
+ if masterfctx in self.revmap: # no need to update linelog
masterfctx = None
# ... - @ <- rev (can be an arbitrary changeset,
@@ -342,18 +355,20 @@
initvisit = [revfctx]
if masterfctx:
if masterfctx.rev() is None:
- raise error.Abort(_('cannot update linelog to wdir()'),
- hint=_('set fastannotate.mainbranch'))
+ raise error.Abort(
+ _('cannot update linelog to wdir()'),
+ hint=_('set fastannotate.mainbranch'),
+ )
initvisit.append(masterfctx)
visit = initvisit[:]
pcache = {}
needed = {revfctx: 1}
- hist = {} # {fctx: ([(llrev or fctx, linenum)], text)}
+ hist = {} # {fctx: ([(llrev or fctx, linenum)], text)}
while visit:
f = visit.pop()
if f in pcache or f in hist:
continue
- if f in self.revmap: # in the old main branch, it's a joint
+ if f in self.revmap: # in the old main branch, it's a joint
llrev = self.revmap.hsh2rev(f.node())
self.linelog.annotate(llrev)
result = self.linelog.annotateresult
@@ -387,19 +402,24 @@
if self.ui.debugflag:
if newmainbranch:
- self.ui.debug('fastannotate: %s: %d new changesets in the main'
- ' branch\n' % (self.path, len(newmainbranch)))
- elif not hist: # no joints, no updates
- self.ui.debug('fastannotate: %s: linelog cannot help in '
- 'annotating this revision\n' % self.path)
+ self.ui.debug(
+ 'fastannotate: %s: %d new changesets in the main'
+ ' branch\n' % (self.path, len(newmainbranch))
+ )
+ elif not hist: # no joints, no updates
+ self.ui.debug(
+ 'fastannotate: %s: linelog cannot help in '
+ 'annotating this revision\n' % self.path
+ )
# prepare annotateresult so we can update linelog incrementally
self.linelog.annotate(self.linelog.maxrev)
# 3rd DFS does the actual annotate
visit = initvisit[:]
- progress = self.ui.makeprogress(('building cache'),
- total=len(newmainbranch))
+ progress = self.ui.makeprogress(
+ 'building cache', total=len(newmainbranch)
+ )
while visit:
f = visit[-1]
if f in hist:
@@ -416,8 +436,8 @@
continue
visit.pop()
- blocks = None # mdiff blocks, used for appending linelog
- ismainbranch = (f in newmainbranch)
+ blocks = None # mdiff blocks, used for appending linelog
+ ismainbranch = f in newmainbranch
# curr is the same as the traditional annotate algorithm,
# if we only care about linear history (do not follow merge),
# then curr is not actually used.
@@ -437,22 +457,23 @@
hist[f] = curr
del pcache[f]
- if ismainbranch: # need to write to linelog
+ if ismainbranch: # need to write to linelog
progress.increment()
bannotated = None
- if len(pl) == 2 and self.opts.followmerge: # merge
+ if len(pl) == 2 and self.opts.followmerge: # merge
bannotated = curr[0]
- if blocks is None: # no parents, add an empty one
+ if blocks is None: # no parents, add an empty one
blocks = list(self._diffblocks('', curr[1]))
self._appendrev(f, blocks, bannotated)
- elif showpath: # not append linelog, but we need to record path
+ elif showpath: # not append linelog, but we need to record path
self._node2path[f.node()] = f.path()
progress.complete()
result = [
((self.revmap.rev2hsh(fr) if isinstance(fr, int) else fr.node()), l)
- for fr, l in hist[revfctx][0]] # [(node, linenumber)]
+ for fr, l in hist[revfctx][0]
+ ] # [(node, linenumber)]
return self._refineannotateresult(result, revfctx, showpath, showlines)
def canannotatedirectly(self, rev):
@@ -557,7 +578,7 @@
continue
hsh = annotateresult[idxs[0]][0]
break
- except StopIteration: # no more unresolved lines
+ except StopIteration: # no more unresolved lines
return result
if hsh is None:
# the remaining key2idxs are not in main branch, resolving them
@@ -567,9 +588,11 @@
if rev not in revlines:
hsh = annotateresult[idxs[0]][0]
if self.ui.debugflag:
- self.ui.debug('fastannotate: reading %s line #%d '
- 'to resolve lines %r\n'
- % (node.short(hsh), linenum, idxs))
+ self.ui.debug(
+ 'fastannotate: reading %s line #%d '
+ 'to resolve lines %r\n'
+ % (node.short(hsh), linenum, idxs)
+ )
fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
lines = mdiff.splitnewlines(fctx.data())
revlines[rev] = lines
@@ -579,8 +602,9 @@
return result
# run the annotate and the lines should match to the file content
- self.ui.debug('fastannotate: annotate %s to resolve lines\n'
- % node.short(hsh))
+ self.ui.debug(
+ 'fastannotate: annotate %s to resolve lines\n' % node.short(hsh)
+ )
linelog.annotate(rev)
fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
annotated = linelog.annotateresult
@@ -608,14 +632,17 @@
hsh = f.node()
llrev = self.revmap.hsh2rev(hsh)
if not llrev:
- raise faerror.CorruptedFileError('%s is not in revmap'
- % node.hex(hsh))
+ raise faerror.CorruptedFileError(
+ '%s is not in revmap' % node.hex(hsh)
+ )
if (self.revmap.rev2flag(llrev) & revmapmod.sidebranchflag) != 0:
- raise faerror.CorruptedFileError('%s is not in revmap mainbranch'
- % node.hex(hsh))
+ raise faerror.CorruptedFileError(
+ '%s is not in revmap mainbranch' % node.hex(hsh)
+ )
self.linelog.annotate(llrev)
- result = [(self.revmap.rev2hsh(r), l)
- for r, l in self.linelog.annotateresult]
+ result = [
+ (self.revmap.rev2hsh(r), l) for r, l in self.linelog.annotateresult
+ ]
return self._refineannotateresult(result, f, showpath, showlines)
def _refineannotateresult(self, result, f, showpath, showlines):
@@ -625,13 +652,13 @@
if showpath:
result = self._addpathtoresult(result)
if showlines:
- if isinstance(f, bytes): # f: node or fctx
+ if isinstance(f, bytes): # f: node or fctx
llrev = self.revmap.hsh2rev(f)
fctx = self._resolvefctx(f, self.revmap.rev2path(llrev))
else:
fctx = f
lines = mdiff.splitnewlines(fctx.data())
- if len(lines) != len(result): # linelog is probably corrupted
+ if len(lines) != len(result): # linelog is probably corrupted
raise faerror.CorruptedFileError()
result = (result, lines)
return result
@@ -660,15 +687,18 @@
# append sidebranch revisions to revmap
siderevs = []
- siderevmap = {} # node: int
+ siderevmap = {} # node: int
if bannotated is not None:
for (a1, a2, b1, b2), op in blocks:
if op != '=':
# f could be either linelong rev, or fctx.
- siderevs += [f for f, l in bannotated[b1:b2]
- if not isinstance(f, int)]
+ siderevs += [
+ f
+ for f, l in bannotated[b1:b2]
+ if not isinstance(f, int)
+ ]
siderevs = set(siderevs)
- if fctx in siderevs: # mainnode must be appended seperately
+ if fctx in siderevs: # mainnode must be appended seperately
siderevs.remove(fctx)
for f in siderevs:
siderevmap[f] = getllrev(f)
@@ -683,8 +713,10 @@
if bannotated is None:
linelog.replacelines(llrev, a1, a2, b1, b2)
else:
- blines = [((r if isinstance(r, int) else siderevmap[r]), l)
- for r, l in bannotated[b1:b2]]
+ blines = [
+ ((r if isinstance(r, int) else siderevmap[r]), l)
+ for r, l in bannotated[b1:b2]
+ ]
linelog.replacelines_vec(llrev, a1, a2, blines)
def _addpathtoresult(self, annotateresult, revmap=None):
@@ -717,11 +749,13 @@
"""-> (fctx) -> [fctx]"""
followrename = self.opts.followrename
followmerge = self.opts.followmerge
+
def parents(f):
pl = _parents(f, follow=followrename)
if not followmerge:
pl = pl[:1]
return pl
+
return parents
@util.propertycache
@@ -731,6 +765,7 @@
def _resolvefctx(self, rev, path=None, **kwds):
return resolvefctx(self.repo, rev, (path or self.path), **kwds)
+
def _unlinkpaths(paths):
"""silent, best-effort unlink"""
for path in paths:
@@ -739,13 +774,15 @@
except OSError:
pass
+
class pathhelper(object):
"""helper for getting paths for lockfile, linelog and revmap"""
def __init__(self, repo, path, opts=defaultopts):
# different options use different directories
- self._vfspath = os.path.join('fastannotate',
- opts.shortstr, encodedir(path))
+ self._vfspath = os.path.join(
+ 'fastannotate', opts.shortstr, encodedir(path)
+ )
self._repo = repo
@property
@@ -763,6 +800,7 @@
def revmappath(self):
return self._repo.vfs.join(self._vfspath + '.m')
+
@contextlib.contextmanager
def annotatecontext(repo, path, opts=defaultopts, rebuild=False):
"""context needed to perform (fast) annotate on a file
@@ -799,6 +837,7 @@
if actx is not None:
actx.close()
+
def fctxannotatecontext(fctx, follow=True, diffopts=None, rebuild=False):
"""like annotatecontext but get the context from a fctx. convenient when
used in fctx.annotate