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.
--- a/mercurial/cmdutil.py Wed Jan 03 15:13:22 2018 +0900
+++ b/mercurial/cmdutil.py Wed Jan 03 15:46:15 2018 +0900
@@ -2371,6 +2371,13 @@
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 @@
_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 @@
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 @@
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 @@
# _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 @@
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 @@
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'):
--- a/tests/test-glog.t Wed Jan 03 15:13:22 2018 +0900
+++ b/tests/test-glog.t Wed Jan 03 15:46:15 2018 +0900
@@ -1724,20 +1724,14 @@
$ 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 @@
$ 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 @@
$ 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 @@
$ 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 @@
$ 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 @@
$ 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 @@
<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 @@
<spanset- 0:9>, set([1])>
$ testlog -f ../b
[]
- (func
- (symbol 'follow')
- (string 'b'))
- <filteredset
- <spanset- 0:4>,
- <generatorsetdesc+>>
+ []
+ <generatorsetdesc->
$ cd ..
Test --hidden