comparison hgext/fastannotate/context.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children eef9a2d67051
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
50 pl = [p for p in pl if p.path() == f.path()] 50 pl = [p for p in pl if p.path() == f.path()]
51 51
52 # renamed filectx won't have a filelog yet, so set it 52 # renamed filectx won't have a filelog yet, so set it
53 # from the cache to save time 53 # from the cache to save time
54 for p in pl: 54 for p in pl:
55 if not '_filelog' in p.__dict__: 55 if not b'_filelog' in p.__dict__:
56 p._filelog = _getflog(f._repo, p.path()) 56 p._filelog = _getflog(f._repo, p.path())
57 57
58 return pl 58 return pl
59 59
60 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(b'\n')
66 if text and not text.endswith('\n'): 66 if text and not text.endswith(b'\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
71 # extracted from mercurial.context.basefilectx.annotate. slightly modified 71 # extracted from mercurial.context.basefilectx.annotate. slightly modified
73 # calculating diff here. 73 # calculating diff here.
74 def _pair(parent, child, blocks): 74 def _pair(parent, child, blocks):
75 for (a1, a2, b1, b2), t in blocks: 75 for (a1, a2, b1, b2), t in blocks:
76 # Changed blocks ('!') or blocks made only of blank lines ('~') 76 # Changed blocks ('!') or blocks made only of blank lines ('~')
77 # belong to the child. 77 # belong to the child.
78 if t == '=': 78 if t == b'=':
79 child[0][b1:b2] = parent[0][a1:a2] 79 child[0][b1:b2] = parent[0][a1:a2]
80 return child 80 return child
81 81
82 82
83 # 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)
117 fctx = ctx[path] 117 fctx = ctx[path]
118 else: 118 else:
119 fctx = repo.filectx(path, changeid=ctx.rev()) 119 fctx = repo.filectx(path, changeid=ctx.rev())
120 else: 120 else:
121 fctx = ctx[path] 121 fctx = ctx[path]
122 if adjustctx == 'linkrev': 122 if adjustctx == b'linkrev':
123 introrev = fctx.linkrev() 123 introrev = fctx.linkrev()
124 else: 124 else:
125 introrev = fctx.introrev() 125 introrev = fctx.introrev()
126 if introrev != ctx.rev(): 126 if introrev != ctx.rev():
127 fctx._changeid = introrev 127 fctx._changeid = introrev
130 130
131 131
132 # like mercurial.store.encodedir, but use linelog suffixes: .m, .l, .lock 132 # like mercurial.store.encodedir, but use linelog suffixes: .m, .l, .lock
133 def encodedir(path): 133 def encodedir(path):
134 return ( 134 return (
135 path.replace('.hg/', '.hg.hg/') 135 path.replace(b'.hg/', b'.hg.hg/')
136 .replace('.l/', '.l.hg/') 136 .replace(b'.l/', b'.l.hg/')
137 .replace('.m/', '.m.hg/') 137 .replace(b'.m/', b'.m.hg/')
138 .replace('.lock/', '.lock.hg/') 138 .replace(b'.lock/', b'.lock.hg/')
139 ) 139 )
140 140
141 141
142 def hashdiffopts(diffopts): 142 def hashdiffopts(diffopts):
143 diffoptstr = stringutil.pprint( 143 diffoptstr = stringutil.pprint(
155 followrename: follow renames, like "hg annotate -f" 155 followrename: follow renames, like "hg annotate -f"
156 followmerge: follow p2 of a merge changeset, otherwise p2 is ignored 156 followmerge: follow p2 of a merge changeset, otherwise p2 is ignored
157 """ 157 """
158 158
159 defaults = { 159 defaults = {
160 'diffopts': None, 160 b'diffopts': None,
161 'followrename': True, 161 b'followrename': True,
162 'followmerge': True, 162 b'followmerge': True,
163 } 163 }
164 164
165 def __init__(self, **opts): 165 def __init__(self, **opts):
166 opts = pycompat.byteskwargs(opts) 166 opts = pycompat.byteskwargs(opts)
167 for k, v in self.defaults.iteritems(): 167 for k, v in self.defaults.iteritems():
168 setattr(self, k, opts.get(k, v)) 168 setattr(self, k, opts.get(k, v))
169 169
170 @util.propertycache 170 @util.propertycache
171 def shortstr(self): 171 def shortstr(self):
172 """represent opts in a short string, suitable for a directory name""" 172 """represent opts in a short string, suitable for a directory name"""
173 result = '' 173 result = b''
174 if not self.followrename: 174 if not self.followrename:
175 result += 'r0' 175 result += b'r0'
176 if not self.followmerge: 176 if not self.followmerge:
177 result += 'm0' 177 result += b'm0'
178 if self.diffopts is not None: 178 if self.diffopts is not None:
179 assert isinstance(self.diffopts, mdiff.diffopts) 179 assert isinstance(self.diffopts, mdiff.diffopts)
180 diffopthash = hashdiffopts(self.diffopts) 180 diffopthash = hashdiffopts(self.diffopts)
181 if diffopthash != _defaultdiffopthash: 181 if diffopthash != _defaultdiffopthash:
182 result += 'i' + diffopthash 182 result += b'i' + diffopthash
183 return result or 'default' 183 return result or b'default'
184 184
185 185
186 defaultopts = annotateopts() 186 defaultopts = annotateopts()
187 187
188 188
204 204
205 @property 205 @property
206 def linelog(self): 206 def linelog(self):
207 if self._linelog is None: 207 if self._linelog is None:
208 if os.path.exists(self.linelogpath): 208 if os.path.exists(self.linelogpath):
209 with open(self.linelogpath, 'rb') as f: 209 with open(self.linelogpath, b'rb') as f:
210 try: 210 try:
211 self._linelog = linelogmod.linelog.fromdata(f.read()) 211 self._linelog = linelogmod.linelog.fromdata(f.read())
212 except linelogmod.LineLogError: 212 except linelogmod.LineLogError:
213 self._linelog = linelogmod.linelog() 213 self._linelog = linelogmod.linelog()
214 else: 214 else:
224 def close(self): 224 def close(self):
225 if self._revmap is not None: 225 if self._revmap is not None:
226 self._revmap.flush() 226 self._revmap.flush()
227 self._revmap = None 227 self._revmap = None
228 if self._linelog is not None: 228 if self._linelog is not None:
229 with open(self.linelogpath, 'wb') as f: 229 with open(self.linelogpath, b'wb') as f:
230 f.write(self._linelog.encode()) 230 f.write(self._linelog.encode())
231 self._linelog = None 231 self._linelog = None
232 232
233 __del__ = close 233 __del__ = close
234 234
306 # fast path: if rev is in the main branch already 306 # fast path: if rev is in the main branch already
307 directly, revfctx = self.canannotatedirectly(rev) 307 directly, revfctx = self.canannotatedirectly(rev)
308 if directly: 308 if directly:
309 if self.ui.debugflag: 309 if self.ui.debugflag:
310 self.ui.debug( 310 self.ui.debug(
311 'fastannotate: %s: using fast path ' 311 b'fastannotate: %s: using fast path '
312 '(resolved fctx: %s)\n' 312 b'(resolved fctx: %s)\n'
313 % ( 313 % (
314 self.path, 314 self.path,
315 stringutil.pprint(util.safehasattr(revfctx, 'node')), 315 stringutil.pprint(util.safehasattr(revfctx, b'node')),
316 ) 316 )
317 ) 317 )
318 return self.annotatedirectly(revfctx, showpath, showlines) 318 return self.annotatedirectly(revfctx, showpath, showlines)
319 319
320 # resolve master 320 # resolve master
354 # "hist", reducing its memory usage otherwise could be huge. 354 # "hist", reducing its memory usage otherwise could be huge.
355 initvisit = [revfctx] 355 initvisit = [revfctx]
356 if masterfctx: 356 if masterfctx:
357 if masterfctx.rev() is None: 357 if masterfctx.rev() is None:
358 raise error.Abort( 358 raise error.Abort(
359 _('cannot update linelog to wdir()'), 359 _(b'cannot update linelog to wdir()'),
360 hint=_('set fastannotate.mainbranch'), 360 hint=_(b'set fastannotate.mainbranch'),
361 ) 361 )
362 initvisit.append(masterfctx) 362 initvisit.append(masterfctx)
363 visit = initvisit[:] 363 visit = initvisit[:]
364 pcache = {} 364 pcache = {}
365 needed = {revfctx: 1} 365 needed = {revfctx: 1}
401 self._checklastmasterhead(f) 401 self._checklastmasterhead(f)
402 402
403 if self.ui.debugflag: 403 if self.ui.debugflag:
404 if newmainbranch: 404 if newmainbranch:
405 self.ui.debug( 405 self.ui.debug(
406 'fastannotate: %s: %d new changesets in the main' 406 b'fastannotate: %s: %d new changesets in the main'
407 ' branch\n' % (self.path, len(newmainbranch)) 407 b' branch\n' % (self.path, len(newmainbranch))
408 ) 408 )
409 elif not hist: # no joints, no updates 409 elif not hist: # no joints, no updates
410 self.ui.debug( 410 self.ui.debug(
411 'fastannotate: %s: linelog cannot help in ' 411 b'fastannotate: %s: linelog cannot help in '
412 'annotating this revision\n' % self.path 412 b'annotating this revision\n' % self.path
413 ) 413 )
414 414
415 # prepare annotateresult so we can update linelog incrementally 415 # prepare annotateresult so we can update linelog incrementally
416 self.linelog.annotate(self.linelog.maxrev) 416 self.linelog.annotate(self.linelog.maxrev)
417 417
418 # 3rd DFS does the actual annotate 418 # 3rd DFS does the actual annotate
419 visit = initvisit[:] 419 visit = initvisit[:]
420 progress = self.ui.makeprogress( 420 progress = self.ui.makeprogress(
421 'building cache', total=len(newmainbranch) 421 b'building cache', total=len(newmainbranch)
422 ) 422 )
423 while visit: 423 while visit:
424 f = visit[-1] 424 f = visit[-1]
425 if f in hist: 425 if f in hist:
426 visit.pop() 426 visit.pop()
461 progress.increment() 461 progress.increment()
462 bannotated = None 462 bannotated = None
463 if len(pl) == 2 and self.opts.followmerge: # merge 463 if len(pl) == 2 and self.opts.followmerge: # merge
464 bannotated = curr[0] 464 bannotated = curr[0]
465 if blocks is None: # no parents, add an empty one 465 if blocks is None: # no parents, add an empty one
466 blocks = list(self._diffblocks('', curr[1])) 466 blocks = list(self._diffblocks(b'', curr[1]))
467 self._appendrev(f, blocks, bannotated) 467 self._appendrev(f, blocks, bannotated)
468 elif showpath: # not append linelog, but we need to record path 468 elif showpath: # not append linelog, but we need to record path
469 self._node2path[f.node()] = f.path() 469 self._node2path[f.node()] = f.path()
470 470
471 progress.complete() 471 progress.complete()
488 if not isinstance(rev, int) and rev is not None: 488 if not isinstance(rev, int) and rev is not None:
489 hsh = {20: bytes, 40: node.bin}.get(len(rev), lambda x: None)(rev) 489 hsh = {20: bytes, 40: node.bin}.get(len(rev), lambda x: None)(rev)
490 if hsh is not None and (hsh, self.path) in self.revmap: 490 if hsh is not None and (hsh, self.path) in self.revmap:
491 f = hsh 491 f = hsh
492 if f is None: 492 if f is None:
493 adjustctx = 'linkrev' if self._perfhack else True 493 adjustctx = b'linkrev' if self._perfhack else True
494 f = self._resolvefctx(rev, adjustctx=adjustctx, resolverev=True) 494 f = self._resolvefctx(rev, adjustctx=adjustctx, resolverev=True)
495 result = f in self.revmap 495 result = f in self.revmap
496 if not result and self._perfhack: 496 if not result and self._perfhack:
497 # redo the resolution without perfhack - as we are going to 497 # redo the resolution without perfhack - as we are going to
498 # do write operations, we need a correct fctx. 498 # do write operations, we need a correct fctx.
509 revfctx = self._resolvefctx(rev, resolverev=True, adjustctx=True) 509 revfctx = self._resolvefctx(rev, resolverev=True, adjustctx=True)
510 510
511 # find a chain from rev to anything in the mainbranch 511 # find a chain from rev to anything in the mainbranch
512 if revfctx not in self.revmap: 512 if revfctx not in self.revmap:
513 chain = [revfctx] 513 chain = [revfctx]
514 a = '' 514 a = b''
515 while True: 515 while True:
516 f = chain[-1] 516 f = chain[-1]
517 pl = self._parentfunc(f) 517 pl = self._parentfunc(f)
518 if not pl: 518 if not pl:
519 break 519 break
587 for (rev, linenum), idxs in key2idxs.iteritems(): 587 for (rev, linenum), idxs in key2idxs.iteritems():
588 if rev not in revlines: 588 if rev not in revlines:
589 hsh = annotateresult[idxs[0]][0] 589 hsh = annotateresult[idxs[0]][0]
590 if self.ui.debugflag: 590 if self.ui.debugflag:
591 self.ui.debug( 591 self.ui.debug(
592 'fastannotate: reading %s line #%d ' 592 b'fastannotate: reading %s line #%d '
593 'to resolve lines %r\n' 593 b'to resolve lines %r\n'
594 % (node.short(hsh), linenum, idxs) 594 % (node.short(hsh), linenum, idxs)
595 ) 595 )
596 fctx = self._resolvefctx(hsh, revmap.rev2path(rev)) 596 fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
597 lines = mdiff.splitnewlines(fctx.data()) 597 lines = mdiff.splitnewlines(fctx.data())
598 revlines[rev] = lines 598 revlines[rev] = lines
601 assert all(x is not None for x in result) 601 assert all(x is not None for x in result)
602 return result 602 return result
603 603
604 # 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
605 self.ui.debug( 605 self.ui.debug(
606 'fastannotate: annotate %s to resolve lines\n' % node.short(hsh) 606 b'fastannotate: annotate %s to resolve lines\n'
607 % node.short(hsh)
607 ) 608 )
608 linelog.annotate(rev) 609 linelog.annotate(rev)
609 fctx = self._resolvefctx(hsh, revmap.rev2path(rev)) 610 fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
610 annotated = linelog.annotateresult 611 annotated = linelog.annotateresult
611 lines = mdiff.splitnewlines(fctx.data()) 612 lines = mdiff.splitnewlines(fctx.data())
612 if len(lines) != len(annotated): 613 if len(lines) != len(annotated):
613 raise faerror.CorruptedFileError('unexpected annotated lines') 614 raise faerror.CorruptedFileError(b'unexpected annotated lines')
614 # resolve lines from the annotate result 615 # resolve lines from the annotate result
615 for i, line in enumerate(lines): 616 for i, line in enumerate(lines):
616 k = annotated[i] 617 k = annotated[i]
617 if k in key2idxs: 618 if k in key2idxs:
618 for idx in key2idxs[k]: 619 for idx in key2idxs[k]:
631 else: 632 else:
632 hsh = f.node() 633 hsh = f.node()
633 llrev = self.revmap.hsh2rev(hsh) 634 llrev = self.revmap.hsh2rev(hsh)
634 if not llrev: 635 if not llrev:
635 raise faerror.CorruptedFileError( 636 raise faerror.CorruptedFileError(
636 '%s is not in revmap' % node.hex(hsh) 637 b'%s is not in revmap' % node.hex(hsh)
637 ) 638 )
638 if (self.revmap.rev2flag(llrev) & revmapmod.sidebranchflag) != 0: 639 if (self.revmap.rev2flag(llrev) & revmapmod.sidebranchflag) != 0:
639 raise faerror.CorruptedFileError( 640 raise faerror.CorruptedFileError(
640 '%s is not in revmap mainbranch' % node.hex(hsh) 641 b'%s is not in revmap mainbranch' % node.hex(hsh)
641 ) 642 )
642 self.linelog.annotate(llrev) 643 self.linelog.annotate(llrev)
643 result = [ 644 result = [
644 (self.revmap.rev2hsh(r), l) for r, l in self.linelog.annotateresult 645 (self.revmap.rev2hsh(r), l) for r, l in self.linelog.annotateresult
645 ] 646 ]
675 676
676 def getllrev(f): 677 def getllrev(f):
677 """(fctx) -> int""" 678 """(fctx) -> int"""
678 # f should not be a linelog revision 679 # f should not be a linelog revision
679 if isinstance(f, int): 680 if isinstance(f, int):
680 raise error.ProgrammingError('f should not be an int') 681 raise error.ProgrammingError(b'f should not be an int')
681 # f is a fctx, allocate linelog rev on demand 682 # f is a fctx, allocate linelog rev on demand
682 hsh = f.node() 683 hsh = f.node()
683 rev = revmap.hsh2rev(hsh) 684 rev = revmap.hsh2rev(hsh)
684 if rev is None: 685 if rev is None:
685 rev = revmap.append(hsh, sidebranch=True, path=f.path()) 686 rev = revmap.append(hsh, sidebranch=True, path=f.path())
688 # append sidebranch revisions to revmap 689 # append sidebranch revisions to revmap
689 siderevs = [] 690 siderevs = []
690 siderevmap = {} # node: int 691 siderevmap = {} # node: int
691 if bannotated is not None: 692 if bannotated is not None:
692 for (a1, a2, b1, b2), op in blocks: 693 for (a1, a2, b1, b2), op in blocks:
693 if op != '=': 694 if op != b'=':
694 # f could be either linelong rev, or fctx. 695 # f could be either linelong rev, or fctx.
695 siderevs += [ 696 siderevs += [
696 f 697 f
697 for f, l in bannotated[b1:b2] 698 for f, l in bannotated[b1:b2]
698 if not isinstance(f, int) 699 if not isinstance(f, int)
706 # the changeset in the main branch, could be a merge 707 # the changeset in the main branch, could be a merge
707 llrev = revmap.append(fctx.node(), path=fctx.path()) 708 llrev = revmap.append(fctx.node(), path=fctx.path())
708 siderevmap[fctx] = llrev 709 siderevmap[fctx] = llrev
709 710
710 for (a1, a2, b1, b2), op in reversed(blocks): 711 for (a1, a2, b1, b2), op in reversed(blocks):
711 if op == '=': 712 if op == b'=':
712 continue 713 continue
713 if bannotated is None: 714 if bannotated is None:
714 linelog.replacelines(llrev, a1, a2, b1, b2) 715 linelog.replacelines(llrev, a1, a2, b1, b2)
715 else: 716 else:
716 blines = [ 717 blines = [
758 759
759 return parents 760 return parents
760 761
761 @util.propertycache 762 @util.propertycache
762 def _perfhack(self): 763 def _perfhack(self):
763 return self.ui.configbool('fastannotate', 'perfhack') 764 return self.ui.configbool(b'fastannotate', b'perfhack')
764 765
765 def _resolvefctx(self, rev, path=None, **kwds): 766 def _resolvefctx(self, rev, path=None, **kwds):
766 return resolvefctx(self.repo, rev, (path or self.path), **kwds) 767 return resolvefctx(self.repo, rev, (path or self.path), **kwds)
767 768
768 769
779 """helper for getting paths for lockfile, linelog and revmap""" 780 """helper for getting paths for lockfile, linelog and revmap"""
780 781
781 def __init__(self, repo, path, opts=defaultopts): 782 def __init__(self, repo, path, opts=defaultopts):
782 # different options use different directories 783 # different options use different directories
783 self._vfspath = os.path.join( 784 self._vfspath = os.path.join(
784 'fastannotate', opts.shortstr, encodedir(path) 785 b'fastannotate', opts.shortstr, encodedir(path)
785 ) 786 )
786 self._repo = repo 787 self._repo = repo
787 788
788 @property 789 @property
789 def dirname(self): 790 def dirname(self):
790 return os.path.dirname(self._repo.vfs.join(self._vfspath)) 791 return os.path.dirname(self._repo.vfs.join(self._vfspath))
791 792
792 @property 793 @property
793 def linelogpath(self): 794 def linelogpath(self):
794 return self._repo.vfs.join(self._vfspath + '.l') 795 return self._repo.vfs.join(self._vfspath + b'.l')
795 796
796 def lock(self): 797 def lock(self):
797 return lockmod.lock(self._repo.vfs, self._vfspath + '.lock') 798 return lockmod.lock(self._repo.vfs, self._vfspath + b'.lock')
798 799
799 @property 800 @property
800 def revmappath(self): 801 def revmappath(self):
801 return self._repo.vfs.join(self._vfspath + '.m') 802 return self._repo.vfs.join(self._vfspath + b'.m')
802 803
803 804
804 @contextlib.contextmanager 805 @contextlib.contextmanager
805 def annotatecontext(repo, path, opts=defaultopts, rebuild=False): 806 def annotatecontext(repo, path, opts=defaultopts, rebuild=False):
806 """context needed to perform (fast) annotate on a file 807 """context needed to perform (fast) annotate on a file
829 actx.rebuild() 830 actx.rebuild()
830 yield actx 831 yield actx
831 except Exception: 832 except Exception:
832 if actx is not None: 833 if actx is not None:
833 actx.rebuild() 834 actx.rebuild()
834 repo.ui.debug('fastannotate: %s: cache broken and deleted\n' % path) 835 repo.ui.debug(b'fastannotate: %s: cache broken and deleted\n' % path)
835 raise 836 raise
836 finally: 837 finally:
837 if actx is not None: 838 if actx is not None:
838 actx.close() 839 actx.close()
839 840
842 """like annotatecontext but get the context from a fctx. convenient when 843 """like annotatecontext but get the context from a fctx. convenient when
843 used in fctx.annotate 844 used in fctx.annotate
844 """ 845 """
845 repo = fctx._repo 846 repo = fctx._repo
846 path = fctx._path 847 path = fctx._path
847 if repo.ui.configbool('fastannotate', 'forcefollow', True): 848 if repo.ui.configbool(b'fastannotate', b'forcefollow', True):
848 follow = True 849 follow = True
849 aopts = annotateopts(diffopts=diffopts, followrename=follow) 850 aopts = annotateopts(diffopts=diffopts, followrename=follow)
850 return annotatecontext(repo, path, aopts, rebuild) 851 return annotatecontext(repo, path, aopts, rebuild)