# HG changeset patch # User Yuya Nishihara # Date 2018-01-03 06:46:15 # Node ID b25fa5da4ca2832986906dcf49b28e6ddee0657e # Parent 659dfbd852e28656fad791dce4f2a22646888ba8 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. 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'))) , - >, + , > $ 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:*'))) , - >, + , > 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')) - , - > + [] + Test --follow and multiple renames $ hg up -q tip $ testlog -f e [] - (func - (symbol 'follow') - (string 'e')) - , - > + [] + Test --follow and multiple filelog heads $ hg up -q 2 $ testlog -f g [] - (func - (symbol 'follow') - (string 'g')) - , - > + [] + $ 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')) - , - > + [] + $ 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')))) - , - , - >> + [] + $ 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 '.')) - , - > + [] + Cannot compare with log --follow-first FILE as it never worked $ hg log -G --print-revset --follow-first e [] - (func - (symbol '_followfirst') - (string 'e')) - , - > + [] + $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n' @ 6 merge 5 and 4 |\ @@ -1958,20 +1911,14 @@ Test --removed > $ 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'))) , - >, + , > Test --patch and --stat with --follow and --follow-first @@ -2353,12 +2300,8 @@ Test subdir , set([1])> $ testlog -f ../b [] - (func - (symbol 'follow') - (string 'b')) - , - > + [] + $ cd .. Test --hidden