# HG changeset patch # User Yuya Nishihara # Date 2018-02-28 20:09:05 # Node ID 7affcabf561ed6b38e6ce67310651ab615b12014 # Parent 1b9f6440506b3434e97eadd6f9b4b39dd0004154 dagop: move annotateline and _annotatepair from context.py The annotate logic is large. Let's move it out of the context module, which is basically an abstraction layer of repository operations. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -26,10 +26,8 @@ from .node import ( wdirnodes, wdirrev, ) -from .thirdparty import ( - attr, -) from . import ( + dagop, encoding, error, fileset, @@ -978,6 +976,8 @@ class basefilectx(object): the line number at the first appearance in the managed file, otherwise, number has a fixed value of False. ''' + annotateline = dagop.annotateline + _annotatepair = dagop._annotatepair def lines(text): if text.endswith("\n"): @@ -1105,74 +1105,6 @@ class basefilectx(object): """ return self._repo.wwritedata(self.path(), self.data()) -@attr.s(slots=True, frozen=True) -class annotateline(object): - fctx = attr.ib() - lineno = attr.ib(default=False) - # Whether this annotation was the result of a skip-annotate. - skip = attr.ib(default=False) - -def _annotatepair(parents, childfctx, child, skipchild, diffopts): - r''' - Given parent and child fctxes and annotate data for parents, for all lines - in either parent that match the child, annotate the child with the parent's - data. - - Additionally, if `skipchild` is True, replace all other lines with parent - annotate data as well such that child is never blamed for any lines. - - See test-annotate.py for unit tests. - ''' - pblocks = [(parent, mdiff.allblocks(parent[1], child[1], opts=diffopts)) - for parent in parents] - - if skipchild: - # Need to iterate over the blocks twice -- make it a list - pblocks = [(p, list(blocks)) for (p, blocks) in pblocks] - # Mercurial currently prefers p2 over p1 for annotate. - # TODO: change this? - for parent, blocks in pblocks: - for (a1, a2, b1, b2), t in blocks: - # Changed blocks ('!') or blocks made only of blank lines ('~') - # belong to the child. - if t == '=': - child[0][b1:b2] = parent[0][a1:a2] - - if skipchild: - # Now try and match up anything that couldn't be matched, - # Reversing pblocks maintains bias towards p2, matching above - # behavior. - pblocks.reverse() - - # The heuristics are: - # * Work on blocks of changed lines (effectively diff hunks with -U0). - # This could potentially be smarter but works well enough. - # * For a non-matching section, do a best-effort fit. Match lines in - # diff hunks 1:1, dropping lines as necessary. - # * Repeat the last line as a last resort. - - # First, replace as much as possible without repeating the last line. - remaining = [(parent, []) for parent, _blocks in pblocks] - for idx, (parent, blocks) in enumerate(pblocks): - for (a1, a2, b1, b2), _t in blocks: - if a2 - a1 >= b2 - b1: - for bk in xrange(b1, b2): - if child[0][bk].fctx == childfctx: - ak = min(a1 + (bk - b1), a2 - 1) - child[0][bk] = attr.evolve(parent[0][ak], skip=True) - else: - remaining[idx][1].append((a1, a2, b1, b2)) - - # Then, look at anything left, which might involve repeating the last - # line. - for parent, blocks in remaining: - for a1, a2, b1, b2 in blocks: - for bk in xrange(b1, b2): - if child[0][bk].fctx == childfctx: - ak = min(a1 + (bk - b1), a2 - 1) - child[0][bk] = attr.evolve(parent[0][ak], skip=True) - return child - class filectx(basefilectx): """A filecontext object makes access to data related to a particular filerevision convenient.""" diff --git a/mercurial/dagop.py b/mercurial/dagop.py --- a/mercurial/dagop.py +++ b/mercurial/dagop.py @@ -9,6 +9,9 @@ from __future__ import absolute_import import heapq +from .thirdparty import ( + attr, +) from . import ( error, mdiff, @@ -358,6 +361,74 @@ def blockdescendants(fctx, fromline, tol if inrange: yield c, linerange1 +@attr.s(slots=True, frozen=True) +class annotateline(object): + fctx = attr.ib() + lineno = attr.ib(default=False) + # Whether this annotation was the result of a skip-annotate. + skip = attr.ib(default=False) + +def _annotatepair(parents, childfctx, child, skipchild, diffopts): + r''' + Given parent and child fctxes and annotate data for parents, for all lines + in either parent that match the child, annotate the child with the parent's + data. + + Additionally, if `skipchild` is True, replace all other lines with parent + annotate data as well such that child is never blamed for any lines. + + See test-annotate.py for unit tests. + ''' + pblocks = [(parent, mdiff.allblocks(parent[1], child[1], opts=diffopts)) + for parent in parents] + + if skipchild: + # Need to iterate over the blocks twice -- make it a list + pblocks = [(p, list(blocks)) for (p, blocks) in pblocks] + # Mercurial currently prefers p2 over p1 for annotate. + # TODO: change this? + for parent, blocks in pblocks: + for (a1, a2, b1, b2), t in blocks: + # Changed blocks ('!') or blocks made only of blank lines ('~') + # belong to the child. + if t == '=': + child[0][b1:b2] = parent[0][a1:a2] + + if skipchild: + # Now try and match up anything that couldn't be matched, + # Reversing pblocks maintains bias towards p2, matching above + # behavior. + pblocks.reverse() + + # The heuristics are: + # * Work on blocks of changed lines (effectively diff hunks with -U0). + # This could potentially be smarter but works well enough. + # * For a non-matching section, do a best-effort fit. Match lines in + # diff hunks 1:1, dropping lines as necessary. + # * Repeat the last line as a last resort. + + # First, replace as much as possible without repeating the last line. + remaining = [(parent, []) for parent, _blocks in pblocks] + for idx, (parent, blocks) in enumerate(pblocks): + for (a1, a2, b1, b2), _t in blocks: + if a2 - a1 >= b2 - b1: + for bk in xrange(b1, b2): + if child[0][bk].fctx == childfctx: + ak = min(a1 + (bk - b1), a2 - 1) + child[0][bk] = attr.evolve(parent[0][ak], skip=True) + else: + remaining[idx][1].append((a1, a2, b1, b2)) + + # Then, look at anything left, which might involve repeating the last + # line. + for parent, blocks in remaining: + for a1, a2, b1, b2 in blocks: + for bk in xrange(b1, b2): + if child[0][bk].fctx == childfctx: + ak = min(a1 + (bk - b1), a2 - 1) + child[0][bk] = attr.evolve(parent[0][ak], skip=True) + return child + def toposort(revs, parentsfunc, firstbranch=()): """Yield revisions from heads to roots one (topo) branch at a time. diff --git a/tests/test-annotate.py b/tests/test-annotate.py --- a/tests/test-annotate.py +++ b/tests/test-annotate.py @@ -6,7 +6,7 @@ import unittest from mercurial import ( mdiff, ) -from mercurial.context import ( +from mercurial.dagop import ( annotateline, _annotatepair, )