##// END OF EJS Templates
dagop: extract core algorithm of annotate() from context.py...
Yuya Nishihara -
r36936:5d3abd6a default
parent child Browse files
Show More
@@ -32,7 +32,6 b' from . import ('
32 32 error,
33 33 fileset,
34 34 match as matchmod,
35 mdiff,
36 35 obsolete as obsmod,
37 36 obsutil,
38 37 patch,
@@ -976,22 +975,6 b' class basefilectx(object):'
976 975 the line number at the first appearance in the managed file, otherwise,
977 976 number has a fixed value of False.
978 977 '''
979 annotateline = dagop.annotateline
980 _annotatepair = dagop._annotatepair
981
982 def lines(text):
983 if text.endswith("\n"):
984 return text.count("\n")
985 return text.count("\n") + int(bool(text))
986
987 if linenumber:
988 def decorate(text, rev):
989 return ([annotateline(fctx=rev, lineno=i)
990 for i in xrange(1, lines(text) + 1)], text)
991 else:
992 def decorate(text, rev):
993 return ([annotateline(fctx=rev)] * lines(text), text)
994
995 978 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
996 979
997 980 def parents(f):
@@ -1027,60 +1010,8 b' class basefilectx(object):'
1027 1010 ac = cl.ancestors([base.rev()], inclusive=True)
1028 1011 base._ancestrycontext = ac
1029 1012
1030 # This algorithm would prefer to be recursive, but Python is a
1031 # bit recursion-hostile. Instead we do an iterative
1032 # depth-first search.
1033
1034 # 1st DFS pre-calculates pcache and needed
1035 visit = [base]
1036 pcache = {}
1037 needed = {base: 1}
1038 while visit:
1039 f = visit.pop()
1040 if f in pcache:
1041 continue
1042 pl = parents(f)
1043 pcache[f] = pl
1044 for p in pl:
1045 needed[p] = needed.get(p, 0) + 1
1046 if p not in pcache:
1047 visit.append(p)
1048
1049 # 2nd DFS does the actual annotate
1050 visit[:] = [base]
1051 hist = {}
1052 while visit:
1053 f = visit[-1]
1054 if f in hist:
1055 visit.pop()
1056 continue
1057
1058 ready = True
1059 pl = pcache[f]
1060 for p in pl:
1061 if p not in hist:
1062 ready = False
1063 visit.append(p)
1064 if ready:
1065 visit.pop()
1066 curr = decorate(f.data(), f)
1067 skipchild = False
1068 if skiprevs is not None:
1069 skipchild = f._changeid in skiprevs
1070 curr = _annotatepair([hist[p] for p in pl], f, curr, skipchild,
1071 diffopts)
1072 for p in pl:
1073 if needed[p] == 1:
1074 del hist[p]
1075 del needed[p]
1076 else:
1077 needed[p] -= 1
1078
1079 hist[f] = curr
1080 del pcache[f]
1081
1082 lineattrs, text = hist[base]
1083 return pycompat.ziplist(lineattrs, mdiff.splitnewlines(text))
1013 return dagop.annotate(base, parents, linenumber=linenumber,
1014 skiprevs=skiprevs, diffopts=diffopts)
1084 1015
1085 1016 def ancestors(self, followfirst=False):
1086 1017 visit = {}
@@ -17,6 +17,7 b' from . import ('
17 17 mdiff,
18 18 node,
19 19 patch,
20 pycompat,
20 21 smartset,
21 22 )
22 23
@@ -429,6 +430,80 b' def _annotatepair(parents, childfctx, ch'
429 430 child[0][bk] = attr.evolve(parent[0][ak], skip=True)
430 431 return child
431 432
433 def annotate(base, parents, linenumber=False, skiprevs=None, diffopts=None):
434 """Core algorithm for filectx.annotate()
435
436 `parents(fctx)` is a function returning a list of parent filectxs.
437 """
438
439 def lines(text):
440 if text.endswith("\n"):
441 return text.count("\n")
442 return text.count("\n") + int(bool(text))
443
444 if linenumber:
445 def decorate(text, rev):
446 return ([annotateline(fctx=rev, lineno=i)
447 for i in xrange(1, lines(text) + 1)], text)
448 else:
449 def decorate(text, rev):
450 return ([annotateline(fctx=rev)] * lines(text), text)
451
452 # This algorithm would prefer to be recursive, but Python is a
453 # bit recursion-hostile. Instead we do an iterative
454 # depth-first search.
455
456 # 1st DFS pre-calculates pcache and needed
457 visit = [base]
458 pcache = {}
459 needed = {base: 1}
460 while visit:
461 f = visit.pop()
462 if f in pcache:
463 continue
464 pl = parents(f)
465 pcache[f] = pl
466 for p in pl:
467 needed[p] = needed.get(p, 0) + 1
468 if p not in pcache:
469 visit.append(p)
470
471 # 2nd DFS does the actual annotate
472 visit[:] = [base]
473 hist = {}
474 while visit:
475 f = visit[-1]
476 if f in hist:
477 visit.pop()
478 continue
479
480 ready = True
481 pl = pcache[f]
482 for p in pl:
483 if p not in hist:
484 ready = False
485 visit.append(p)
486 if ready:
487 visit.pop()
488 curr = decorate(f.data(), f)
489 skipchild = False
490 if skiprevs is not None:
491 skipchild = f._changeid in skiprevs
492 curr = _annotatepair([hist[p] for p in pl], f, curr, skipchild,
493 diffopts)
494 for p in pl:
495 if needed[p] == 1:
496 del hist[p]
497 del needed[p]
498 else:
499 needed[p] -= 1
500
501 hist[f] = curr
502 del pcache[f]
503
504 lineattrs, text = hist[base]
505 return pycompat.ziplist(lineattrs, mdiff.splitnewlines(text))
506
432 507 def toposort(revs, parentsfunc, firstbranch=()):
433 508 """Yield revisions from heads to roots one (topo) branch at a time.
434 509
General Comments 0
You need to be logged in to leave comments. Login now