##// END OF EJS Templates
annotate: add core algorithm to skip a rev...
Siddharth Agarwal -
r32485:05abc47f default
parent child Browse files
Show More
@@ -1044,7 +1044,8 b' class basefilectx(object):'
1044 if ready:
1044 if ready:
1045 visit.pop()
1045 visit.pop()
1046 curr = decorate(f.data(), f)
1046 curr = decorate(f.data(), f)
1047 curr = _annotatepair([hist[p] for p in pl], curr, diffopts)
1047 curr = _annotatepair([hist[p] for p in pl], f, curr, False,
1048 diffopts)
1048 for p in pl:
1049 for p in pl:
1049 if needed[p] == 1:
1050 if needed[p] == 1:
1050 del hist[p]
1051 del hist[p]
@@ -1073,9 +1074,72 b' class basefilectx(object):'
1073 c = visit.pop(max(visit))
1074 c = visit.pop(max(visit))
1074 yield c
1075 yield c
1075
1076
1076 def _annotatepair(parents, child, diffopts):
1077 def _annotatepair(parents, childfctx, child, skipchild, diffopts):
1078 r'''
1079 Given parent and child fctxes and annotate data for parents, for all lines
1080 in either parent that match the child, annotate the child with the parent's
1081 data.
1082
1083 Additionally, if `skipchild` is True, replace all other lines with parent
1084 annotate data as well such that child is never blamed for any lines.
1085
1086 >>> oldfctx = 'old'
1087 >>> p1fctx, p2fctx, childfctx = 'p1', 'p2', 'c'
1088 >>> olddata = 'a\nb\n'
1089 >>> p1data = 'a\nb\nc\n'
1090 >>> p2data = 'a\nc\nd\n'
1091 >>> childdata = 'a\nb2\nc\nc2\nd\n'
1092 >>> diffopts = mdiff.diffopts()
1093
1094 >>> def decorate(text, rev):
1095 ... return ([(rev, i) for i in xrange(1, text.count('\n') + 1)], text)
1096
1097 Basic usage:
1098
1099 >>> oldann = decorate(olddata, oldfctx)
1100 >>> p1ann = decorate(p1data, p1fctx)
1101 >>> p1ann = _annotatepair([oldann], p1fctx, p1ann, False, diffopts)
1102 >>> p1ann[0]
1103 [('old', 1), ('old', 2), ('p1', 3)]
1104 >>> p2ann = decorate(p2data, p2fctx)
1105 >>> p2ann = _annotatepair([oldann], p2fctx, p2ann, False, diffopts)
1106 >>> p2ann[0]
1107 [('old', 1), ('p2', 2), ('p2', 3)]
1108
1109 Test with multiple parents (note the difference caused by ordering):
1110
1111 >>> childann = decorate(childdata, childfctx)
1112 >>> childann = _annotatepair([p1ann, p2ann], childfctx, childann, False,
1113 ... diffopts)
1114 >>> childann[0]
1115 [('old', 1), ('c', 2), ('p2', 2), ('c', 4), ('p2', 3)]
1116
1117 >>> childann = decorate(childdata, childfctx)
1118 >>> childann = _annotatepair([p2ann, p1ann], childfctx, childann, False,
1119 ... diffopts)
1120 >>> childann[0]
1121 [('old', 1), ('c', 2), ('p1', 3), ('c', 4), ('p2', 3)]
1122
1123 Test with skipchild (note the difference caused by ordering):
1124
1125 >>> childann = decorate(childdata, childfctx)
1126 >>> childann = _annotatepair([p1ann, p2ann], childfctx, childann, True,
1127 ... diffopts)
1128 >>> childann[0]
1129 [('old', 1), ('old', 2), ('p2', 2), ('p2', 2), ('p2', 3)]
1130
1131 >>> childann = decorate(childdata, childfctx)
1132 >>> childann = _annotatepair([p2ann, p1ann], childfctx, childann, True,
1133 ... diffopts)
1134 >>> childann[0]
1135 [('old', 1), ('old', 2), ('p1', 3), ('p1', 3), ('p2', 3)]
1136 '''
1077 pblocks = [(parent, mdiff.allblocks(parent[1], child[1], opts=diffopts))
1137 pblocks = [(parent, mdiff.allblocks(parent[1], child[1], opts=diffopts))
1078 for parent in parents]
1138 for parent in parents]
1139
1140 if skipchild:
1141 # Need to iterate over the blocks twice -- make it a list
1142 pblocks = [(p, list(blocks)) for (p, blocks) in pblocks]
1079 # Mercurial currently prefers p2 over p1 for annotate.
1143 # Mercurial currently prefers p2 over p1 for annotate.
1080 # TODO: change this?
1144 # TODO: change this?
1081 for parent, blocks in pblocks:
1145 for parent, blocks in pblocks:
@@ -1084,6 +1148,40 b' def _annotatepair(parents, child, diffop'
1084 # belong to the child.
1148 # belong to the child.
1085 if t == '=':
1149 if t == '=':
1086 child[0][b1:b2] = parent[0][a1:a2]
1150 child[0][b1:b2] = parent[0][a1:a2]
1151
1152 if skipchild:
1153 # Now try and match up anything that couldn't be matched,
1154 # Reversing pblocks maintains bias towards p2, matching above
1155 # behavior.
1156 pblocks.reverse()
1157
1158 # The heuristics are:
1159 # * Work on blocks of changed lines (effectively diff hunks with -U0).
1160 # This could potentially be smarter but works well enough.
1161 # * For a non-matching section, do a best-effort fit. Match lines in
1162 # diff hunks 1:1, dropping lines as necessary.
1163 # * Repeat the last line as a last resort.
1164
1165 # First, replace as much as possible without repeating the last line.
1166 remaining = [(parent, []) for parent, _blocks in pblocks]
1167 for idx, (parent, blocks) in enumerate(pblocks):
1168 for (a1, a2, b1, b2), _t in blocks:
1169 if a2 - a1 >= b2 - b1:
1170 for bk in xrange(b1, b2):
1171 if child[0][bk][0] == childfctx:
1172 ak = min(a1 + (bk - b1), a2 - 1)
1173 child[0][bk] = parent[0][ak]
1174 else:
1175 remaining[idx][1].append((a1, a2, b1, b2))
1176
1177 # Then, look at anything left, which might involve repeating the last
1178 # line.
1179 for parent, blocks in remaining:
1180 for a1, a2, b1, b2 in blocks:
1181 for bk in xrange(b1, b2):
1182 if child[0][bk][0] == childfctx:
1183 ak = min(a1 + (bk - b1), a2 - 1)
1184 child[0][bk] = parent[0][ak]
1087 return child
1185 return child
1088
1186
1089 class filectx(basefilectx):
1187 class filectx(basefilectx):
@@ -25,6 +25,7 b" testmod('mercurial.changegroup')"
25 testmod('mercurial.changelog')
25 testmod('mercurial.changelog')
26 testmod('mercurial.color')
26 testmod('mercurial.color')
27 testmod('mercurial.config')
27 testmod('mercurial.config')
28 testmod('mercurial.context')
28 testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE)
29 testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE)
29 testmod('mercurial.dispatch')
30 testmod('mercurial.dispatch')
30 testmod('mercurial.encoding')
31 testmod('mercurial.encoding')
General Comments 0
You need to be logged in to leave comments. Login now