comparison hgext/fastannotate/context.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 0152a907f714
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
21 node, 21 node,
22 pycompat, 22 pycompat,
23 scmutil, 23 scmutil,
24 util, 24 util,
25 ) 25 )
26 from mercurial.utils import ( 26 from mercurial.utils import stringutil
27 stringutil,
28 )
29 27
30 from . import ( 28 from . import (
31 error as faerror, 29 error as faerror,
32 revmap as revmapmod, 30 revmap as revmapmod,
33 ) 31 )
34 32
35 # given path, get filelog, cached 33 # given path, get filelog, cached
36 @util.lrucachefunc 34 @util.lrucachefunc
37 def _getflog(repo, path): 35 def _getflog(repo, path):
38 return repo.file(path) 36 return repo.file(path)
37
39 38
40 # extracted from mercurial.context.basefilectx.annotate 39 # extracted from mercurial.context.basefilectx.annotate
41 def _parents(f, follow=True): 40 def _parents(f, follow=True):
42 # Cut _descendantrev here to mitigate the penalty of lazy linkrev 41 # Cut _descendantrev here to mitigate the penalty of lazy linkrev
43 # adjustment. Otherwise, p._adjustlinkrev() would walk changelog 42 # adjustment. Otherwise, p._adjustlinkrev() would walk changelog
56 if not '_filelog' in p.__dict__: 55 if not '_filelog' in p.__dict__:
57 p._filelog = _getflog(f._repo, p.path()) 56 p._filelog = _getflog(f._repo, p.path())
58 57
59 return pl 58 return pl
60 59
60
61 # extracted from mercurial.context.basefilectx.annotate. slightly modified 61 # extracted from mercurial.context.basefilectx.annotate. slightly modified
62 # so it takes a fctx instead of a pair of text and fctx. 62 # so it takes a fctx instead of a pair of text and fctx.
63 def _decorate(fctx): 63 def _decorate(fctx):
64 text = fctx.data() 64 text = fctx.data()
65 linecount = text.count('\n') 65 linecount = text.count('\n')
66 if text and not text.endswith('\n'): 66 if text and not text.endswith('\n'):
67 linecount += 1 67 linecount += 1
68 return ([(fctx, i) for i in pycompat.xrange(linecount)], text) 68 return ([(fctx, i) for i in pycompat.xrange(linecount)], text)
69
69 70
70 # extracted from mercurial.context.basefilectx.annotate. slightly modified 71 # extracted from mercurial.context.basefilectx.annotate. slightly modified
71 # so it takes an extra "blocks" parameter calculated elsewhere, instead of 72 # so it takes an extra "blocks" parameter calculated elsewhere, instead of
72 # calculating diff here. 73 # calculating diff here.
73 def _pair(parent, child, blocks): 74 def _pair(parent, child, blocks):
76 # belong to the child. 77 # belong to the child.
77 if t == '=': 78 if t == '=':
78 child[0][b1:b2] = parent[0][a1:a2] 79 child[0][b1:b2] = parent[0][a1:a2]
79 return child 80 return child
80 81
82
81 # like scmutil.revsingle, but with lru cache, so their states (like manifests) 83 # like scmutil.revsingle, but with lru cache, so their states (like manifests)
82 # could be reused 84 # could be reused
83 _revsingle = util.lrucachefunc(scmutil.revsingle) 85 _revsingle = util.lrucachefunc(scmutil.revsingle)
86
84 87
85 def resolvefctx(repo, rev, path, resolverev=False, adjustctx=None): 88 def resolvefctx(repo, rev, path, resolverev=False, adjustctx=None):
86 """(repo, str, str) -> fctx 89 """(repo, str, str) -> fctx
87 90
88 get the filectx object from repo, rev, path, in an efficient way. 91 get the filectx object from repo, rev, path, in an efficient way.
123 if introrev != ctx.rev(): 126 if introrev != ctx.rev():
124 fctx._changeid = introrev 127 fctx._changeid = introrev
125 fctx._changectx = repo[introrev] 128 fctx._changectx = repo[introrev]
126 return fctx 129 return fctx
127 130
131
128 # like mercurial.store.encodedir, but use linelog suffixes: .m, .l, .lock 132 # like mercurial.store.encodedir, but use linelog suffixes: .m, .l, .lock
129 def encodedir(path): 133 def encodedir(path):
130 return (path 134 return (
131 .replace('.hg/', '.hg.hg/') 135 path.replace('.hg/', '.hg.hg/')
132 .replace('.l/', '.l.hg/') 136 .replace('.l/', '.l.hg/')
133 .replace('.m/', '.m.hg/') 137 .replace('.m/', '.m.hg/')
134 .replace('.lock/', '.lock.hg/')) 138 .replace('.lock/', '.lock.hg/')
139 )
140
135 141
136 def hashdiffopts(diffopts): 142 def hashdiffopts(diffopts):
137 diffoptstr = stringutil.pprint(sorted( 143 diffoptstr = stringutil.pprint(
138 (k, getattr(diffopts, k)) 144 sorted((k, getattr(diffopts, k)) for k in mdiff.diffopts.defaults)
139 for k in mdiff.diffopts.defaults 145 )
140 ))
141 return node.hex(hashlib.sha1(diffoptstr).digest())[:6] 146 return node.hex(hashlib.sha1(diffoptstr).digest())[:6]
142 147
148
143 _defaultdiffopthash = hashdiffopts(mdiff.defaultopts) 149 _defaultdiffopthash = hashdiffopts(mdiff.defaultopts)
150
144 151
145 class annotateopts(object): 152 class annotateopts(object):
146 """like mercurial.mdiff.diffopts, but is for annotate 153 """like mercurial.mdiff.diffopts, but is for annotate
147 154
148 followrename: follow renames, like "hg annotate -f" 155 followrename: follow renames, like "hg annotate -f"
173 diffopthash = hashdiffopts(self.diffopts) 180 diffopthash = hashdiffopts(self.diffopts)
174 if diffopthash != _defaultdiffopthash: 181 if diffopthash != _defaultdiffopthash:
175 result += 'i' + diffopthash 182 result += 'i' + diffopthash
176 return result or 'default' 183 return result or 'default'
177 184
185
178 defaultopts = annotateopts() 186 defaultopts = annotateopts()
187
179 188
180 class _annotatecontext(object): 189 class _annotatecontext(object):
181 """do not use this class directly as it does not use lock to protect 190 """do not use this class directly as it does not use lock to protect
182 writes. use "with annotatecontext(...)" instead. 191 writes. use "with annotatecontext(...)" instead.
183 """ 192 """
189 self.opts = opts 198 self.opts = opts
190 self.linelogpath = linelogpath 199 self.linelogpath = linelogpath
191 self.revmappath = revmappath 200 self.revmappath = revmappath
192 self._linelog = None 201 self._linelog = None
193 self._revmap = None 202 self._revmap = None
194 self._node2path = {} # {str: str} 203 self._node2path = {} # {str: str}
195 204
196 @property 205 @property
197 def linelog(self): 206 def linelog(self):
198 if self._linelog is None: 207 if self._linelog is None:
199 if os.path.exists(self.linelogpath): 208 if os.path.exists(self.linelogpath):
296 305
297 # fast path: if rev is in the main branch already 306 # fast path: if rev is in the main branch already
298 directly, revfctx = self.canannotatedirectly(rev) 307 directly, revfctx = self.canannotatedirectly(rev)
299 if directly: 308 if directly:
300 if self.ui.debugflag: 309 if self.ui.debugflag:
301 self.ui.debug('fastannotate: %s: using fast path ' 310 self.ui.debug(
302 '(resolved fctx: %s)\n' 311 'fastannotate: %s: using fast path '
303 % (self.path, 312 '(resolved fctx: %s)\n'
304 stringutil.pprint(util.safehasattr(revfctx, 313 % (
305 'node')))) 314 self.path,
315 stringutil.pprint(util.safehasattr(revfctx, 'node')),
316 )
317 )
306 return self.annotatedirectly(revfctx, showpath, showlines) 318 return self.annotatedirectly(revfctx, showpath, showlines)
307 319
308 # resolve master 320 # resolve master
309 masterfctx = None 321 masterfctx = None
310 if master: 322 if master:
311 try: 323 try:
312 masterfctx = self._resolvefctx(master, resolverev=True, 324 masterfctx = self._resolvefctx(
313 adjustctx=True) 325 master, resolverev=True, adjustctx=True
314 except LookupError: # master does not have the file 326 )
327 except LookupError: # master does not have the file
315 pass 328 pass
316 else: 329 else:
317 if masterfctx in self.revmap: # no need to update linelog 330 if masterfctx in self.revmap: # no need to update linelog
318 masterfctx = None 331 masterfctx = None
319 332
320 # ... - @ <- rev (can be an arbitrary changeset, 333 # ... - @ <- rev (can be an arbitrary changeset,
321 # / not necessarily a descendant 334 # / not necessarily a descendant
322 # master -> o of master) 335 # master -> o of master)
340 # "needed" is a simple reference counting dict to free items in 353 # "needed" is a simple reference counting dict to free items in
341 # "hist", reducing its memory usage otherwise could be huge. 354 # "hist", reducing its memory usage otherwise could be huge.
342 initvisit = [revfctx] 355 initvisit = [revfctx]
343 if masterfctx: 356 if masterfctx:
344 if masterfctx.rev() is None: 357 if masterfctx.rev() is None:
345 raise error.Abort(_('cannot update linelog to wdir()'), 358 raise error.Abort(
346 hint=_('set fastannotate.mainbranch')) 359 _('cannot update linelog to wdir()'),
360 hint=_('set fastannotate.mainbranch'),
361 )
347 initvisit.append(masterfctx) 362 initvisit.append(masterfctx)
348 visit = initvisit[:] 363 visit = initvisit[:]
349 pcache = {} 364 pcache = {}
350 needed = {revfctx: 1} 365 needed = {revfctx: 1}
351 hist = {} # {fctx: ([(llrev or fctx, linenum)], text)} 366 hist = {} # {fctx: ([(llrev or fctx, linenum)], text)}
352 while visit: 367 while visit:
353 f = visit.pop() 368 f = visit.pop()
354 if f in pcache or f in hist: 369 if f in pcache or f in hist:
355 continue 370 continue
356 if f in self.revmap: # in the old main branch, it's a joint 371 if f in self.revmap: # in the old main branch, it's a joint
357 llrev = self.revmap.hsh2rev(f.node()) 372 llrev = self.revmap.hsh2rev(f.node())
358 self.linelog.annotate(llrev) 373 self.linelog.annotate(llrev)
359 result = self.linelog.annotateresult 374 result = self.linelog.annotateresult
360 hist[f] = (result, f.data()) 375 hist[f] = (result, f.data())
361 continue 376 continue
385 if masterfctx is not None: 400 if masterfctx is not None:
386 self._checklastmasterhead(f) 401 self._checklastmasterhead(f)
387 402
388 if self.ui.debugflag: 403 if self.ui.debugflag:
389 if newmainbranch: 404 if newmainbranch:
390 self.ui.debug('fastannotate: %s: %d new changesets in the main' 405 self.ui.debug(
391 ' branch\n' % (self.path, len(newmainbranch))) 406 'fastannotate: %s: %d new changesets in the main'
392 elif not hist: # no joints, no updates 407 ' branch\n' % (self.path, len(newmainbranch))
393 self.ui.debug('fastannotate: %s: linelog cannot help in ' 408 )
394 'annotating this revision\n' % self.path) 409 elif not hist: # no joints, no updates
410 self.ui.debug(
411 'fastannotate: %s: linelog cannot help in '
412 'annotating this revision\n' % self.path
413 )
395 414
396 # prepare annotateresult so we can update linelog incrementally 415 # prepare annotateresult so we can update linelog incrementally
397 self.linelog.annotate(self.linelog.maxrev) 416 self.linelog.annotate(self.linelog.maxrev)
398 417
399 # 3rd DFS does the actual annotate 418 # 3rd DFS does the actual annotate
400 visit = initvisit[:] 419 visit = initvisit[:]
401 progress = self.ui.makeprogress(('building cache'), 420 progress = self.ui.makeprogress(
402 total=len(newmainbranch)) 421 'building cache', total=len(newmainbranch)
422 )
403 while visit: 423 while visit:
404 f = visit[-1] 424 f = visit[-1]
405 if f in hist: 425 if f in hist:
406 visit.pop() 426 visit.pop()
407 continue 427 continue
414 visit.append(p) 434 visit.append(p)
415 if not ready: 435 if not ready:
416 continue 436 continue
417 437
418 visit.pop() 438 visit.pop()
419 blocks = None # mdiff blocks, used for appending linelog 439 blocks = None # mdiff blocks, used for appending linelog
420 ismainbranch = (f in newmainbranch) 440 ismainbranch = f in newmainbranch
421 # curr is the same as the traditional annotate algorithm, 441 # curr is the same as the traditional annotate algorithm,
422 # if we only care about linear history (do not follow merge), 442 # if we only care about linear history (do not follow merge),
423 # then curr is not actually used. 443 # then curr is not actually used.
424 assert f not in hist 444 assert f not in hist
425 curr = _decorate(f) 445 curr = _decorate(f)
435 needed[p] -= 1 455 needed[p] -= 1
436 456
437 hist[f] = curr 457 hist[f] = curr
438 del pcache[f] 458 del pcache[f]
439 459
440 if ismainbranch: # need to write to linelog 460 if ismainbranch: # need to write to linelog
441 progress.increment() 461 progress.increment()
442 bannotated = None 462 bannotated = None
443 if len(pl) == 2 and self.opts.followmerge: # merge 463 if len(pl) == 2 and self.opts.followmerge: # merge
444 bannotated = curr[0] 464 bannotated = curr[0]
445 if blocks is None: # no parents, add an empty one 465 if blocks is None: # no parents, add an empty one
446 blocks = list(self._diffblocks('', curr[1])) 466 blocks = list(self._diffblocks('', curr[1]))
447 self._appendrev(f, blocks, bannotated) 467 self._appendrev(f, blocks, bannotated)
448 elif showpath: # not append linelog, but we need to record path 468 elif showpath: # not append linelog, but we need to record path
449 self._node2path[f.node()] = f.path() 469 self._node2path[f.node()] = f.path()
450 470
451 progress.complete() 471 progress.complete()
452 472
453 result = [ 473 result = [
454 ((self.revmap.rev2hsh(fr) if isinstance(fr, int) else fr.node()), l) 474 ((self.revmap.rev2hsh(fr) if isinstance(fr, int) else fr.node()), l)
455 for fr, l in hist[revfctx][0]] # [(node, linenumber)] 475 for fr, l in hist[revfctx][0]
476 ] # [(node, linenumber)]
456 return self._refineannotateresult(result, revfctx, showpath, showlines) 477 return self._refineannotateresult(result, revfctx, showpath, showlines)
457 478
458 def canannotatedirectly(self, rev): 479 def canannotatedirectly(self, rev):
459 """(str) -> bool, fctx or node. 480 """(str) -> bool, fctx or node.
460 return (True, f) if we can annotate without updating the linelog, pass 481 return (True, f) if we can annotate without updating the linelog, pass
555 for (rev, _linenum), idxs in key2idxs.iteritems(): 576 for (rev, _linenum), idxs in key2idxs.iteritems():
556 if revmap.rev2flag(rev) & revmapmod.sidebranchflag: 577 if revmap.rev2flag(rev) & revmapmod.sidebranchflag:
557 continue 578 continue
558 hsh = annotateresult[idxs[0]][0] 579 hsh = annotateresult[idxs[0]][0]
559 break 580 break
560 except StopIteration: # no more unresolved lines 581 except StopIteration: # no more unresolved lines
561 return result 582 return result
562 if hsh is None: 583 if hsh is None:
563 # the remaining key2idxs are not in main branch, resolving them 584 # the remaining key2idxs are not in main branch, resolving them
564 # using the hard way... 585 # using the hard way...
565 revlines = {} 586 revlines = {}
566 for (rev, linenum), idxs in key2idxs.iteritems(): 587 for (rev, linenum), idxs in key2idxs.iteritems():
567 if rev not in revlines: 588 if rev not in revlines:
568 hsh = annotateresult[idxs[0]][0] 589 hsh = annotateresult[idxs[0]][0]
569 if self.ui.debugflag: 590 if self.ui.debugflag:
570 self.ui.debug('fastannotate: reading %s line #%d ' 591 self.ui.debug(
571 'to resolve lines %r\n' 592 'fastannotate: reading %s line #%d '
572 % (node.short(hsh), linenum, idxs)) 593 'to resolve lines %r\n'
594 % (node.short(hsh), linenum, idxs)
595 )
573 fctx = self._resolvefctx(hsh, revmap.rev2path(rev)) 596 fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
574 lines = mdiff.splitnewlines(fctx.data()) 597 lines = mdiff.splitnewlines(fctx.data())
575 revlines[rev] = lines 598 revlines[rev] = lines
576 for idx in idxs: 599 for idx in idxs:
577 result[idx] = revlines[rev][linenum] 600 result[idx] = revlines[rev][linenum]
578 assert all(x is not None for x in result) 601 assert all(x is not None for x in result)
579 return result 602 return result
580 603
581 # run the annotate and the lines should match to the file content 604 # run the annotate and the lines should match to the file content
582 self.ui.debug('fastannotate: annotate %s to resolve lines\n' 605 self.ui.debug(
583 % node.short(hsh)) 606 'fastannotate: annotate %s to resolve lines\n' % node.short(hsh)
607 )
584 linelog.annotate(rev) 608 linelog.annotate(rev)
585 fctx = self._resolvefctx(hsh, revmap.rev2path(rev)) 609 fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
586 annotated = linelog.annotateresult 610 annotated = linelog.annotateresult
587 lines = mdiff.splitnewlines(fctx.data()) 611 lines = mdiff.splitnewlines(fctx.data())
588 if len(lines) != len(annotated): 612 if len(lines) != len(annotated):
606 hsh = f 630 hsh = f
607 else: 631 else:
608 hsh = f.node() 632 hsh = f.node()
609 llrev = self.revmap.hsh2rev(hsh) 633 llrev = self.revmap.hsh2rev(hsh)
610 if not llrev: 634 if not llrev:
611 raise faerror.CorruptedFileError('%s is not in revmap' 635 raise faerror.CorruptedFileError(
612 % node.hex(hsh)) 636 '%s is not in revmap' % node.hex(hsh)
637 )
613 if (self.revmap.rev2flag(llrev) & revmapmod.sidebranchflag) != 0: 638 if (self.revmap.rev2flag(llrev) & revmapmod.sidebranchflag) != 0:
614 raise faerror.CorruptedFileError('%s is not in revmap mainbranch' 639 raise faerror.CorruptedFileError(
615 % node.hex(hsh)) 640 '%s is not in revmap mainbranch' % node.hex(hsh)
641 )
616 self.linelog.annotate(llrev) 642 self.linelog.annotate(llrev)
617 result = [(self.revmap.rev2hsh(r), l) 643 result = [
618 for r, l in self.linelog.annotateresult] 644 (self.revmap.rev2hsh(r), l) for r, l in self.linelog.annotateresult
645 ]
619 return self._refineannotateresult(result, f, showpath, showlines) 646 return self._refineannotateresult(result, f, showpath, showlines)
620 647
621 def _refineannotateresult(self, result, f, showpath, showlines): 648 def _refineannotateresult(self, result, f, showpath, showlines):
622 """add the missing path or line contents, they can be expensive. 649 """add the missing path or line contents, they can be expensive.
623 f could be either node or fctx. 650 f could be either node or fctx.
624 """ 651 """
625 if showpath: 652 if showpath:
626 result = self._addpathtoresult(result) 653 result = self._addpathtoresult(result)
627 if showlines: 654 if showlines:
628 if isinstance(f, bytes): # f: node or fctx 655 if isinstance(f, bytes): # f: node or fctx
629 llrev = self.revmap.hsh2rev(f) 656 llrev = self.revmap.hsh2rev(f)
630 fctx = self._resolvefctx(f, self.revmap.rev2path(llrev)) 657 fctx = self._resolvefctx(f, self.revmap.rev2path(llrev))
631 else: 658 else:
632 fctx = f 659 fctx = f
633 lines = mdiff.splitnewlines(fctx.data()) 660 lines = mdiff.splitnewlines(fctx.data())
634 if len(lines) != len(result): # linelog is probably corrupted 661 if len(lines) != len(result): # linelog is probably corrupted
635 raise faerror.CorruptedFileError() 662 raise faerror.CorruptedFileError()
636 result = (result, lines) 663 result = (result, lines)
637 return result 664 return result
638 665
639 def _appendrev(self, fctx, blocks, bannotated=None): 666 def _appendrev(self, fctx, blocks, bannotated=None):
658 rev = revmap.append(hsh, sidebranch=True, path=f.path()) 685 rev = revmap.append(hsh, sidebranch=True, path=f.path())
659 return rev 686 return rev
660 687
661 # append sidebranch revisions to revmap 688 # append sidebranch revisions to revmap
662 siderevs = [] 689 siderevs = []
663 siderevmap = {} # node: int 690 siderevmap = {} # node: int
664 if bannotated is not None: 691 if bannotated is not None:
665 for (a1, a2, b1, b2), op in blocks: 692 for (a1, a2, b1, b2), op in blocks:
666 if op != '=': 693 if op != '=':
667 # f could be either linelong rev, or fctx. 694 # f could be either linelong rev, or fctx.
668 siderevs += [f for f, l in bannotated[b1:b2] 695 siderevs += [
669 if not isinstance(f, int)] 696 f
697 for f, l in bannotated[b1:b2]
698 if not isinstance(f, int)
699 ]
670 siderevs = set(siderevs) 700 siderevs = set(siderevs)
671 if fctx in siderevs: # mainnode must be appended seperately 701 if fctx in siderevs: # mainnode must be appended seperately
672 siderevs.remove(fctx) 702 siderevs.remove(fctx)
673 for f in siderevs: 703 for f in siderevs:
674 siderevmap[f] = getllrev(f) 704 siderevmap[f] = getllrev(f)
675 705
676 # the changeset in the main branch, could be a merge 706 # the changeset in the main branch, could be a merge
681 if op == '=': 711 if op == '=':
682 continue 712 continue
683 if bannotated is None: 713 if bannotated is None:
684 linelog.replacelines(llrev, a1, a2, b1, b2) 714 linelog.replacelines(llrev, a1, a2, b1, b2)
685 else: 715 else:
686 blines = [((r if isinstance(r, int) else siderevmap[r]), l) 716 blines = [
687 for r, l in bannotated[b1:b2]] 717 ((r if isinstance(r, int) else siderevmap[r]), l)
718 for r, l in bannotated[b1:b2]
719 ]
688 linelog.replacelines_vec(llrev, a1, a2, blines) 720 linelog.replacelines_vec(llrev, a1, a2, blines)
689 721
690 def _addpathtoresult(self, annotateresult, revmap=None): 722 def _addpathtoresult(self, annotateresult, revmap=None):
691 """(revmap, [(node, linenum)]) -> [(node, linenum, path)]""" 723 """(revmap, [(node, linenum)]) -> [(node, linenum, path)]"""
692 if revmap is None: 724 if revmap is None:
715 @util.propertycache 747 @util.propertycache
716 def _parentfunc(self): 748 def _parentfunc(self):
717 """-> (fctx) -> [fctx]""" 749 """-> (fctx) -> [fctx]"""
718 followrename = self.opts.followrename 750 followrename = self.opts.followrename
719 followmerge = self.opts.followmerge 751 followmerge = self.opts.followmerge
752
720 def parents(f): 753 def parents(f):
721 pl = _parents(f, follow=followrename) 754 pl = _parents(f, follow=followrename)
722 if not followmerge: 755 if not followmerge:
723 pl = pl[:1] 756 pl = pl[:1]
724 return pl 757 return pl
758
725 return parents 759 return parents
726 760
727 @util.propertycache 761 @util.propertycache
728 def _perfhack(self): 762 def _perfhack(self):
729 return self.ui.configbool('fastannotate', 'perfhack') 763 return self.ui.configbool('fastannotate', 'perfhack')
730 764
731 def _resolvefctx(self, rev, path=None, **kwds): 765 def _resolvefctx(self, rev, path=None, **kwds):
732 return resolvefctx(self.repo, rev, (path or self.path), **kwds) 766 return resolvefctx(self.repo, rev, (path or self.path), **kwds)
767
733 768
734 def _unlinkpaths(paths): 769 def _unlinkpaths(paths):
735 """silent, best-effort unlink""" 770 """silent, best-effort unlink"""
736 for path in paths: 771 for path in paths:
737 try: 772 try:
738 util.unlink(path) 773 util.unlink(path)
739 except OSError: 774 except OSError:
740 pass 775 pass
741 776
777
742 class pathhelper(object): 778 class pathhelper(object):
743 """helper for getting paths for lockfile, linelog and revmap""" 779 """helper for getting paths for lockfile, linelog and revmap"""
744 780
745 def __init__(self, repo, path, opts=defaultopts): 781 def __init__(self, repo, path, opts=defaultopts):
746 # different options use different directories 782 # different options use different directories
747 self._vfspath = os.path.join('fastannotate', 783 self._vfspath = os.path.join(
748 opts.shortstr, encodedir(path)) 784 'fastannotate', opts.shortstr, encodedir(path)
785 )
749 self._repo = repo 786 self._repo = repo
750 787
751 @property 788 @property
752 def dirname(self): 789 def dirname(self):
753 return os.path.dirname(self._repo.vfs.join(self._vfspath)) 790 return os.path.dirname(self._repo.vfs.join(self._vfspath))
760 return lockmod.lock(self._repo.vfs, self._vfspath + '.lock') 797 return lockmod.lock(self._repo.vfs, self._vfspath + '.lock')
761 798
762 @property 799 @property
763 def revmappath(self): 800 def revmappath(self):
764 return self._repo.vfs.join(self._vfspath + '.m') 801 return self._repo.vfs.join(self._vfspath + '.m')
802
765 803
766 @contextlib.contextmanager 804 @contextlib.contextmanager
767 def annotatecontext(repo, path, opts=defaultopts, rebuild=False): 805 def annotatecontext(repo, path, opts=defaultopts, rebuild=False):
768 """context needed to perform (fast) annotate on a file 806 """context needed to perform (fast) annotate on a file
769 807
797 raise 835 raise
798 finally: 836 finally:
799 if actx is not None: 837 if actx is not None:
800 actx.close() 838 actx.close()
801 839
840
802 def fctxannotatecontext(fctx, follow=True, diffopts=None, rebuild=False): 841 def fctxannotatecontext(fctx, follow=True, diffopts=None, rebuild=False):
803 """like annotatecontext but get the context from a fctx. convenient when 842 """like annotatecontext but get the context from a fctx. convenient when
804 used in fctx.annotate 843 used in fctx.annotate
805 """ 844 """
806 repo = fctx._repo 845 repo = fctx._repo