Patchwork [6,of,6] log: pack filematcher and hunksfilter into changesetdiffer object

login
register
mail settings
Submitter Yuya Nishihara
Date Feb. 7, 2018, 1:25 p.m.
Message ID <5f9dcb5d72da427abbfa.1518009907@mimosa>
Download mbox | patch
Permalink /patch/27427/
State Accepted
Headers show

Comments

Yuya Nishihara - Feb. 7, 2018, 1:25 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1516517658 -32400
#      Sun Jan 21 15:54:18 2018 +0900
# Node ID 5f9dcb5d72da427abbfa2c304bdbe4dd555e0c7d
# Parent  f95d0d1e012a512550de945350e08f3dc7db090f
log: pack filematcher and hunksfilter into changesetdiffer object

This is just a way of getting rid of clumsy makefilematcher/makehunksfilter
arguments. There might be a better abstraction, but I don't think this is bad.

This makes filematcher and hunksfilter available by default, but that should
be fine.
Denis Laxalde - Feb. 7, 2018, 2:58 p.m.
Yuya Nishihara a écrit :
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1516517658 -32400
> #      Sun Jan 21 15:54:18 2018 +0900
> # Node ID 5f9dcb5d72da427abbfa2c304bdbe4dd555e0c7d
> # Parent  f95d0d1e012a512550de945350e08f3dc7db090f
> log: pack filematcher and hunksfilter into changesetdiffer object
> 
> This is just a way of getting rid of clumsy makefilematcher/makehunksfilter
> arguments. There might be a better abstraction, but I don't think this is bad.

Alternatively we could have a makediffer(makefilematcher=None,
makehunksfilter=None) factory function returning the showdiff function
with capture context; that would avoid setting _make<attribute> in
client code. Not sure this is better and I agree that your proposal is
"not bad".

Nice cleanup overall.

> This makes filematcher and hunksfilter available by default, but that should
> be fine.
> 
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -3419,17 +3419,15 @@ def log(ui, repo, *pats, **opts):
>          )
>  
>      repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
> -    revs, filematcher = logcmdutil.getrevs(repo, pats, opts)
> -    hunksfilter = None
> +    revs, differ = logcmdutil.getrevs(repo, pats, opts)
>  
>      if opts.get('graph'):
>          if linerange:
>              raise error.Abort(_('graph not supported with line range patterns'))
> -        return logcmdutil.graphlog(ui, repo, revs, filematcher, opts)
> +        return logcmdutil.graphlog(ui, repo, revs, differ, opts)
>  
>      if linerange:
> -        revs, filematcher, hunksfilter = logcmdutil.getlinerangerevs(
> -            repo, revs, opts)
> +        revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
>  
>      getrenamed = None
>      if opts.get('copies'):
> @@ -3439,9 +3437,7 @@ def log(ui, repo, *pats, **opts):
>          getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
>  
>      ui.pager('log')
> -    displayer = logcmdutil.changesetdisplayer(ui, repo, opts,
> -                                              makefilematcher=filematcher,
> -                                              makehunksfilter=hunksfilter,
> +    displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
>                                                buffered=True)
>      for rev in revs:
>          ctx = repo[rev]
> diff --git a/mercurial/logcmdutil.py b/mercurial/logcmdutil.py
> --- a/mercurial/logcmdutil.py
> +++ b/mercurial/logcmdutil.py
> @@ -109,6 +109,23 @@ def diffordiffstat(ui, repo, diffopts, n
>              sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
>                       stat=stat, fp=fp, prefix=prefix)
>  
> +class changesetdiffer(object):
> +    """Generate diff of changeset with pre-configured filtering functions"""
> +
> +    def _makefilematcher(self, ctx):
> +        return scmutil.matchall(ctx.repo())
> +
> +    def _makehunksfilter(self, ctx):
> +        return None
> +
> +    def showdiff(self, ui, ctx, diffopts, stat=False):
> +        repo = ctx.repo()
> +        node = ctx.node()
> +        prev = ctx.p1().node()
> +        diffordiffstat(ui, repo, diffopts, prev, node,
> +                       match=self._makefilematcher(ctx), stat=stat,
> +                       hunksfilterfn=self._makehunksfilter(ctx))
> +
>  def changesetlabels(ctx):
>      labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
>      if ctx.obsolete():
> @@ -122,13 +139,11 @@ def changesetlabels(ctx):
>  class changesetprinter(object):
>      '''show changeset information when templating not requested.'''
>  
> -    def __init__(self, ui, repo, makefilematcher=None, makehunksfilter=None,
> -                 diffopts=None, buffered=False):
> +    def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
>          self.ui = ui
>          self.repo = repo
>          self.buffered = buffered
> -        self._makefilematcher = makefilematcher or (lambda ctx: None)
> -        self._makehunksfilter = makehunksfilter or (lambda ctx: None)
> +        self._differ = differ or changesetdiffer()
>          self.diffopts = diffopts or {}
>          self.header = {}
>          self.hunk = {}
> @@ -267,35 +282,23 @@ class changesetprinter(object):
>          '''
>  
>      def _showpatch(self, ctx):
> -        matchfn = self._makefilematcher(ctx)
> -        hunksfilterfn = self._makehunksfilter(ctx)
> -        if not matchfn:
> -            return
>          stat = self.diffopts.get('stat')
>          diff = self.diffopts.get('patch')
>          diffopts = patch.diffallopts(self.ui, self.diffopts)
> -        node = ctx.node()
> -        prev = ctx.p1().node()
>          if stat:
> -            diffordiffstat(self.ui, self.repo, diffopts, prev, node,
> -                           match=matchfn, stat=True,
> -                           hunksfilterfn=hunksfilterfn)
> +            self._differ.showdiff(self.ui, ctx, diffopts, stat=True)
>          if stat and diff:
>              self.ui.write("\n")
>          if diff:
> -            diffordiffstat(self.ui, self.repo, diffopts, prev, node,
> -                           match=matchfn, stat=False,
> -                           hunksfilterfn=hunksfilterfn)
> +            self._differ.showdiff(self.ui, ctx, diffopts, stat=False)
>          if stat or diff:
>              self.ui.write("\n")
>  
>  class jsonchangeset(changesetprinter):
>      '''format changeset information.'''
>  
> -    def __init__(self, ui, repo, makefilematcher=None, makehunksfilter=None,
> -                 diffopts=None, buffered=False):
> -        changesetprinter.__init__(self, ui, repo, makefilematcher,
> -                                  makehunksfilter, diffopts, buffered)
> +    def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
> +        changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
>          self.cache = {}
>          self._first = True
>  
> @@ -370,21 +373,17 @@ class jsonchangeset(changesetprinter):
>                                ", ".join('"%s": "%s"' % (j(k), j(v))
>                                                          for k, v in copies))
>  
> -        matchfn = self._makefilematcher(ctx)
>          stat = self.diffopts.get('stat')
>          diff = self.diffopts.get('patch')
>          diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
> -        node, prev = ctx.node(), ctx.p1().node()
> -        if matchfn and stat:
> +        if stat:
>              self.ui.pushbuffer()
> -            diffordiffstat(self.ui, self.repo, diffopts, prev, node,
> -                           match=matchfn, stat=True)
> +            self._differ.showdiff(self.ui, ctx, diffopts, stat=True)
>              self.ui.write((',\n  "diffstat": "%s"')
>                            % j(self.ui.popbuffer()))
> -        if matchfn and diff:
> +        if diff:
>              self.ui.pushbuffer()
> -            diffordiffstat(self.ui, self.repo, diffopts, prev, node,
> -                           match=matchfn, stat=False)
> +            self._differ.showdiff(self.ui, ctx, diffopts, stat=False)
>              self.ui.write((',\n  "diff": "%s"') % j(self.ui.popbuffer()))
>  
>          self.ui.write("\n }")
> @@ -400,10 +399,9 @@ class changesettemplater(changesetprinte
>  
>      # Arguments before "buffered" used to be positional. Consider not
>      # adding/removing arguments before "buffered" to not break callers.
> -    def __init__(self, ui, repo, tmplspec, makefilematcher=None,
> -                 makehunksfilter=None, diffopts=None, buffered=False):
> -        changesetprinter.__init__(self, ui, repo, makefilematcher,
> -                                  makehunksfilter, diffopts, buffered)
> +    def __init__(self, ui, repo, tmplspec, differ=None, diffopts=None,
> +                 buffered=False):
> +        changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
>          tres = formatter.templateresources(ui, repo)
>          self.t = formatter.loadtemplater(ui, tmplspec,
>                                           defaults=templatekw.keywords,
> @@ -520,8 +518,7 @@ def maketemplater(ui, repo, tmpl, buffer
>      spec = templatespec(tmpl, None)
>      return changesettemplater(ui, repo, spec, buffered=buffered)
>  
> -def changesetdisplayer(ui, repo, opts, makefilematcher=None,
> -                       makehunksfilter=None, buffered=False):
> +def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
>      """show one changeset using template or regular display.
>  
>      Display format will be the first non-empty hit of:
> @@ -532,12 +529,7 @@ def changesetdisplayer(ui, repo, opts, m
>      If all of these values are either the unset or the empty string,
>      regular display via changesetprinter() is done.
>      """
> -    # options
> -    if not makefilematcher and (opts.get('patch') or opts.get('stat')):
> -        def makefilematcher(ctx):
> -            return scmutil.matchall(repo)
> -
> -    postargs = (makefilematcher, makehunksfilter, opts, buffered)
> +    postargs = (differ, opts, buffered)
>      if opts.get('template') == 'json':
>          return jsonchangeset(ui, repo, *postargs)
>  
> @@ -713,10 +705,9 @@ def _initialrevs(repo, opts):
>      return revs
>  
>  def getrevs(repo, pats, opts):
> -    """Return (revs, filematcher) where revs is a smartset
> +    """Return (revs, differ) where revs is a smartset
>  
> -    filematcher is a callable taking a changectx and returning a match
> -    objects filtering the files to be detailed when displaying the revision.
> +    differ is a changesetdiffer with pre-configured file matcher.
>      """
>      follow = opts.get('follow') or opts.get('follow_first')
>      followfirst = opts.get('follow_first')
> @@ -749,7 +740,10 @@ def getrevs(repo, pats, opts):
>          revs = matcher(repo, revs)
>      if limit is not None:
>          revs = revs.slice(0, limit)
> -    return revs, filematcher
> +
> +    differ = changesetdiffer()
> +    differ._makefilematcher = filematcher
> +    return revs, differ
>  
>  def _parselinerangeopt(repo, opts):
>      """Parse --line-range log option and return a list of tuples (filename,
> @@ -772,16 +766,13 @@ def _parselinerangeopt(repo, opts):
>      return linerangebyfname
>  
>  def getlinerangerevs(repo, userrevs, opts):
> -    """Return (revs, filematcher, hunksfilter).
> +    """Return (revs, differ).
>  
>      "revs" are revisions obtained by processing "line-range" log options and
>      walking block ancestors of each specified file/line-range.
>  
> -    "filematcher(ctx) -> match" is a factory function returning a match object
> -    for a given revision for file patterns specified in --line-range option.
> -
> -    "hunksfilter(ctx) -> filterfn(fctx, hunks)" is a factory function
> -    returning a hunks filtering function.
> +    "differ" is a changesetdiffer with pre-configured file matcher and hunks
> +    filter.
>      """
>      wctx = repo[None]
>  
> @@ -830,7 +821,10 @@ def getlinerangerevs(repo, userrevs, opt
>  
>      revs = sorted(linerangesbyrev, reverse=True)
>  
> -    return revs, filematcher, hunksfilter
> +    differ = changesetdiffer()
> +    differ._makefilematcher = filematcher
> +    differ._makehunksfilter = hunksfilter
> +    return revs, differ
>  
>  def _graphnodeformatter(ui, displayer):
>      spec = ui.config('ui', 'graphnodetemplate')
> @@ -897,7 +891,7 @@ def displaygraph(ui, repo, dag, displaye
>              lines = []
>      displayer.close()
>  
> -def graphlog(ui, repo, revs, filematcher, opts):
> +def graphlog(ui, repo, revs, differ, opts):
>      # Parameters are identical to log command ones
>      revdag = graphmod.dagwalker(repo, revs)
>  
> @@ -909,8 +903,7 @@ def graphlog(ui, repo, revs, filematcher
>          getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
>  
>      ui.pager('log')
> -    displayer = changesetdisplayer(ui, repo, opts, makefilematcher=filematcher,
> -                                   buffered=True)
> +    displayer = changesetdisplayer(ui, repo, opts, differ, buffered=True)
>      displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
>  
>  def checkunsupportedgraphflags(pats, opts):
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>
Yuya Nishihara - Feb. 8, 2018, 12:19 p.m.
On Wed, 7 Feb 2018 15:58:14 +0100, Denis Laxalde wrote:
> Yuya Nishihara a écrit :
> > # HG changeset patch
> > # User Yuya Nishihara <yuya@tcha.org>
> > # Date 1516517658 -32400
> > #      Sun Jan 21 15:54:18 2018 +0900
> > # Node ID 5f9dcb5d72da427abbfa2c304bdbe4dd555e0c7d
> > # Parent  f95d0d1e012a512550de945350e08f3dc7db090f
> > log: pack filematcher and hunksfilter into changesetdiffer object
> > 
> > This is just a way of getting rid of clumsy makefilematcher/makehunksfilter
> > arguments. There might be a better abstraction, but I don't think this is bad.
> 
> Alternatively we could have a makediffer(makefilematcher=None,
> makehunksfilter=None) factory function returning the showdiff function
> with capture context; that would avoid setting _make<attribute> in
> client code.

Yeah, that's doable. But I slightly prefer a class over a set of closures.
I mean it might be possible to factor out makefilematcher/makehunksfilter
to a class holding a repo, instead of overwriting object attributes.

Anyway, thanks for reviewing.
Augie Fackler - Feb. 11, 2018, 1:51 p.m.
On Wed, Feb 07, 2018 at 10:25:07PM +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1516517658 -32400
> #      Sun Jan 21 15:54:18 2018 +0900
> # Node ID 5f9dcb5d72da427abbfa2c304bdbe4dd555e0c7d
> # Parent  f95d0d1e012a512550de945350e08f3dc7db090f
> log: pack filematcher and hunksfilter into changesetdiffer object

queued, thanks

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -3419,17 +3419,15 @@  def log(ui, repo, *pats, **opts):
         )
 
     repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
-    revs, filematcher = logcmdutil.getrevs(repo, pats, opts)
-    hunksfilter = None
+    revs, differ = logcmdutil.getrevs(repo, pats, opts)
 
     if opts.get('graph'):
         if linerange:
             raise error.Abort(_('graph not supported with line range patterns'))
-        return logcmdutil.graphlog(ui, repo, revs, filematcher, opts)
+        return logcmdutil.graphlog(ui, repo, revs, differ, opts)
 
     if linerange:
-        revs, filematcher, hunksfilter = logcmdutil.getlinerangerevs(
-            repo, revs, opts)
+        revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
 
     getrenamed = None
     if opts.get('copies'):
@@ -3439,9 +3437,7 @@  def log(ui, repo, *pats, **opts):
         getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
 
     ui.pager('log')
-    displayer = logcmdutil.changesetdisplayer(ui, repo, opts,
-                                              makefilematcher=filematcher,
-                                              makehunksfilter=hunksfilter,
+    displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
                                               buffered=True)
     for rev in revs:
         ctx = repo[rev]
diff --git a/mercurial/logcmdutil.py b/mercurial/logcmdutil.py
--- a/mercurial/logcmdutil.py
+++ b/mercurial/logcmdutil.py
@@ -109,6 +109,23 @@  def diffordiffstat(ui, repo, diffopts, n
             sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
                      stat=stat, fp=fp, prefix=prefix)
 
+class changesetdiffer(object):
+    """Generate diff of changeset with pre-configured filtering functions"""
+
+    def _makefilematcher(self, ctx):
+        return scmutil.matchall(ctx.repo())
+
+    def _makehunksfilter(self, ctx):
+        return None
+
+    def showdiff(self, ui, ctx, diffopts, stat=False):
+        repo = ctx.repo()
+        node = ctx.node()
+        prev = ctx.p1().node()
+        diffordiffstat(ui, repo, diffopts, prev, node,
+                       match=self._makefilematcher(ctx), stat=stat,
+                       hunksfilterfn=self._makehunksfilter(ctx))
+
 def changesetlabels(ctx):
     labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
     if ctx.obsolete():
@@ -122,13 +139,11 @@  def changesetlabels(ctx):
 class changesetprinter(object):
     '''show changeset information when templating not requested.'''
 
-    def __init__(self, ui, repo, makefilematcher=None, makehunksfilter=None,
-                 diffopts=None, buffered=False):
+    def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
         self.ui = ui
         self.repo = repo
         self.buffered = buffered
-        self._makefilematcher = makefilematcher or (lambda ctx: None)
-        self._makehunksfilter = makehunksfilter or (lambda ctx: None)
+        self._differ = differ or changesetdiffer()
         self.diffopts = diffopts or {}
         self.header = {}
         self.hunk = {}
@@ -267,35 +282,23 @@  class changesetprinter(object):
         '''
 
     def _showpatch(self, ctx):
-        matchfn = self._makefilematcher(ctx)
-        hunksfilterfn = self._makehunksfilter(ctx)
-        if not matchfn:
-            return
         stat = self.diffopts.get('stat')
         diff = self.diffopts.get('patch')
         diffopts = patch.diffallopts(self.ui, self.diffopts)
-        node = ctx.node()
-        prev = ctx.p1().node()
         if stat:
-            diffordiffstat(self.ui, self.repo, diffopts, prev, node,
-                           match=matchfn, stat=True,
-                           hunksfilterfn=hunksfilterfn)
+            self._differ.showdiff(self.ui, ctx, diffopts, stat=True)
         if stat and diff:
             self.ui.write("\n")
         if diff:
-            diffordiffstat(self.ui, self.repo, diffopts, prev, node,
-                           match=matchfn, stat=False,
-                           hunksfilterfn=hunksfilterfn)
+            self._differ.showdiff(self.ui, ctx, diffopts, stat=False)
         if stat or diff:
             self.ui.write("\n")
 
 class jsonchangeset(changesetprinter):
     '''format changeset information.'''
 
-    def __init__(self, ui, repo, makefilematcher=None, makehunksfilter=None,
-                 diffopts=None, buffered=False):
-        changesetprinter.__init__(self, ui, repo, makefilematcher,
-                                  makehunksfilter, diffopts, buffered)
+    def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
+        changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
         self.cache = {}
         self._first = True
 
@@ -370,21 +373,17 @@  class jsonchangeset(changesetprinter):
                               ", ".join('"%s": "%s"' % (j(k), j(v))
                                                         for k, v in copies))
 
-        matchfn = self._makefilematcher(ctx)
         stat = self.diffopts.get('stat')
         diff = self.diffopts.get('patch')
         diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
-        node, prev = ctx.node(), ctx.p1().node()
-        if matchfn and stat:
+        if stat:
             self.ui.pushbuffer()
-            diffordiffstat(self.ui, self.repo, diffopts, prev, node,
-                           match=matchfn, stat=True)
+            self._differ.showdiff(self.ui, ctx, diffopts, stat=True)
             self.ui.write((',\n  "diffstat": "%s"')
                           % j(self.ui.popbuffer()))
-        if matchfn and diff:
+        if diff:
             self.ui.pushbuffer()
-            diffordiffstat(self.ui, self.repo, diffopts, prev, node,
-                           match=matchfn, stat=False)
+            self._differ.showdiff(self.ui, ctx, diffopts, stat=False)
             self.ui.write((',\n  "diff": "%s"') % j(self.ui.popbuffer()))
 
         self.ui.write("\n }")
@@ -400,10 +399,9 @@  class changesettemplater(changesetprinte
 
     # Arguments before "buffered" used to be positional. Consider not
     # adding/removing arguments before "buffered" to not break callers.
-    def __init__(self, ui, repo, tmplspec, makefilematcher=None,
-                 makehunksfilter=None, diffopts=None, buffered=False):
-        changesetprinter.__init__(self, ui, repo, makefilematcher,
-                                  makehunksfilter, diffopts, buffered)
+    def __init__(self, ui, repo, tmplspec, differ=None, diffopts=None,
+                 buffered=False):
+        changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
         tres = formatter.templateresources(ui, repo)
         self.t = formatter.loadtemplater(ui, tmplspec,
                                          defaults=templatekw.keywords,
@@ -520,8 +518,7 @@  def maketemplater(ui, repo, tmpl, buffer
     spec = templatespec(tmpl, None)
     return changesettemplater(ui, repo, spec, buffered=buffered)
 
-def changesetdisplayer(ui, repo, opts, makefilematcher=None,
-                       makehunksfilter=None, buffered=False):
+def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
     """show one changeset using template or regular display.
 
     Display format will be the first non-empty hit of:
@@ -532,12 +529,7 @@  def changesetdisplayer(ui, repo, opts, m
     If all of these values are either the unset or the empty string,
     regular display via changesetprinter() is done.
     """
-    # options
-    if not makefilematcher and (opts.get('patch') or opts.get('stat')):
-        def makefilematcher(ctx):
-            return scmutil.matchall(repo)
-
-    postargs = (makefilematcher, makehunksfilter, opts, buffered)
+    postargs = (differ, opts, buffered)
     if opts.get('template') == 'json':
         return jsonchangeset(ui, repo, *postargs)
 
@@ -713,10 +705,9 @@  def _initialrevs(repo, opts):
     return revs
 
 def getrevs(repo, pats, opts):
-    """Return (revs, filematcher) where revs is a smartset
+    """Return (revs, differ) where revs is a smartset
 
-    filematcher is a callable taking a changectx and returning a match
-    objects filtering the files to be detailed when displaying the revision.
+    differ is a changesetdiffer with pre-configured file matcher.
     """
     follow = opts.get('follow') or opts.get('follow_first')
     followfirst = opts.get('follow_first')
@@ -749,7 +740,10 @@  def getrevs(repo, pats, opts):
         revs = matcher(repo, revs)
     if limit is not None:
         revs = revs.slice(0, limit)
-    return revs, filematcher
+
+    differ = changesetdiffer()
+    differ._makefilematcher = filematcher
+    return revs, differ
 
 def _parselinerangeopt(repo, opts):
     """Parse --line-range log option and return a list of tuples (filename,
@@ -772,16 +766,13 @@  def _parselinerangeopt(repo, opts):
     return linerangebyfname
 
 def getlinerangerevs(repo, userrevs, opts):
-    """Return (revs, filematcher, hunksfilter).
+    """Return (revs, differ).
 
     "revs" are revisions obtained by processing "line-range" log options and
     walking block ancestors of each specified file/line-range.
 
-    "filematcher(ctx) -> match" is a factory function returning a match object
-    for a given revision for file patterns specified in --line-range option.
-
-    "hunksfilter(ctx) -> filterfn(fctx, hunks)" is a factory function
-    returning a hunks filtering function.
+    "differ" is a changesetdiffer with pre-configured file matcher and hunks
+    filter.
     """
     wctx = repo[None]
 
@@ -830,7 +821,10 @@  def getlinerangerevs(repo, userrevs, opt
 
     revs = sorted(linerangesbyrev, reverse=True)
 
-    return revs, filematcher, hunksfilter
+    differ = changesetdiffer()
+    differ._makefilematcher = filematcher
+    differ._makehunksfilter = hunksfilter
+    return revs, differ
 
 def _graphnodeformatter(ui, displayer):
     spec = ui.config('ui', 'graphnodetemplate')
@@ -897,7 +891,7 @@  def displaygraph(ui, repo, dag, displaye
             lines = []
     displayer.close()
 
-def graphlog(ui, repo, revs, filematcher, opts):
+def graphlog(ui, repo, revs, differ, opts):
     # Parameters are identical to log command ones
     revdag = graphmod.dagwalker(repo, revs)
 
@@ -909,8 +903,7 @@  def graphlog(ui, repo, revs, filematcher
         getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
 
     ui.pager('log')
-    displayer = changesetdisplayer(ui, repo, opts, makefilematcher=filematcher,
-                                   buffered=True)
+    displayer = changesetdisplayer(ui, repo, opts, differ, buffered=True)
     displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
 
 def checkunsupportedgraphflags(pats, opts):