Patchwork [5,of,9] log: resolve --follow thoroughly in getlogrevs()

login
register
mail settings
Submitter Yuya Nishihara
Date Jan. 15, 2018, 1:26 p.m.
Message ID <a5c339104828029e996c.1516022799@mimosa>
Download mbox | patch
Permalink /patch/26766/
State Accepted
Headers show

Comments

Yuya Nishihara - Jan. 15, 2018, 1:26 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1514961975 -32400
#      Wed Jan 03 15:46:15 2018 +0900
# Node ID a5c339104828029e996ce749862fe0de7f8695ba
# Parent  cc1b4965788f4d478ff055e16b12487be941ff0c
log: resolve --follow thoroughly in getlogrevs()

This makes sense because --follow isn't really an option to filter revisions,
but an option to extend revisions to be filtered.

_fileancestors() is a minimal copy of revset._follow(). They are slightly
different in that which revision the matcher sees. _fileancestors() also
uses ctx.walk() instead of ctx.manifest().walk() to show a better warning
on bad match, which will be tested later.

Patch

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -2371,6 +2371,13 @@  def _makelogmatcher(repo, pats, opts):
 
     return match, pats, slowpath
 
+def _fileancestors(repo, revs, match, followfirst):
+    fctxs = []
+    for r in revs:
+        ctx = repo[r]
+        fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
+    return dagop.filerevancestors(fctxs, followfirst=followfirst)
+
 def _makefollowlogfilematcher(repo, files, followfirst):
     # When displaying a revision with --patch --follow FILE, we have
     # to know which file of the revision must be diffed. With
@@ -2406,14 +2413,10 @@  def _makenofollowlogfilematcher(repo, pa
 _opt2logrevset = {
     'no_merges':        ('not merge()', None),
     'only_merges':      ('merge()', None),
-    '_ancestors':       ('ancestors(%r)', None),
-    '_fancestors':      ('_firstancestors(%r)', None),
     '_matchfiles':      (None, '_matchfiles(%ps)'),
     'date':             ('date(%s)', None),
     'branch':           ('branch(%s)', '%lr'),
     '_patslog':         ('filelog(%s)', '%lr'),
-    '_patsfollow':      ('follow(%s)', '%lr'),
-    '_patsfollowfirst': ('_followfirst(%s)', '%lr'),
     'keyword':          ('keyword(%s)', '%lr'),
     'prune':            ('ancestors(%s)', 'not %lr'),
     'user':             ('user(%s)', '%lr'),
@@ -2429,19 +2432,12 @@  def _makelogrevset(repo, match, pats, sl
     opts = dict(opts)
     # follow or not follow?
     follow = opts.get('follow') or opts.get('follow_first')
-    if opts.get('follow_first'):
-        followfirst = 1
-    else:
-        followfirst = 0
 
     # branch and only_branch are really aliases and must be handled at
     # the same time
     opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
     opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
 
-    fpats = ('_patsfollow', '_patsfollowfirst')
-    fnopats = ('_ancestors', '_fancestors')
-
     if slowpath:
         # See walkchangerevs() slow path.
         #
@@ -2459,19 +2455,8 @@  def _makelogrevset(repo, match, pats, sl
         for p in opts.get('exclude', []):
             matchargs.append('x:' + p)
         opts['_matchfiles'] = matchargs
-        if follow:
-            opts[fnopats[followfirst]] = '.'
-    else:
-        if follow:
-            if pats:
-                # follow() revset interprets its file argument as a
-                # manifest entry, so use match.files(), not pats.
-                opts[fpats[followfirst]] = list(match.files())
-            else:
-                op = fnopats[followfirst]
-                opts[op] = '.'
-        else:
-            opts['_patslog'] = list(pats)
+    elif not follow:
+        opts['_patslog'] = list(pats)
 
     filematcher = None
     if opts.get('patch') or opts.get('stat'):
@@ -2482,7 +2467,7 @@  def _makelogrevset(repo, match, pats, sl
             # _makefollowlogfilematcher expects its files argument to be
             # relative to the repo root, so use match.files(), not pats.
             filematcher = _makefollowlogfilematcher(repo, match.files(),
-                                                    followfirst)
+                                                    opts.get('follow_first'))
         else:
             filematcher = _makenofollowlogfilematcher(repo, pats, opts)
             if filematcher is None:
@@ -2511,13 +2496,14 @@  def _makelogrevset(repo, match, pats, sl
     return expr, filematcher
 
 def _logrevs(repo, opts):
+    """Return the initial set of revisions to be filtered or followed"""
     follow = opts.get('follow') or opts.get('follow_first')
     if opts.get('rev'):
         revs = scmutil.revrange(repo, opts['rev'])
     elif follow and repo.dirstate.p1() == nullid:
         revs = smartset.baseset()
     elif follow:
-        revs = repo.revs('reverse(:.)')
+        revs = repo.revs('.')
     else:
         revs = smartset.spanset(repo)
         revs.reverse()
@@ -2541,8 +2527,11 @@  def getlogrevs(repo, pats, opts):
     if not revs:
         return smartset.baseset(), None
     match, pats, slowpath = _makelogmatcher(repo, pats, opts)
-    if opts.get('rev') and follow:
-        revs = dagop.revancestors(repo, revs, followfirst=followfirst)
+    if follow:
+        if opts.get('rev') or slowpath or not pats:
+            revs = dagop.revancestors(repo, revs, followfirst=followfirst)
+        else:
+            revs = _fileancestors(repo, revs, match, followfirst)
         revs.reverse()
     expr, filematcher = _makelogrevset(repo, match, pats, slowpath, opts)
     if opts.get('graph') and opts.get('rev'):
diff --git a/tests/test-glog.t b/tests/test-glog.t
--- a/tests/test-glog.t
+++ b/tests/test-glog.t
@@ -1724,20 +1724,14 @@  Test --follow on a directory
   $ hg up -q '.^'
   $ testlog -f dir
   []
-  (and
-    (func
-      (symbol 'ancestors')
-      (symbol '.'))
-    (func
-      (symbol '_matchfiles')
-      (list
-        (string 'r:')
-        (string 'd:relpath')
-        (string 'p:dir'))))
+  (func
+    (symbol '_matchfiles')
+    (list
+      (string 'r:')
+      (string 'd:relpath')
+      (string 'p:dir')))
   <filteredset
-    <filteredset
-      <spanset- 0:4>,
-      <generatorsetdesc+>>,
+    <generatorsetdesc->,
     <matchfiles patterns=['dir'], include=[] exclude=[], default='relpath', rev=None>>
   $ hg up -q tip
 
@@ -1752,20 +1746,14 @@  Test --follow and patterns
 
   $ testlog -f 'glob:*'
   []
-  (and
-    (func
-      (symbol 'ancestors')
-      (symbol '.'))
-    (func
-      (symbol '_matchfiles')
-      (list
-        (string 'r:')
-        (string 'd:relpath')
-        (string 'p:glob:*'))))
+  (func
+    (symbol '_matchfiles')
+    (list
+      (string 'r:')
+      (string 'd:relpath')
+      (string 'p:glob:*')))
   <filteredset
-    <filteredset
-      <spanset- 0:5>,
-      <generatorsetdesc+>>,
+    <generatorsetdesc->,
     <matchfiles patterns=['glob:*'], include=[] exclude=[], default='relpath', rev=None>>
 
 Test --follow on a single rename
@@ -1773,36 +1761,24 @@  Test --follow on a single rename
   $ hg up -q 2
   $ testlog -f a
   []
-  (func
-    (symbol 'follow')
-    (string 'a'))
-  <filteredset
-    <spanset- 0:3>,
-    <generatorsetdesc+>>
+  []
+  <generatorsetdesc->
 
 Test --follow and multiple renames
 
   $ hg up -q tip
   $ testlog -f e
   []
-  (func
-    (symbol 'follow')
-    (string 'e'))
-  <filteredset
-    <spanset- 0:5>,
-    <generatorsetdesc+>>
+  []
+  <generatorsetdesc->
 
 Test --follow and multiple filelog heads
 
   $ hg up -q 2
   $ testlog -f g
   []
-  (func
-    (symbol 'follow')
-    (string 'g'))
-  <filteredset
-    <spanset- 0:3>,
-    <generatorsetdesc+>>
+  []
+  <generatorsetdesc->
   $ cat log.nodes
   nodetag 2
   nodetag 1
@@ -1810,12 +1786,8 @@  Test --follow and multiple filelog heads
   $ hg up -q tip
   $ testlog -f g
   []
-  (func
-    (symbol 'follow')
-    (string 'g'))
-  <filteredset
-    <spanset- 0:5>,
-    <generatorsetdesc+>>
+  []
+  <generatorsetdesc->
   $ cat log.nodes
   nodetag 3
   nodetag 2
@@ -1825,19 +1797,8 @@  Test --follow and multiple files
 
   $ testlog -f g e
   []
-  (or
-    (list
-      (func
-        (symbol 'follow')
-        (string 'g'))
-      (func
-        (symbol 'follow')
-        (string 'e'))))
-  <filteredset
-    <spanset- 0:5>,
-    <addset
-      <generatorsetdesc+>,
-      <generatorsetdesc+>>>
+  []
+  <generatorsetdesc->
   $ cat log.nodes
   nodetag 4
   nodetag 3
@@ -1866,23 +1827,15 @@  Test --follow-first
   $ hg ci -m "merge 5 and 4"
   $ testlog --follow-first
   []
-  (func
-    (symbol '_firstancestors')
-    (symbol '.'))
-  <filteredset
-    <spanset- 0:7>,
-    <generatorsetdesc+>>
+  []
+  <generatorsetdesc->
 
 Cannot compare with log --follow-first FILE as it never worked
 
   $ hg log -G --print-revset --follow-first e
   []
-  (func
-    (symbol '_followfirst')
-    (string 'e'))
-  <filteredset
-    <spanset- 0:7>,
-    <generatorsetdesc+>>
+  []
+  <generatorsetdesc->
   $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
   @    6 merge 5 and 4
   |\
@@ -1958,20 +1911,14 @@  Test --removed
     <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=None>>
   $ testlog --removed --follow a
   []
-  (and
-    (func
-      (symbol 'ancestors')
-      (symbol '.'))
-    (func
-      (symbol '_matchfiles')
-      (list
-        (string 'r:')
-        (string 'd:relpath')
-        (string 'p:a'))))
+  (func
+    (symbol '_matchfiles')
+    (list
+      (string 'r:')
+      (string 'd:relpath')
+      (string 'p:a')))
   <filteredset
-    <filteredset
-      <spanset- 0:5>,
-      <generatorsetdesc+>>,
+    <generatorsetdesc->,
     <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=None>>
 
 Test --patch and --stat with --follow and --follow-first
@@ -2353,12 +2300,8 @@  Test subdir
     <spanset- 0:9>, set([1])>
   $ testlog -f ../b
   []
-  (func
-    (symbol 'follow')
-    (string 'b'))
-  <filteredset
-    <spanset- 0:4>,
-    <generatorsetdesc+>>
+  []
+  <generatorsetdesc->
   $ cd ..
 
 Test --hidden