Mercurial > hg
diff mercurial/dagop.py @ 32904:582080a4a812
dagop: move blockancestors() and blockdescendants() from context
context.py seems not a good place to host these functions.
% wc -l mercurial/context.py mercurial/dagop.py
2306 mercurial/context.py
424 mercurial/dagop.py
2730 total
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sun, 19 Feb 2017 19:37:14 +0900 |
parents | 27932a76a88d |
children | b9e2269aeff8 |
line wrap: on
line diff
--- a/mercurial/dagop.py Sun Oct 16 18:03:24 2016 +0900 +++ b/mercurial/dagop.py Sun Feb 19 19:37:14 2017 +0900 @@ -11,7 +11,9 @@ from . import ( error, + mdiff, node, + patch, smartset, ) @@ -140,6 +142,89 @@ revs.sort() return revs +def _changesrange(fctx1, fctx2, linerange2, diffopts): + """Return `(diffinrange, linerange1)` where `diffinrange` is True + if diff from fctx2 to fctx1 has changes in linerange2 and + `linerange1` is the new line range for fctx1. + """ + blocks = mdiff.allblocks(fctx1.data(), fctx2.data(), diffopts) + filteredblocks, linerange1 = mdiff.blocksinrange(blocks, linerange2) + diffinrange = any(stype == '!' for _, stype in filteredblocks) + return diffinrange, linerange1 + +def blockancestors(fctx, fromline, toline, followfirst=False): + """Yield ancestors of `fctx` with respect to the block of lines within + `fromline`-`toline` range. + """ + diffopts = patch.diffopts(fctx._repo.ui) + introrev = fctx.introrev() + if fctx.rev() != introrev: + fctx = fctx.filectx(fctx.filenode(), changeid=introrev) + visit = {(fctx.linkrev(), fctx.filenode()): (fctx, (fromline, toline))} + while visit: + c, linerange2 = visit.pop(max(visit)) + pl = c.parents() + if followfirst: + pl = pl[:1] + if not pl: + # The block originates from the initial revision. + yield c, linerange2 + continue + inrange = False + for p in pl: + inrangep, linerange1 = _changesrange(p, c, linerange2, diffopts) + inrange = inrange or inrangep + if linerange1[0] == linerange1[1]: + # Parent's linerange is empty, meaning that the block got + # introduced in this revision; no need to go futher in this + # branch. + continue + # Set _descendantrev with 'c' (a known descendant) so that, when + # _adjustlinkrev is called for 'p', it receives this descendant + # (as srcrev) instead possibly topmost introrev. + p._descendantrev = c.rev() + visit[p.linkrev(), p.filenode()] = p, linerange1 + if inrange: + yield c, linerange2 + +def blockdescendants(fctx, fromline, toline): + """Yield descendants of `fctx` with respect to the block of lines within + `fromline`-`toline` range. + """ + # First possibly yield 'fctx' if it has changes in range with respect to + # its parents. + try: + c, linerange1 = next(blockancestors(fctx, fromline, toline)) + except StopIteration: + pass + else: + if c == fctx: + yield c, linerange1 + + diffopts = patch.diffopts(fctx._repo.ui) + fl = fctx.filelog() + seen = {fctx.filerev(): (fctx, (fromline, toline))} + for i in fl.descendants([fctx.filerev()]): + c = fctx.filectx(i) + inrange = False + for x in fl.parentrevs(i): + try: + p, linerange2 = seen[x] + except KeyError: + # nullrev or other branch + continue + inrangep, linerange1 = _changesrange(c, p, linerange2, diffopts) + inrange = inrange or inrangep + # If revision 'i' has been seen (it's a merge), we assume that its + # line range is the same independently of which parents was used + # to compute it. + assert i not in seen or seen[i][1] == linerange1, ( + 'computed line range for %s is not consistent between ' + 'ancestor branches' % c) + seen[i] = c, linerange1 + if inrange: + yield c, linerange1 + def toposort(revs, parentsfunc, firstbranch=()): """Yield revisions from heads to roots one (topo) branch at a time.