##// 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 error,
32 error,
33 fileset,
33 fileset,
34 match as matchmod,
34 match as matchmod,
35 mdiff,
36 obsolete as obsmod,
35 obsolete as obsmod,
37 obsutil,
36 obsutil,
38 patch,
37 patch,
@@ -976,22 +975,6 b' class basefilectx(object):'
976 the line number at the first appearance in the managed file, otherwise,
975 the line number at the first appearance in the managed file, otherwise,
977 number has a fixed value of False.
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 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
978 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
996
979
997 def parents(f):
980 def parents(f):
@@ -1027,60 +1010,8 b' class basefilectx(object):'
1027 ac = cl.ancestors([base.rev()], inclusive=True)
1010 ac = cl.ancestors([base.rev()], inclusive=True)
1028 base._ancestrycontext = ac
1011 base._ancestrycontext = ac
1029
1012
1030 # This algorithm would prefer to be recursive, but Python is a
1013 return dagop.annotate(base, parents, linenumber=linenumber,
1031 # bit recursion-hostile. Instead we do an iterative
1014 skiprevs=skiprevs, diffopts=diffopts)
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))
1084
1015
1085 def ancestors(self, followfirst=False):
1016 def ancestors(self, followfirst=False):
1086 visit = {}
1017 visit = {}
@@ -17,6 +17,7 b' from . import ('
17 mdiff,
17 mdiff,
18 node,
18 node,
19 patch,
19 patch,
20 pycompat,
20 smartset,
21 smartset,
21 )
22 )
22
23
@@ -429,6 +430,80 b' def _annotatepair(parents, childfctx, ch'
429 child[0][bk] = attr.evolve(parent[0][ak], skip=True)
430 child[0][bk] = attr.evolve(parent[0][ak], skip=True)
430 return child
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 def toposort(revs, parentsfunc, firstbranch=()):
507 def toposort(revs, parentsfunc, firstbranch=()):
433 """Yield revisions from heads to roots one (topo) branch at a time.
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