[PATCH 2 of 3] context: add a `blockancestors(fromline, toline)` method to filectx

Denis Laxalde denis.laxalde at logilab.fr
Mon Nov 28 04:54:15 EST 2016


# HG changeset patch
# User Denis Laxalde <denis at laxalde.org>
# Date 1480086869 -3600
#      Fri Nov 25 16:14:29 2016 +0100
# Node ID 6dd93ae7b35002531308444c87dcf47beb773648
# Parent  0cf70234a38e47a3f7107611885368db9d52f574
# EXP-Topic linerange-log/revset
context: add a `blockancestors(fromline, toline)` method to filectx

This yields filectx instances obtained by walking through ancestors by
only keeping changesets touching the file within specified `linerange =
(fromline, toline)`. It only follows first parent, since this yields weird
results in case of merge (might be improved later).

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.

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -1153,6 +1153,40 @@ class filectx(basefilectx):
         return [filectx(self._repo, self._path, fileid=x,
                         filelog=self._filelog) for x in c]
 
+    def blockancestors(self, fromline, toline):
+        """Yield ancestors of this filectx with respect to the block of lines
+        in `linerange = (lowerbound, upperbound)`.
+        """
+        def changesrange(fctx1, fctx2, linerange2):
+            """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.
+            """
+            diffopts = patch.diffopts(self._repo.ui)
+            blocks = mdiff.allblocks(fctx1.data(), fctx2.data(), diffopts)
+            blocks = mdiff.blocksinrange(blocks, linerange2)
+            # consume blocks to find out linerange1 and whether there's some
+            # diff in linerange2.
+            diffinrange = False
+            while True:
+                try:
+                    _, stype = next(blocks)
+                    if diffinrange:
+                        continue
+                    diffinrange = stype == '!'
+                except StopIteration as exc:
+                    linerange1 = exc.args[0]
+                    break
+            return diffinrange, linerange1
+
+        c, linerange2 = self, (fromline, toline)
+        for p in self.ancestors(followfirst=True):  # TODO handle followfirst
+            inrangep, linerange1 = changesrange(p, c, linerange2)
+            if inrangep:
+                yield c
+            c, linerange2 = p, linerange1
+        yield c  # XXX should we?
+
 class committablectx(basectx):
     """A committablectx object provides common functionality for a context that
     wants the ability to commit, e.g. workingctx or memctx."""


More information about the Mercurial-devel mailing list