dagop: move annotateline and _annotatepair from context.py
authorYuya Nishihara <yuya@tcha.org>
Wed, 28 Feb 2018 15:09:05 -0500
changeset 36917 7affcabf561e
parent 36916 1b9f6440506b
child 36918 5d3abd6a5b25
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.
mercurial/context.py
mercurial/dagop.py
tests/test-annotate.py
--- a/mercurial/context.py	Fri Mar 09 21:59:07 2018 -0500
+++ b/mercurial/context.py	Wed Feb 28 15:09:05 2018 -0500
@@ -26,10 +26,8 @@
     wdirnodes,
     wdirrev,
 )
-from .thirdparty import (
-    attr,
-)
 from . import (
+    dagop,
     encoding,
     error,
     fileset,
@@ -978,6 +976,8 @@
         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 @@
         """
         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."""
--- a/mercurial/dagop.py	Fri Mar 09 21:59:07 2018 -0500
+++ b/mercurial/dagop.py	Wed Feb 28 15:09:05 2018 -0500
@@ -9,6 +9,9 @@
 
 import heapq
 
+from .thirdparty import (
+    attr,
+)
 from . import (
     error,
     mdiff,
@@ -358,6 +361,74 @@
         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.
 
--- a/tests/test-annotate.py	Fri Mar 09 21:59:07 2018 -0500
+++ b/tests/test-annotate.py	Wed Feb 28 15:09:05 2018 -0500
@@ -6,7 +6,7 @@
 from mercurial import (
     mdiff,
 )
-from mercurial.context import (
+from mercurial.dagop import (
     annotateline,
     _annotatepair,
 )