Patchwork [3,of,3,RFC,V3] log: changed implementation to use graphlog code

login
register
mail settings
Submitter Lucas Moscovicz
Date March 10, 2014, 6:27 p.m.
Message ID <c92e09b96370180719b1.1394476052@dev1037.prn2.facebook.com>
Download mbox | patch
Permalink /patch/3905/
State Superseded
Commit 69402eb72115417b67c86eef6161e5f11394fb2f
Headers show

Comments

Lucas Moscovicz - March 10, 2014, 6:27 p.m.
# HG changeset patch
# User Lucas Moscovicz <lmoscovicz@fb.com>
# Date 1393629056 28800
#      Fri Feb 28 15:10:56 2014 -0800
# Node ID c92e09b96370180719b1fdc4c23e3119c8466564
# Parent  b30cc2e46ac6cc9c41799dc911be8b4bfc1a9f18
log: changed implementation to use graphlog code

Now that revsets work in a lazy way, log code can be changed to parse every
option into a revset and then evaluate it lazily.

Now expressions like

  "hg log -b default -b ."

are converted into a revset using the same code as graphlog.
Pierre-Yves David - April 14, 2014, 5:01 a.m.
On 03/10/2014 02:27 PM, Lucas Moscovicz wrote:
> # HG changeset patch
> # User Lucas Moscovicz <lmoscovicz@fb.com>
> # Date 1393629056 28800
> #      Fri Feb 28 15:10:56 2014 -0800
> # Node ID c92e09b96370180719b1fdc4c23e3119c8466564
> # Parent  b30cc2e46ac6cc9c41799dc911be8b4bfc1a9f18
> log: changed implementation to use graphlog code

What about this RFC patches, is that something we ended up queuing 
before you left? Or is this something that still needs to be done?


>
> Now that revsets work in a lazy way, log code can be changed to parse every
> option into a revset and then evaluate it lazily.
>
> Now expressions like
>
>    "hg log -b default -b ."
>
> are converted into a revset using the same code as graphlog.
>
> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
> --- a/mercurial/cmdutil.py
> +++ b/mercurial/cmdutil.py
> @@ -1451,7 +1451,7 @@
>
>       return filematcher
>
> -def _makegraphlogrevset(repo, pats, opts, revs):
> +def _makelogrevset(repo, pats, opts, revs):
>       """Return (expr, filematcher) where expr is a revset string built
>       from log options and file patterns or None. If --stat or --patch
>       are not passed filematcher is None. Otherwise it is a callable
> @@ -1606,7 +1606,7 @@
>       possiblyunsorted = False # whether revs might need sorting
>       if opts.get('rev'):
>           revs = scmutil.revrange(repo, opts['rev'])
> -        # Don't sort here because _makegraphlogrevset might depend on the
> +        # Don't sort here because _makelogrevset might depend on the
>           # order of revs
>           possiblyunsorted = True
>       else:
> @@ -1617,7 +1617,7 @@
>               revs.reverse()
>       if not revs:
>           return revset.baseset([]), None, None
> -    expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
> +    expr, filematcher = _makelogrevset(repo, pats, opts, revs)
>       if possiblyunsorted:
>           revs.sort(reverse=True)
>       if expr:
> @@ -1644,6 +1644,54 @@
>
>       return revs, expr, filematcher
>
> +def getlogrevs(repo, pats, opts):
> +    """Return (revs, expr, filematcher) where revs is an iterable of
> +    revision numbers, expr is a revset string built from log options
> +    and file patterns or None, and used to filter 'revs'. If --stat or
> +    --patch are not passed filematcher is None. Otherwise it is a
> +    callable taking a revision number and returning a match objects
> +    filtering the files to be detailed when displaying the revision.
> +    """
> +    limit = loglimit(opts)
> +    # Default --rev value depends on --follow but --follow behaviour
> +    # depends on revisions resolved from --rev...
> +    follow = opts.get('follow') or opts.get('follow_first')
> +    if opts.get('rev'):
> +        revs = scmutil.revrange(repo, opts['rev'])
> +    elif follow:
> +        revs = revset.baseset(repo.revs('reverse(:.)'))
> +    else:
> +        revs = revset.spanset(repo)
> +        revs.reverse()
> +    if not revs:
> +        return revset.baseset([]), None, None
> +    expr, filematcher = _makelogrevset(repo, pats, opts, revs)
> +    if expr:
> +        # Revset matchers often operate faster on revisions in changelog
> +        # order, because most filters deal with the changelog.
> +        if not opts.get('rev'):
> +            revs.reverse()
> +        matcher = revset.match(repo.ui, expr)
> +        # Revset matches can reorder revisions. "A or B" typically returns
> +        # returns the revision matching A then the revision matching B. Sort
> +        # again to fix that.
> +        revs = matcher(repo, revs)
> +        if not opts.get('rev'):
> +            revs.sort(reverse=True)
> +    if limit is not None:
> +        count = 0
> +        limitedrevs = revset.baseset([])
> +        it = iter(revs)
> +        while count < limit:
> +            try:
> +                limitedrevs.append(it.next())
> +            except (StopIteration):
> +                break
> +            count += 1
> +        revs = limitedrevs
> +
> +    return revs, expr, filematcher
> +
>   def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
>                    filematcher=None):
>       seen, state = [], graphmod.asciistate()
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -4054,55 +4054,22 @@
>       if opts.get('graph'):
>           return cmdutil.graphlog(ui, repo, *pats, **opts)
>
> -    matchfn = scmutil.match(repo[None], pats, opts)
> +    revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
>       limit = cmdutil.loglimit(opts)
>       count = 0
>
> -    getrenamed, endrev = None, None
> +    getrenamed = None
>       if opts.get('copies'):
> +        endrev = None
>           if opts.get('rev'):
> -            endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
> +            endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
>           getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
>
> -    df = False
> -    if opts.get("date"):
> -        df = util.matchdate(opts["date"])
> -
> -    branches = opts.get('branch', []) + opts.get('only_branch', [])
> -    opts['branch'] = [repo.lookupbranch(b) for b in branches]
> -
> -    displayer = cmdutil.show_changeset(ui, repo, opts, True)
> -    def prep(ctx, fns):
> -        rev = ctx.rev()
> -        parents = [p for p in repo.changelog.parentrevs(rev)
> -                   if p != nullrev]
> -        if opts.get('no_merges') and len(parents) == 2:
> -            return
> -        if opts.get('only_merges') and len(parents) != 2:
> -            return
> -        if opts.get('branch') and ctx.branch() not in opts['branch']:
> -            return
> -        if df and not df(ctx.date()[0]):
> -            return
> -
> -        lower = encoding.lower
> -        if opts.get('user'):
> -            luser = lower(ctx.user())
> -            for k in [lower(x) for x in opts['user']]:
> -                if (k in luser):
> -                    break
> -            else:
> -                return
> -        if opts.get('keyword'):
> -            luser = lower(ctx.user())
> -            ldesc = lower(ctx.description())
> -            lfiles = lower(" ".join(ctx.files()))
> -            for k in [lower(x) for x in opts['keyword']]:
> -                if (k in luser or k in ldesc or k in lfiles):
> -                    break
> -            else:
> -                return
> -
> +    displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
> +    for rev in revs:
> +        if count == limit:
> +            break
> +        ctx = repo[rev]
>           copies = None
>           if getrenamed is not None and rev:
>               copies = []
> @@ -4110,22 +4077,11 @@
>                   rename = getrenamed(fn, rev)
>                   if rename:
>                       copies.append((fn, rename[0]))
> -
> -        revmatchfn = None
> -        if opts.get('patch') or opts.get('stat'):
> -            if opts.get('follow') or opts.get('follow_first'):
> -                # note: this might be wrong when following through merges
> -                revmatchfn = scmutil.match(repo[None], fns, default='path')
> -            else:
> -                revmatchfn = matchfn
> -
> +        revmatchfn = filematcher and filematcher(ctx.rev()) or None
>           displayer.show(ctx, copies=copies, matchfn=revmatchfn)
> -
> -    for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
> -        if displayer.flush(ctx.rev()):
> +        if displayer.flush(rev):
>               count += 1
> -        if count == limit:
> -            break
> +
>       displayer.close()
>
>   @command('manifest',
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>

Patch

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1451,7 +1451,7 @@ 
 
     return filematcher
 
-def _makegraphlogrevset(repo, pats, opts, revs):
+def _makelogrevset(repo, pats, opts, revs):
     """Return (expr, filematcher) where expr is a revset string built
     from log options and file patterns or None. If --stat or --patch
     are not passed filematcher is None. Otherwise it is a callable
@@ -1606,7 +1606,7 @@ 
     possiblyunsorted = False # whether revs might need sorting
     if opts.get('rev'):
         revs = scmutil.revrange(repo, opts['rev'])
-        # Don't sort here because _makegraphlogrevset might depend on the
+        # Don't sort here because _makelogrevset might depend on the
         # order of revs
         possiblyunsorted = True
     else:
@@ -1617,7 +1617,7 @@ 
             revs.reverse()
     if not revs:
         return revset.baseset([]), None, None
-    expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
+    expr, filematcher = _makelogrevset(repo, pats, opts, revs)
     if possiblyunsorted:
         revs.sort(reverse=True)
     if expr:
@@ -1644,6 +1644,54 @@ 
 
     return revs, expr, filematcher
 
+def getlogrevs(repo, pats, opts):
+    """Return (revs, expr, filematcher) where revs is an iterable of
+    revision numbers, expr is a revset string built from log options
+    and file patterns or None, and used to filter 'revs'. If --stat or
+    --patch are not passed filematcher is None. Otherwise it is a
+    callable taking a revision number and returning a match objects
+    filtering the files to be detailed when displaying the revision.
+    """
+    limit = loglimit(opts)
+    # Default --rev value depends on --follow but --follow behaviour
+    # depends on revisions resolved from --rev...
+    follow = opts.get('follow') or opts.get('follow_first')
+    if opts.get('rev'):
+        revs = scmutil.revrange(repo, opts['rev'])
+    elif follow:
+        revs = revset.baseset(repo.revs('reverse(:.)'))
+    else:
+        revs = revset.spanset(repo)
+        revs.reverse()
+    if not revs:
+        return revset.baseset([]), None, None
+    expr, filematcher = _makelogrevset(repo, pats, opts, revs)
+    if expr:
+        # Revset matchers often operate faster on revisions in changelog
+        # order, because most filters deal with the changelog.
+        if not opts.get('rev'):
+            revs.reverse()
+        matcher = revset.match(repo.ui, expr)
+        # Revset matches can reorder revisions. "A or B" typically returns
+        # returns the revision matching A then the revision matching B. Sort
+        # again to fix that.
+        revs = matcher(repo, revs)
+        if not opts.get('rev'):
+            revs.sort(reverse=True)
+    if limit is not None:
+        count = 0
+        limitedrevs = revset.baseset([])
+        it = iter(revs)
+        while count < limit:
+            try:
+                limitedrevs.append(it.next())
+            except (StopIteration):
+                break
+            count += 1
+        revs = limitedrevs
+
+    return revs, expr, filematcher
+
 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
                  filematcher=None):
     seen, state = [], graphmod.asciistate()
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -4054,55 +4054,22 @@ 
     if opts.get('graph'):
         return cmdutil.graphlog(ui, repo, *pats, **opts)
 
-    matchfn = scmutil.match(repo[None], pats, opts)
+    revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
     limit = cmdutil.loglimit(opts)
     count = 0
 
-    getrenamed, endrev = None, None
+    getrenamed = None
     if opts.get('copies'):
+        endrev = None
         if opts.get('rev'):
-            endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
+            endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
         getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
 
-    df = False
-    if opts.get("date"):
-        df = util.matchdate(opts["date"])
-
-    branches = opts.get('branch', []) + opts.get('only_branch', [])
-    opts['branch'] = [repo.lookupbranch(b) for b in branches]
-
-    displayer = cmdutil.show_changeset(ui, repo, opts, True)
-    def prep(ctx, fns):
-        rev = ctx.rev()
-        parents = [p for p in repo.changelog.parentrevs(rev)
-                   if p != nullrev]
-        if opts.get('no_merges') and len(parents) == 2:
-            return
-        if opts.get('only_merges') and len(parents) != 2:
-            return
-        if opts.get('branch') and ctx.branch() not in opts['branch']:
-            return
-        if df and not df(ctx.date()[0]):
-            return
-
-        lower = encoding.lower
-        if opts.get('user'):
-            luser = lower(ctx.user())
-            for k in [lower(x) for x in opts['user']]:
-                if (k in luser):
-                    break
-            else:
-                return
-        if opts.get('keyword'):
-            luser = lower(ctx.user())
-            ldesc = lower(ctx.description())
-            lfiles = lower(" ".join(ctx.files()))
-            for k in [lower(x) for x in opts['keyword']]:
-                if (k in luser or k in ldesc or k in lfiles):
-                    break
-            else:
-                return
-
+    displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
+    for rev in revs:
+        if count == limit:
+            break
+        ctx = repo[rev]
         copies = None
         if getrenamed is not None and rev:
             copies = []
@@ -4110,22 +4077,11 @@ 
                 rename = getrenamed(fn, rev)
                 if rename:
                     copies.append((fn, rename[0]))
-
-        revmatchfn = None
-        if opts.get('patch') or opts.get('stat'):
-            if opts.get('follow') or opts.get('follow_first'):
-                # note: this might be wrong when following through merges
-                revmatchfn = scmutil.match(repo[None], fns, default='path')
-            else:
-                revmatchfn = matchfn
-
+        revmatchfn = filematcher and filematcher(ctx.rev()) or None
         displayer.show(ctx, copies=copies, matchfn=revmatchfn)
-
-    for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
-        if displayer.flush(ctx.rev()):
+        if displayer.flush(rev):
             count += 1
-        if count == limit:
-            break
+
     displayer.close()
 
 @command('manifest',