974 in the file, where ctx is the filectx of the node where |
973 in the file, where ctx is the filectx of the node where |
975 that line was last changed; if linenumber parameter is true, number is |
974 that line was last changed; if linenumber parameter is true, number is |
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): |
998 # Cut _descendantrev here to mitigate the penalty of lazy linkrev |
981 # Cut _descendantrev here to mitigate the penalty of lazy linkrev |
999 # adjustment. Otherwise, p._adjustlinkrev() would walk changelog |
982 # adjustment. Otherwise, p._adjustlinkrev() would walk changelog |
1025 inclusive=True) |
1008 inclusive=True) |
1026 else: |
1009 else: |
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 = {} |
1087 c = self |
1018 c = self |
1088 if followfirst: |
1019 if followfirst: |