comparison mercurial/context.py @ 30718:ce662ee40d2d

context: add a `blockancestors(fctx, fromline, toline)` function This yields ancestors of `fctx` by only keeping changesets touching the file within specified linerange = (fromline, toline). Matching revisions are found by inspecting the result of `mdiff.allblocks()`, filtered by `mdiff.blocksinrange()`, to find out if there are blocks of type "!" within specified line range. If, at some iteration, an ancestor with an empty line range is encountered, the algorithm stops as it means that the considered block of lines actually has been introduced in the revision of this iteration. Otherwise, we finally yield the initial revision of the file as the block originates from it. When a merge changeset is encountered during ancestors lookup, we consider there's a diff in the current line range as long as there is a diff between the merge changeset and at least one of its parents (in the current line range).
author Denis Laxalde <denis.laxalde@logilab.fr>
date Wed, 28 Dec 2016 23:03:37 +0100
parents 9bf43a72b49d
children 2df983125d37
comparison
equal deleted inserted replaced
30717:3eeb8e138e5c 30718:ce662ee40d2d
1151 # hard for renames 1151 # hard for renames
1152 c = self._filelog.children(self._filenode) 1152 c = self._filelog.children(self._filenode)
1153 return [filectx(self._repo, self._path, fileid=x, 1153 return [filectx(self._repo, self._path, fileid=x,
1154 filelog=self._filelog) for x in c] 1154 filelog=self._filelog) for x in c]
1155 1155
1156 def blockancestors(fctx, fromline, toline):
1157 """Yield ancestors of `fctx` with respect to the block of lines within
1158 `fromline`-`toline` range.
1159 """
1160 def changesrange(fctx1, fctx2, linerange2):
1161 """Return `(diffinrange, linerange1)` where `diffinrange` is True
1162 if diff from fctx2 to fctx1 has changes in linerange2 and
1163 `linerange1` is the new line range for fctx1.
1164 """
1165 diffopts = patch.diffopts(fctx._repo.ui)
1166 blocks = mdiff.allblocks(fctx1.data(), fctx2.data(), diffopts)
1167 filteredblocks, linerange1 = mdiff.blocksinrange(blocks, linerange2)
1168 diffinrange = any(stype == '!' for _, stype in filteredblocks)
1169 return diffinrange, linerange1
1170
1171 visit = {(fctx.linkrev(), fctx.filenode()): (fctx, (fromline, toline))}
1172 while visit:
1173 c, linerange2 = visit.pop(max(visit))
1174 pl = c.parents()
1175 if not pl:
1176 # The block originates from the initial revision.
1177 yield c
1178 continue
1179 inrange = False
1180 for p in pl:
1181 inrangep, linerange1 = changesrange(p, c, linerange2)
1182 inrange = inrange or inrangep
1183 if linerange1[0] == linerange1[1]:
1184 # Parent's linerange is empty, meaning that the block got
1185 # introduced in this revision; no need to go futher in this
1186 # branch.
1187 continue
1188 visit[p.linkrev(), p.filenode()] = p, linerange1
1189 if inrange:
1190 yield c
1191
1156 class committablectx(basectx): 1192 class committablectx(basectx):
1157 """A committablectx object provides common functionality for a context that 1193 """A committablectx object provides common functionality for a context that
1158 wants the ability to commit, e.g. workingctx or memctx.""" 1194 wants the ability to commit, e.g. workingctx or memctx."""
1159 def __init__(self, repo, text="", user=None, date=None, extra=None, 1195 def __init__(self, repo, text="", user=None, date=None, extra=None,
1160 changes=None): 1196 changes=None):