comparison mercurial/context.py @ 36918:5d3abd6a5b25

dagop: extract core algorithm of annotate() from context.py See the previous patch for why.
author Yuya Nishihara <yuya@tcha.org>
date Wed, 28 Feb 2018 15:19:47 -0500
parents 7affcabf561e
children b235bde38a83
comparison
equal deleted inserted replaced
36917:7affcabf561e 36918:5d3abd6a5b25
30 dagop, 30 dagop,
31 encoding, 31 encoding,
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,
39 pathutil, 38 pathutil,
40 phases, 39 phases,
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: