comparison mercurial/revset.py @ 35816:f6ca1e11d8b4 stable

revset: evaluate filesets against each revision for 'file()' (issue5778) After f2aeff8a87b6, the fileset was evaluated to a set of files against the working directory, and then those files were applied against each revision. The result was nonsense. For example, `hg log -r 'file("set:exec()")'` on the Mercurial repo listed revision 0 because it has the `hg` script, which is currently +x. But that bit wasn't applied until revision 280 (which 'contains()' properly indicates). This technique was borrowed from checkstatus(), which services adds(), modifies(), and removes(), so it seems safe enough. The 'r:' case is explicitly assigned to wdirrev, freeing up rev=None to mean "re-evaluate at each revision". The distinction is important to avoid behavior changes with `hg log set:...` (test-largefiles-misc.t and test-fileset-generated.t drop current log output without this). I'm not sure what the right behavior for that is (1fd352aa08fc explicitly enabled this behavior for graphlog), but the day before the release isn't the time to experiment.
author Matt Harbison <matt_harbison@yahoo.com>
date Sun, 28 Jan 2018 14:08:59 -0500
parents 134ef400cb11
children 00a56c83ab64
comparison
equal deleted inserted replaced
35815:b5df7fcf5d80 35816:f6ca1e11d8b4
1063 exc.append(value) 1063 exc.append(value)
1064 elif prefix == 'r:': 1064 elif prefix == 'r:':
1065 if rev is not None: 1065 if rev is not None:
1066 raise error.ParseError('_matchfiles expected at most one ' 1066 raise error.ParseError('_matchfiles expected at most one '
1067 'revision') 1067 'revision')
1068 if value != '': # empty means working directory; leave rev as None 1068 if value == '': # empty means working directory
1069 rev = node.wdirrev
1070 else:
1069 rev = value 1071 rev = value
1070 elif prefix == 'd:': 1072 elif prefix == 'd:':
1071 if default is not None: 1073 if default is not None:
1072 raise error.ParseError('_matchfiles expected at most one ' 1074 raise error.ParseError('_matchfiles expected at most one '
1073 'default mode') 1075 'default mode')
1074 default = value 1076 default = value
1075 else: 1077 else:
1076 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix) 1078 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1077 if not default: 1079 if not default:
1078 default = 'glob' 1080 default = 'glob'
1079 1081 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc)
1080 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc, 1082
1081 exclude=exc, ctx=repo[rev], default=default) 1083 mcache = [None]
1082 1084
1083 # This directly read the changelog data as creating changectx for all 1085 # This directly read the changelog data as creating changectx for all
1084 # revisions is quite expensive. 1086 # revisions is quite expensive.
1085 getfiles = repo.changelog.readfiles 1087 getfiles = repo.changelog.readfiles
1086 wdirrev = node.wdirrev 1088 wdirrev = node.wdirrev
1087 def matches(x): 1089 def matches(x):
1088 if x == wdirrev: 1090 if x == wdirrev:
1089 files = repo[x].files() 1091 files = repo[x].files()
1090 else: 1092 else:
1091 files = getfiles(x) 1093 files = getfiles(x)
1094
1095 if not mcache[0] or (hasset and rev is None):
1096 r = x if rev is None else rev
1097 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats,
1098 include=inc, exclude=exc, ctx=repo[r],
1099 default=default)
1100 m = mcache[0]
1101
1092 for f in files: 1102 for f in files:
1093 if m(f): 1103 if m(f):
1094 return True 1104 return True
1095 return False 1105 return False
1096 1106