diff -r a41eeb877d07 -r 1c31b343e514 mercurial/match.py --- a/mercurial/match.py Sun Jun 18 00:09:39 2023 +0200 +++ b/mercurial/match.py Mon Jun 12 16:51:08 2023 +0200 @@ -30,6 +30,7 @@ b're', b'glob', b'path', + b'filepath', b'relglob', b'relpath', b'relre', @@ -181,6 +182,8 @@ 're:' - a regular expression 'path:' - a path relative to repository root, which is matched recursively + 'filepath:' - an exact path to a single file, relative to the + repository root 'rootfilesin:' - a path relative to repository root, which is matched non-recursively (will not match subdirectories) 'relglob:' - an unrooted glob (*.c matches C files in all dirs) @@ -334,10 +337,18 @@ """Convert 'kind:pat' from the patterns list to tuples with kind and normalized and rooted patterns and with listfiles expanded.""" kindpats = [] + kinds_to_normalize = ( + b'relglob', + b'path', + b'filepath', + b'rootfilesin', + b'rootglob', + ) + for kind, pat in [_patsplit(p, default) for p in patterns]: if kind in cwdrelativepatternkinds: pat = pathutil.canonpath(root, cwd, pat, auditor=auditor) - elif kind in (b'relglob', b'path', b'rootfilesin', b'rootglob'): + elif kind in kinds_to_normalize: pat = util.normpath(pat) elif kind in (b'listfile', b'listfile0'): try: @@ -1340,6 +1351,10 @@ return b'' if kind == b're': return pat + if kind == b'filepath': + raise error.ProgrammingError( + "'filepath:' patterns should not be converted to a regex" + ) if kind in (b'path', b'relpath'): if pat == b'.': return b'' @@ -1444,7 +1459,14 @@ """ try: allgroups = [] - regexps = [_regex(k, p, globsuffix) for (k, p, s) in kindpats] + regexps = [] + exact = set() + for (kind, pattern, _source) in kindpats: + if kind == b'filepath': + exact.add(pattern) + continue + regexps.append(_regex(kind, pattern, globsuffix)) + fullregexp = _joinregexes(regexps) startidx = 0 @@ -1469,9 +1491,20 @@ allgroups.append(_joinregexes(group)) allmatchers = [_rematcher(g) for g in allgroups] func = lambda s: any(m(s) for m in allmatchers) - return fullregexp, func + + actualfunc = func + if exact: + # An empty regex will always match, so only call the regex if + # there were any actual patterns to match. + if not regexps: + actualfunc = lambda s: s in exact + else: + actualfunc = lambda s: s in exact or func(s) + return fullregexp, actualfunc except re.error: for k, p, s in kindpats: + if k == b'filepath': + continue try: _rematcher(_regex(k, p, globsuffix)) except re.error: @@ -1502,7 +1535,7 @@ break root.append(p) r.append(b'/'.join(root)) - elif kind in (b'relpath', b'path'): + elif kind in (b'relpath', b'path', b'filepath'): if pat == b'.': pat = b'' r.append(pat)