Mercurial > hg
comparison mercurial/match.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 72890d8f9860 |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
19 pathutil, | 19 pathutil, |
20 policy, | 20 policy, |
21 pycompat, | 21 pycompat, |
22 util, | 22 util, |
23 ) | 23 ) |
24 from .utils import ( | 24 from .utils import stringutil |
25 stringutil, | 25 |
26 rustmod = policy.importrust(r'filepatterns') | |
27 | |
28 allpatternkinds = ( | |
29 're', | |
30 'glob', | |
31 'path', | |
32 'relglob', | |
33 'relpath', | |
34 'relre', | |
35 'rootglob', | |
36 'listfile', | |
37 'listfile0', | |
38 'set', | |
39 'include', | |
40 'subinclude', | |
41 'rootfilesin', | |
26 ) | 42 ) |
27 | |
28 rustmod = policy.importrust(r'filepatterns') | |
29 | |
30 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre', | |
31 'rootglob', | |
32 'listfile', 'listfile0', 'set', 'include', 'subinclude', | |
33 'rootfilesin') | |
34 cwdrelativepatternkinds = ('relpath', 'glob') | 43 cwdrelativepatternkinds = ('relpath', 'glob') |
35 | 44 |
36 propertycache = util.propertycache | 45 propertycache = util.propertycache |
46 | |
37 | 47 |
38 def _rematcher(regex): | 48 def _rematcher(regex): |
39 '''compile the regexp with the best available regexp engine and return a | 49 '''compile the regexp with the best available regexp engine and return a |
40 matcher function''' | 50 matcher function''' |
41 m = util.re.compile(regex) | 51 m = util.re.compile(regex) |
43 # slightly faster, provided by facebook's re2 bindings | 53 # slightly faster, provided by facebook's re2 bindings |
44 return m.test_match | 54 return m.test_match |
45 except AttributeError: | 55 except AttributeError: |
46 return m.match | 56 return m.match |
47 | 57 |
58 | |
48 def _expandsets(kindpats, ctx=None, listsubrepos=False, badfn=None): | 59 def _expandsets(kindpats, ctx=None, listsubrepos=False, badfn=None): |
49 '''Returns the kindpats list with the 'set' patterns expanded to matchers''' | 60 '''Returns the kindpats list with the 'set' patterns expanded to matchers''' |
50 matchers = [] | 61 matchers = [] |
51 other = [] | 62 other = [] |
52 | 63 |
53 for kind, pat, source in kindpats: | 64 for kind, pat, source in kindpats: |
54 if kind == 'set': | 65 if kind == 'set': |
55 if ctx is None: | 66 if ctx is None: |
56 raise error.ProgrammingError("fileset expression with no " | 67 raise error.ProgrammingError( |
57 "context") | 68 "fileset expression with no " "context" |
69 ) | |
58 matchers.append(ctx.matchfileset(pat, badfn=badfn)) | 70 matchers.append(ctx.matchfileset(pat, badfn=badfn)) |
59 | 71 |
60 if listsubrepos: | 72 if listsubrepos: |
61 for subpath in ctx.substate: | 73 for subpath in ctx.substate: |
62 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn) | 74 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn) |
64 matchers.append(pm) | 76 matchers.append(pm) |
65 | 77 |
66 continue | 78 continue |
67 other.append((kind, pat, source)) | 79 other.append((kind, pat, source)) |
68 return matchers, other | 80 return matchers, other |
81 | |
69 | 82 |
70 def _expandsubinclude(kindpats, root): | 83 def _expandsubinclude(kindpats, root): |
71 '''Returns the list of subinclude matcher args and the kindpats without the | 84 '''Returns the list of subinclude matcher args and the kindpats without the |
72 subincludes in it.''' | 85 subincludes in it.''' |
73 relmatchers = [] | 86 relmatchers = [] |
88 relmatchers.append((prefix, matcherargs)) | 101 relmatchers.append((prefix, matcherargs)) |
89 else: | 102 else: |
90 other.append((kind, pat, source)) | 103 other.append((kind, pat, source)) |
91 | 104 |
92 return relmatchers, other | 105 return relmatchers, other |
106 | |
93 | 107 |
94 def _kindpatsalwaysmatch(kindpats): | 108 def _kindpatsalwaysmatch(kindpats): |
95 """"Checks whether the kindspats match everything, as e.g. | 109 """"Checks whether the kindspats match everything, as e.g. |
96 'relpath:.' does. | 110 'relpath:.' does. |
97 """ | 111 """ |
98 for kind, pat, source in kindpats: | 112 for kind, pat, source in kindpats: |
99 if pat != '' or kind not in ['relpath', 'glob']: | 113 if pat != '' or kind not in ['relpath', 'glob']: |
100 return False | 114 return False |
101 return True | 115 return True |
102 | 116 |
103 def _buildkindpatsmatcher(matchercls, root, kindpats, ctx=None, | 117 |
104 listsubrepos=False, badfn=None): | 118 def _buildkindpatsmatcher( |
119 matchercls, root, kindpats, ctx=None, listsubrepos=False, badfn=None | |
120 ): | |
105 matchers = [] | 121 matchers = [] |
106 fms, kindpats = _expandsets(kindpats, ctx=ctx, | 122 fms, kindpats = _expandsets( |
107 listsubrepos=listsubrepos, badfn=badfn) | 123 kindpats, ctx=ctx, listsubrepos=listsubrepos, badfn=badfn |
124 ) | |
108 if kindpats: | 125 if kindpats: |
109 m = matchercls(root, kindpats, badfn=badfn) | 126 m = matchercls(root, kindpats, badfn=badfn) |
110 matchers.append(m) | 127 matchers.append(m) |
111 if fms: | 128 if fms: |
112 matchers.extend(fms) | 129 matchers.extend(fms) |
114 return nevermatcher(badfn=badfn) | 131 return nevermatcher(badfn=badfn) |
115 if len(matchers) == 1: | 132 if len(matchers) == 1: |
116 return matchers[0] | 133 return matchers[0] |
117 return unionmatcher(matchers) | 134 return unionmatcher(matchers) |
118 | 135 |
119 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob', | 136 |
120 auditor=None, ctx=None, listsubrepos=False, warn=None, | 137 def match( |
121 badfn=None, icasefs=False): | 138 root, |
139 cwd, | |
140 patterns=None, | |
141 include=None, | |
142 exclude=None, | |
143 default='glob', | |
144 auditor=None, | |
145 ctx=None, | |
146 listsubrepos=False, | |
147 warn=None, | |
148 badfn=None, | |
149 icasefs=False, | |
150 ): | |
122 r"""build an object to match a set of file patterns | 151 r"""build an object to match a set of file patterns |
123 | 152 |
124 arguments: | 153 arguments: |
125 root - the canonical root of the tree you're matching against | 154 root - the canonical root of the tree you're matching against |
126 cwd - the current working directory, if relevant | 155 cwd - the current working directory, if relevant |
221 if patterns: | 250 if patterns: |
222 kindpats = normalize(patterns, default, root, cwd, auditor, warn) | 251 kindpats = normalize(patterns, default, root, cwd, auditor, warn) |
223 if _kindpatsalwaysmatch(kindpats): | 252 if _kindpatsalwaysmatch(kindpats): |
224 m = alwaysmatcher(badfn) | 253 m = alwaysmatcher(badfn) |
225 else: | 254 else: |
226 m = _buildkindpatsmatcher(patternmatcher, root, kindpats, ctx=ctx, | 255 m = _buildkindpatsmatcher( |
227 listsubrepos=listsubrepos, badfn=badfn) | 256 patternmatcher, |
257 root, | |
258 kindpats, | |
259 ctx=ctx, | |
260 listsubrepos=listsubrepos, | |
261 badfn=badfn, | |
262 ) | |
228 else: | 263 else: |
229 # It's a little strange that no patterns means to match everything. | 264 # It's a little strange that no patterns means to match everything. |
230 # Consider changing this to match nothing (probably using nevermatcher). | 265 # Consider changing this to match nothing (probably using nevermatcher). |
231 m = alwaysmatcher(badfn) | 266 m = alwaysmatcher(badfn) |
232 | 267 |
233 if include: | 268 if include: |
234 kindpats = normalize(include, 'glob', root, cwd, auditor, warn) | 269 kindpats = normalize(include, 'glob', root, cwd, auditor, warn) |
235 im = _buildkindpatsmatcher(includematcher, root, kindpats, ctx=ctx, | 270 im = _buildkindpatsmatcher( |
236 listsubrepos=listsubrepos, badfn=None) | 271 includematcher, |
272 root, | |
273 kindpats, | |
274 ctx=ctx, | |
275 listsubrepos=listsubrepos, | |
276 badfn=None, | |
277 ) | |
237 m = intersectmatchers(m, im) | 278 m = intersectmatchers(m, im) |
238 if exclude: | 279 if exclude: |
239 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn) | 280 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn) |
240 em = _buildkindpatsmatcher(includematcher, root, kindpats, ctx=ctx, | 281 em = _buildkindpatsmatcher( |
241 listsubrepos=listsubrepos, badfn=None) | 282 includematcher, |
283 root, | |
284 kindpats, | |
285 ctx=ctx, | |
286 listsubrepos=listsubrepos, | |
287 badfn=None, | |
288 ) | |
242 m = differencematcher(m, em) | 289 m = differencematcher(m, em) |
243 return m | 290 return m |
244 | 291 |
292 | |
245 def exact(files, badfn=None): | 293 def exact(files, badfn=None): |
246 return exactmatcher(files, badfn=badfn) | 294 return exactmatcher(files, badfn=badfn) |
247 | 295 |
296 | |
248 def always(badfn=None): | 297 def always(badfn=None): |
249 return alwaysmatcher(badfn) | 298 return alwaysmatcher(badfn) |
250 | 299 |
300 | |
251 def never(badfn=None): | 301 def never(badfn=None): |
252 return nevermatcher(badfn) | 302 return nevermatcher(badfn) |
303 | |
253 | 304 |
254 def badmatch(match, badfn): | 305 def badmatch(match, badfn): |
255 """Make a copy of the given matcher, replacing its bad method with the given | 306 """Make a copy of the given matcher, replacing its bad method with the given |
256 one. | 307 one. |
257 """ | 308 """ |
258 m = copy.copy(match) | 309 m = copy.copy(match) |
259 m.bad = badfn | 310 m.bad = badfn |
260 return m | 311 return m |
312 | |
261 | 313 |
262 def _donormalize(patterns, default, root, cwd, auditor=None, warn=None): | 314 def _donormalize(patterns, default, root, cwd, auditor=None, warn=None): |
263 '''Convert 'kind:pat' from the patterns list to tuples with kind and | 315 '''Convert 'kind:pat' from the patterns list to tuples with kind and |
264 normalized and rooted patterns and with listfiles expanded.''' | 316 normalized and rooted patterns and with listfiles expanded.''' |
265 kindpats = [] | 317 kindpats = [] |
276 else: | 328 else: |
277 files = files.splitlines() | 329 files = files.splitlines() |
278 files = [f for f in files if f] | 330 files = [f for f in files if f] |
279 except EnvironmentError: | 331 except EnvironmentError: |
280 raise error.Abort(_("unable to read file list (%s)") % pat) | 332 raise error.Abort(_("unable to read file list (%s)") % pat) |
281 for k, p, source in _donormalize(files, default, root, cwd, | 333 for k, p, source in _donormalize( |
282 auditor, warn): | 334 files, default, root, cwd, auditor, warn |
335 ): | |
283 kindpats.append((k, p, pat)) | 336 kindpats.append((k, p, pat)) |
284 continue | 337 continue |
285 elif kind == 'include': | 338 elif kind == 'include': |
286 try: | 339 try: |
287 fullpath = os.path.join(root, util.localpath(pat)) | 340 fullpath = os.path.join(root, util.localpath(pat)) |
288 includepats = readpatternfile(fullpath, warn) | 341 includepats = readpatternfile(fullpath, warn) |
289 for k, p, source in _donormalize(includepats, default, | 342 for k, p, source in _donormalize( |
290 root, cwd, auditor, warn): | 343 includepats, default, root, cwd, auditor, warn |
344 ): | |
291 kindpats.append((k, p, source or pat)) | 345 kindpats.append((k, p, source or pat)) |
292 except error.Abort as inst: | 346 except error.Abort as inst: |
293 raise error.Abort('%s: %s' % (pat, inst[0])) | 347 raise error.Abort('%s: %s' % (pat, inst[0])) |
294 except IOError as inst: | 348 except IOError as inst: |
295 if warn: | 349 if warn: |
296 warn(_("skipping unreadable pattern file '%s': %s\n") % | 350 warn( |
297 (pat, stringutil.forcebytestr(inst.strerror))) | 351 _("skipping unreadable pattern file '%s': %s\n") |
352 % (pat, stringutil.forcebytestr(inst.strerror)) | |
353 ) | |
298 continue | 354 continue |
299 # else: re or relre - which cannot be normalized | 355 # else: re or relre - which cannot be normalized |
300 kindpats.append((kind, pat, '')) | 356 kindpats.append((kind, pat, '')) |
301 return kindpats | 357 return kindpats |
302 | 358 |
359 | |
303 class basematcher(object): | 360 class basematcher(object): |
304 | |
305 def __init__(self, badfn=None): | 361 def __init__(self, badfn=None): |
306 if badfn is not None: | 362 if badfn is not None: |
307 self.bad = badfn | 363 self.bad = badfn |
308 | 364 |
309 def __call__(self, fn): | 365 def __call__(self, fn): |
310 return self.matchfn(fn) | 366 return self.matchfn(fn) |
367 | |
311 # Callbacks related to how the matcher is used by dirstate.walk. | 368 # Callbacks related to how the matcher is used by dirstate.walk. |
312 # Subscribers to these events must monkeypatch the matcher object. | 369 # Subscribers to these events must monkeypatch the matcher object. |
313 def bad(self, f, msg): | 370 def bad(self, f, msg): |
314 '''Callback from dirstate.walk for each explicit file that can't be | 371 '''Callback from dirstate.walk for each explicit file that can't be |
315 found/accessed, with an error message.''' | 372 found/accessed, with an error message.''' |
417 def anypats(self): | 474 def anypats(self): |
418 '''None of .always(), .isexact(), and .prefix() is true -- | 475 '''None of .always(), .isexact(), and .prefix() is true -- |
419 optimizations will be difficult.''' | 476 optimizations will be difficult.''' |
420 return not self.always() and not self.isexact() and not self.prefix() | 477 return not self.always() and not self.isexact() and not self.prefix() |
421 | 478 |
479 | |
422 class alwaysmatcher(basematcher): | 480 class alwaysmatcher(basematcher): |
423 '''Matches everything.''' | 481 '''Matches everything.''' |
424 | 482 |
425 def __init__(self, badfn=None): | 483 def __init__(self, badfn=None): |
426 super(alwaysmatcher, self).__init__(badfn) | 484 super(alwaysmatcher, self).__init__(badfn) |
437 def visitchildrenset(self, dir): | 495 def visitchildrenset(self, dir): |
438 return 'all' | 496 return 'all' |
439 | 497 |
440 def __repr__(self): | 498 def __repr__(self): |
441 return r'<alwaysmatcher>' | 499 return r'<alwaysmatcher>' |
500 | |
442 | 501 |
443 class nevermatcher(basematcher): | 502 class nevermatcher(basematcher): |
444 '''Matches nothing.''' | 503 '''Matches nothing.''' |
445 | 504 |
446 def __init__(self, badfn=None): | 505 def __init__(self, badfn=None): |
464 return set() | 523 return set() |
465 | 524 |
466 def __repr__(self): | 525 def __repr__(self): |
467 return r'<nevermatcher>' | 526 return r'<nevermatcher>' |
468 | 527 |
528 | |
469 class predicatematcher(basematcher): | 529 class predicatematcher(basematcher): |
470 """A matcher adapter for a simple boolean function""" | 530 """A matcher adapter for a simple boolean function""" |
471 | 531 |
472 def __init__(self, predfn, predrepr=None, badfn=None): | 532 def __init__(self, predfn, predrepr=None, badfn=None): |
473 super(predicatematcher, self).__init__(badfn) | 533 super(predicatematcher, self).__init__(badfn) |
474 self.matchfn = predfn | 534 self.matchfn = predfn |
475 self._predrepr = predrepr | 535 self._predrepr = predrepr |
476 | 536 |
477 @encoding.strmethod | 537 @encoding.strmethod |
478 def __repr__(self): | 538 def __repr__(self): |
479 s = (stringutil.buildrepr(self._predrepr) | 539 s = stringutil.buildrepr(self._predrepr) or pycompat.byterepr( |
480 or pycompat.byterepr(self.matchfn)) | 540 self.matchfn |
541 ) | |
481 return '<predicatenmatcher pred=%s>' % s | 542 return '<predicatenmatcher pred=%s>' % s |
543 | |
482 | 544 |
483 def normalizerootdir(dir, funcname): | 545 def normalizerootdir(dir, funcname): |
484 if dir == '.': | 546 if dir == '.': |
485 util.nouideprecwarn("match.%s() no longer accepts " | 547 util.nouideprecwarn( |
486 "'.', use '' instead." % funcname, '5.1') | 548 "match.%s() no longer accepts " "'.', use '' instead." % funcname, |
549 '5.1', | |
550 ) | |
487 return '' | 551 return '' |
488 return dir | 552 return dir |
489 | 553 |
490 | 554 |
491 class patternmatcher(basematcher): | 555 class patternmatcher(basematcher): |
534 | 598 |
535 def visitdir(self, dir): | 599 def visitdir(self, dir): |
536 dir = normalizerootdir(dir, 'visitdir') | 600 dir = normalizerootdir(dir, 'visitdir') |
537 if self._prefix and dir in self._fileset: | 601 if self._prefix and dir in self._fileset: |
538 return 'all' | 602 return 'all' |
539 return (dir in self._fileset or | 603 return ( |
540 dir in self._dirs or | 604 dir in self._fileset |
541 any(parentdir in self._fileset | 605 or dir in self._dirs |
542 for parentdir in util.finddirs(dir))) | 606 or any( |
607 parentdir in self._fileset for parentdir in util.finddirs(dir) | |
608 ) | |
609 ) | |
543 | 610 |
544 def visitchildrenset(self, dir): | 611 def visitchildrenset(self, dir): |
545 ret = self.visitdir(dir) | 612 ret = self.visitdir(dir) |
546 if ret is True: | 613 if ret is True: |
547 return 'this' | 614 return 'this' |
553 def prefix(self): | 620 def prefix(self): |
554 return self._prefix | 621 return self._prefix |
555 | 622 |
556 @encoding.strmethod | 623 @encoding.strmethod |
557 def __repr__(self): | 624 def __repr__(self): |
558 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats)) | 625 return '<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats) |
626 | |
559 | 627 |
560 # This is basically a reimplementation of util.dirs that stores the children | 628 # This is basically a reimplementation of util.dirs that stores the children |
561 # instead of just a count of them, plus a small optional optimization to avoid | 629 # instead of just a count of them, plus a small optional optimization to avoid |
562 # some directories we don't need. | 630 # some directories we don't need. |
563 class _dirchildren(object): | 631 class _dirchildren(object): |
586 # Unlike manifest._splittopdir, this does not suffix `dirname` with a | 654 # Unlike manifest._splittopdir, this does not suffix `dirname` with a |
587 # slash. | 655 # slash. |
588 oldpos = len(path) | 656 oldpos = len(path) |
589 pos = path.rfind('/') | 657 pos = path.rfind('/') |
590 while pos != -1: | 658 while pos != -1: |
591 yield path[:pos], path[pos + 1:oldpos] | 659 yield path[:pos], path[pos + 1 : oldpos] |
592 oldpos = pos | 660 oldpos = pos |
593 pos = path.rfind('/', 0, pos) | 661 pos = path.rfind('/', 0, pos) |
594 yield '', path[:oldpos] | 662 yield '', path[:oldpos] |
595 | 663 |
596 def get(self, path): | 664 def get(self, path): |
597 return self._dirs.get(path, set()) | 665 return self._dirs.get(path, set()) |
598 | 666 |
667 | |
599 class includematcher(basematcher): | 668 class includematcher(basematcher): |
600 | |
601 def __init__(self, root, kindpats, badfn=None): | 669 def __init__(self, root, kindpats, badfn=None): |
602 super(includematcher, self).__init__(badfn) | 670 super(includematcher, self).__init__(badfn) |
603 | 671 |
604 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)', root) | 672 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)', root) |
605 self._prefix = _prefix(kindpats) | 673 self._prefix = _prefix(kindpats) |
614 | 682 |
615 def visitdir(self, dir): | 683 def visitdir(self, dir): |
616 dir = normalizerootdir(dir, 'visitdir') | 684 dir = normalizerootdir(dir, 'visitdir') |
617 if self._prefix and dir in self._roots: | 685 if self._prefix and dir in self._roots: |
618 return 'all' | 686 return 'all' |
619 return (dir in self._roots or | 687 return ( |
620 dir in self._dirs or | 688 dir in self._roots |
621 dir in self._parents or | 689 or dir in self._dirs |
622 any(parentdir in self._roots | 690 or dir in self._parents |
623 for parentdir in util.finddirs(dir))) | 691 or any(parentdir in self._roots for parentdir in util.finddirs(dir)) |
692 ) | |
624 | 693 |
625 @propertycache | 694 @propertycache |
626 def _allparentschildren(self): | 695 def _allparentschildren(self): |
627 # It may seem odd that we add dirs, roots, and parents, and then | 696 # It may seem odd that we add dirs, roots, and parents, and then |
628 # restrict to only parents. This is to catch the case of: | 697 # restrict to only parents. This is to catch the case of: |
629 # dirs = ['foo/bar'] | 698 # dirs = ['foo/bar'] |
630 # parents = ['foo'] | 699 # parents = ['foo'] |
631 # if we asked for the children of 'foo', but had only added | 700 # if we asked for the children of 'foo', but had only added |
632 # self._parents, we wouldn't be able to respond ['bar']. | 701 # self._parents, we wouldn't be able to respond ['bar']. |
633 return _dirchildren( | 702 return _dirchildren( |
634 itertools.chain(self._dirs, self._roots, self._parents), | 703 itertools.chain(self._dirs, self._roots, self._parents), |
635 onlyinclude=self._parents) | 704 onlyinclude=self._parents, |
705 ) | |
636 | 706 |
637 def visitchildrenset(self, dir): | 707 def visitchildrenset(self, dir): |
638 if self._prefix and dir in self._roots: | 708 if self._prefix and dir in self._roots: |
639 return 'all' | 709 return 'all' |
640 # Note: this does *not* include the 'dir in self._parents' case from | 710 # Note: this does *not* include the 'dir in self._parents' case from |
641 # visitdir, that's handled below. | 711 # visitdir, that's handled below. |
642 if ('' in self._roots or | 712 if ( |
643 dir in self._roots or | 713 '' in self._roots |
644 dir in self._dirs or | 714 or dir in self._roots |
645 any(parentdir in self._roots | 715 or dir in self._dirs |
646 for parentdir in util.finddirs(dir))): | 716 or any(parentdir in self._roots for parentdir in util.finddirs(dir)) |
717 ): | |
647 return 'this' | 718 return 'this' |
648 | 719 |
649 if dir in self._parents: | 720 if dir in self._parents: |
650 return self._allparentschildren.get(dir) or set() | 721 return self._allparentschildren.get(dir) or set() |
651 return set() | 722 return set() |
652 | 723 |
653 @encoding.strmethod | 724 @encoding.strmethod |
654 def __repr__(self): | 725 def __repr__(self): |
655 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats)) | 726 return '<includematcher includes=%r>' % pycompat.bytestr(self._pats) |
727 | |
656 | 728 |
657 class exactmatcher(basematcher): | 729 class exactmatcher(basematcher): |
658 r'''Matches the input files exactly. They are interpreted as paths, not | 730 r'''Matches the input files exactly. They are interpreted as paths, not |
659 patterns (so no kind-prefixes). | 731 patterns (so no kind-prefixes). |
660 | 732 |
700 return set() | 772 return set() |
701 | 773 |
702 candidates = self._fileset | self._dirs - {''} | 774 candidates = self._fileset | self._dirs - {''} |
703 if dir != '': | 775 if dir != '': |
704 d = dir + '/' | 776 d = dir + '/' |
705 candidates = set(c[len(d):] for c in candidates if | 777 candidates = set(c[len(d) :] for c in candidates if c.startswith(d)) |
706 c.startswith(d)) | |
707 # self._dirs includes all of the directories, recursively, so if | 778 # self._dirs includes all of the directories, recursively, so if |
708 # we're attempting to match foo/bar/baz.txt, it'll have '', 'foo', | 779 # we're attempting to match foo/bar/baz.txt, it'll have '', 'foo', |
709 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a | 780 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a |
710 # '/' in it, indicating a it's for a subdir-of-a-subdir; the | 781 # '/' in it, indicating a it's for a subdir-of-a-subdir; the |
711 # immediate subdir will be in there without a slash. | 782 # immediate subdir will be in there without a slash. |
718 def isexact(self): | 789 def isexact(self): |
719 return True | 790 return True |
720 | 791 |
721 @encoding.strmethod | 792 @encoding.strmethod |
722 def __repr__(self): | 793 def __repr__(self): |
723 return ('<exactmatcher files=%r>' % self._files) | 794 return '<exactmatcher files=%r>' % self._files |
795 | |
724 | 796 |
725 class differencematcher(basematcher): | 797 class differencematcher(basematcher): |
726 '''Composes two matchers by matching if the first matches and the second | 798 '''Composes two matchers by matching if the first matches and the second |
727 does not. | 799 does not. |
728 | 800 |
729 The second matcher's non-matching-attributes (bad, explicitdir, | 801 The second matcher's non-matching-attributes (bad, explicitdir, |
730 traversedir) are ignored. | 802 traversedir) are ignored. |
731 ''' | 803 ''' |
804 | |
732 def __init__(self, m1, m2): | 805 def __init__(self, m1, m2): |
733 super(differencematcher, self).__init__() | 806 super(differencematcher, self).__init__() |
734 self._m1 = m1 | 807 self._m1 = m1 |
735 self._m2 = m2 | 808 self._m2 = m2 |
736 self.bad = m1.bad | 809 self.bad = m1.bad |
787 def isexact(self): | 860 def isexact(self): |
788 return self._m1.isexact() | 861 return self._m1.isexact() |
789 | 862 |
790 @encoding.strmethod | 863 @encoding.strmethod |
791 def __repr__(self): | 864 def __repr__(self): |
792 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2)) | 865 return '<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2) |
866 | |
793 | 867 |
794 def intersectmatchers(m1, m2): | 868 def intersectmatchers(m1, m2): |
795 '''Composes two matchers by matching if both of them match. | 869 '''Composes two matchers by matching if both of them match. |
796 | 870 |
797 The second matcher's non-matching-attributes (bad, explicitdir, | 871 The second matcher's non-matching-attributes (bad, explicitdir, |
810 if m2.always(): | 884 if m2.always(): |
811 m = copy.copy(m1) | 885 m = copy.copy(m1) |
812 return m | 886 return m |
813 return intersectionmatcher(m1, m2) | 887 return intersectionmatcher(m1, m2) |
814 | 888 |
889 | |
815 class intersectionmatcher(basematcher): | 890 class intersectionmatcher(basematcher): |
816 def __init__(self, m1, m2): | 891 def __init__(self, m1, m2): |
817 super(intersectionmatcher, self).__init__() | 892 super(intersectionmatcher, self).__init__() |
818 self._m1 = m1 | 893 self._m1 = m1 |
819 self._m2 = m2 | 894 self._m2 = m2 |
869 def isexact(self): | 944 def isexact(self): |
870 return self._m1.isexact() or self._m2.isexact() | 945 return self._m1.isexact() or self._m2.isexact() |
871 | 946 |
872 @encoding.strmethod | 947 @encoding.strmethod |
873 def __repr__(self): | 948 def __repr__(self): |
874 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2)) | 949 return '<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2) |
950 | |
875 | 951 |
876 class subdirmatcher(basematcher): | 952 class subdirmatcher(basematcher): |
877 """Adapt a matcher to work on a subdirectory only. | 953 """Adapt a matcher to work on a subdirectory only. |
878 | 954 |
879 The paths are remapped to remove/insert the path as needed: | 955 The paths are remapped to remove/insert the path as needed: |
904 super(subdirmatcher, self).__init__() | 980 super(subdirmatcher, self).__init__() |
905 self._path = path | 981 self._path = path |
906 self._matcher = matcher | 982 self._matcher = matcher |
907 self._always = matcher.always() | 983 self._always = matcher.always() |
908 | 984 |
909 self._files = [f[len(path) + 1:] for f in matcher._files | 985 self._files = [ |
910 if f.startswith(path + "/")] | 986 f[len(path) + 1 :] |
987 for f in matcher._files | |
988 if f.startswith(path + "/") | |
989 ] | |
911 | 990 |
912 # If the parent repo had a path to this subrepo and the matcher is | 991 # If the parent repo had a path to this subrepo and the matcher is |
913 # a prefix matcher, this submatcher always matches. | 992 # a prefix matcher, this submatcher always matches. |
914 if matcher.prefix(): | 993 if matcher.prefix(): |
915 self._always = any(f == path for f in matcher._files) | 994 self._always = any(f == path for f in matcher._files) |
946 def prefix(self): | 1025 def prefix(self): |
947 return self._matcher.prefix() and not self._always | 1026 return self._matcher.prefix() and not self._always |
948 | 1027 |
949 @encoding.strmethod | 1028 @encoding.strmethod |
950 def __repr__(self): | 1029 def __repr__(self): |
951 return ('<subdirmatcher path=%r, matcher=%r>' % | 1030 return '<subdirmatcher path=%r, matcher=%r>' % ( |
952 (self._path, self._matcher)) | 1031 self._path, |
1032 self._matcher, | |
1033 ) | |
1034 | |
953 | 1035 |
954 class prefixdirmatcher(basematcher): | 1036 class prefixdirmatcher(basematcher): |
955 """Adapt a matcher to work on a parent directory. | 1037 """Adapt a matcher to work on a parent directory. |
956 | 1038 |
957 The matcher's non-matching-attributes (bad, explicitdir, traversedir) are | 1039 The matcher's non-matching-attributes (bad, explicitdir, traversedir) are |
997 return [self._pathprefix + f for f in self._matcher._files] | 1079 return [self._pathprefix + f for f in self._matcher._files] |
998 | 1080 |
999 def matchfn(self, f): | 1081 def matchfn(self, f): |
1000 if not f.startswith(self._pathprefix): | 1082 if not f.startswith(self._pathprefix): |
1001 return False | 1083 return False |
1002 return self._matcher.matchfn(f[len(self._pathprefix):]) | 1084 return self._matcher.matchfn(f[len(self._pathprefix) :]) |
1003 | 1085 |
1004 @propertycache | 1086 @propertycache |
1005 def _pathdirs(self): | 1087 def _pathdirs(self): |
1006 return set(util.finddirs(self._path)) | 1088 return set(util.finddirs(self._path)) |
1007 | 1089 |
1008 def visitdir(self, dir): | 1090 def visitdir(self, dir): |
1009 if dir == self._path: | 1091 if dir == self._path: |
1010 return self._matcher.visitdir('') | 1092 return self._matcher.visitdir('') |
1011 if dir.startswith(self._pathprefix): | 1093 if dir.startswith(self._pathprefix): |
1012 return self._matcher.visitdir(dir[len(self._pathprefix):]) | 1094 return self._matcher.visitdir(dir[len(self._pathprefix) :]) |
1013 return dir in self._pathdirs | 1095 return dir in self._pathdirs |
1014 | 1096 |
1015 def visitchildrenset(self, dir): | 1097 def visitchildrenset(self, dir): |
1016 if dir == self._path: | 1098 if dir == self._path: |
1017 return self._matcher.visitchildrenset('') | 1099 return self._matcher.visitchildrenset('') |
1018 if dir.startswith(self._pathprefix): | 1100 if dir.startswith(self._pathprefix): |
1019 return self._matcher.visitchildrenset(dir[len(self._pathprefix):]) | 1101 return self._matcher.visitchildrenset(dir[len(self._pathprefix) :]) |
1020 if dir in self._pathdirs: | 1102 if dir in self._pathdirs: |
1021 return 'this' | 1103 return 'this' |
1022 return set() | 1104 return set() |
1023 | 1105 |
1024 def isexact(self): | 1106 def isexact(self): |
1027 def prefix(self): | 1109 def prefix(self): |
1028 return self._matcher.prefix() | 1110 return self._matcher.prefix() |
1029 | 1111 |
1030 @encoding.strmethod | 1112 @encoding.strmethod |
1031 def __repr__(self): | 1113 def __repr__(self): |
1032 return ('<prefixdirmatcher path=%r, matcher=%r>' | 1114 return '<prefixdirmatcher path=%r, matcher=%r>' % ( |
1033 % (pycompat.bytestr(self._path), self._matcher)) | 1115 pycompat.bytestr(self._path), |
1116 self._matcher, | |
1117 ) | |
1118 | |
1034 | 1119 |
1035 class unionmatcher(basematcher): | 1120 class unionmatcher(basematcher): |
1036 """A matcher that is the union of several matchers. | 1121 """A matcher that is the union of several matchers. |
1037 | 1122 |
1038 The non-matching-attributes (bad, explicitdir, traversedir) are taken from | 1123 The non-matching-attributes (bad, explicitdir, traversedir) are taken from |
1080 return 'this' | 1165 return 'this' |
1081 return r | 1166 return r |
1082 | 1167 |
1083 @encoding.strmethod | 1168 @encoding.strmethod |
1084 def __repr__(self): | 1169 def __repr__(self): |
1085 return ('<unionmatcher matchers=%r>' % self._matchers) | 1170 return '<unionmatcher matchers=%r>' % self._matchers |
1171 | |
1086 | 1172 |
1087 def patkind(pattern, default=None): | 1173 def patkind(pattern, default=None): |
1088 '''If pattern is 'kind:pat' with a known kind, return kind. | 1174 '''If pattern is 'kind:pat' with a known kind, return kind. |
1089 | 1175 |
1090 >>> patkind(br're:.*\.c$') | 1176 >>> patkind(br're:.*\.c$') |
1097 >>> patkind(b'main.py', default=b're') | 1183 >>> patkind(b'main.py', default=b're') |
1098 're' | 1184 're' |
1099 ''' | 1185 ''' |
1100 return _patsplit(pattern, default)[0] | 1186 return _patsplit(pattern, default)[0] |
1101 | 1187 |
1188 | |
1102 def _patsplit(pattern, default): | 1189 def _patsplit(pattern, default): |
1103 """Split a string into the optional pattern kind prefix and the actual | 1190 """Split a string into the optional pattern kind prefix and the actual |
1104 pattern.""" | 1191 pattern.""" |
1105 if ':' in pattern: | 1192 if ':' in pattern: |
1106 kind, pat = pattern.split(':', 1) | 1193 kind, pat = pattern.split(':', 1) |
1107 if kind in allpatternkinds: | 1194 if kind in allpatternkinds: |
1108 return kind, pat | 1195 return kind, pat |
1109 return default, pattern | 1196 return default, pattern |
1197 | |
1110 | 1198 |
1111 def _globre(pat): | 1199 def _globre(pat): |
1112 r'''Convert an extended glob string to a regexp string. | 1200 r'''Convert an extended glob string to a regexp string. |
1113 | 1201 |
1114 >>> from . import pycompat | 1202 >>> from . import pycompat |
1133 ''' | 1221 ''' |
1134 i, n = 0, len(pat) | 1222 i, n = 0, len(pat) |
1135 res = '' | 1223 res = '' |
1136 group = 0 | 1224 group = 0 |
1137 escape = util.stringutil.regexbytesescapemap.get | 1225 escape = util.stringutil.regexbytesescapemap.get |
1226 | |
1138 def peek(): | 1227 def peek(): |
1139 return i < n and pat[i:i + 1] | 1228 return i < n and pat[i : i + 1] |
1229 | |
1140 while i < n: | 1230 while i < n: |
1141 c = pat[i:i + 1] | 1231 c = pat[i : i + 1] |
1142 i += 1 | 1232 i += 1 |
1143 if c not in '*?[{},\\': | 1233 if c not in '*?[{},\\': |
1144 res += escape(c, c) | 1234 res += escape(c, c) |
1145 elif c == '*': | 1235 elif c == '*': |
1146 if peek() == '*': | 1236 if peek() == '*': |
1154 res += '[^/]*' | 1244 res += '[^/]*' |
1155 elif c == '?': | 1245 elif c == '?': |
1156 res += '.' | 1246 res += '.' |
1157 elif c == '[': | 1247 elif c == '[': |
1158 j = i | 1248 j = i |
1159 if j < n and pat[j:j + 1] in '!]': | 1249 if j < n and pat[j : j + 1] in '!]': |
1160 j += 1 | 1250 j += 1 |
1161 while j < n and pat[j:j + 1] != ']': | 1251 while j < n and pat[j : j + 1] != ']': |
1162 j += 1 | 1252 j += 1 |
1163 if j >= n: | 1253 if j >= n: |
1164 res += '\\[' | 1254 res += '\\[' |
1165 else: | 1255 else: |
1166 stuff = pat[i:j].replace('\\','\\\\') | 1256 stuff = pat[i:j].replace('\\', '\\\\') |
1167 i = j + 1 | 1257 i = j + 1 |
1168 if stuff[0:1] == '!': | 1258 if stuff[0:1] == '!': |
1169 stuff = '^' + stuff[1:] | 1259 stuff = '^' + stuff[1:] |
1170 elif stuff[0:1] == '^': | 1260 elif stuff[0:1] == '^': |
1171 stuff = '\\' + stuff | 1261 stuff = '\\' + stuff |
1187 res += escape(c, c) | 1277 res += escape(c, c) |
1188 else: | 1278 else: |
1189 res += escape(c, c) | 1279 res += escape(c, c) |
1190 return res | 1280 return res |
1191 | 1281 |
1282 | |
1192 def _regex(kind, pat, globsuffix): | 1283 def _regex(kind, pat, globsuffix): |
1193 '''Convert a (normalized) pattern of any kind into a | 1284 '''Convert a (normalized) pattern of any kind into a |
1194 regular expression. | 1285 regular expression. |
1195 globsuffix is appended to the regexp of globs.''' | 1286 globsuffix is appended to the regexp of globs.''' |
1196 | 1287 |
1197 if rustmod is not None: | 1288 if rustmod is not None: |
1198 try: | 1289 try: |
1199 return rustmod.build_single_regex( | 1290 return rustmod.build_single_regex(kind, pat, globsuffix) |
1200 kind, | |
1201 pat, | |
1202 globsuffix | |
1203 ) | |
1204 except rustmod.PatternError: | 1291 except rustmod.PatternError: |
1205 raise error.ProgrammingError( | 1292 raise error.ProgrammingError( |
1206 'not a regex pattern: %s:%s' % (kind, pat) | 1293 'not a regex pattern: %s:%s' % (kind, pat) |
1207 ) | 1294 ) |
1208 | 1295 |
1225 if kind == 'relglob': | 1312 if kind == 'relglob': |
1226 globre = _globre(pat) | 1313 globre = _globre(pat) |
1227 if globre.startswith('[^/]*'): | 1314 if globre.startswith('[^/]*'): |
1228 # When pat has the form *XYZ (common), make the returned regex more | 1315 # When pat has the form *XYZ (common), make the returned regex more |
1229 # legible by returning the regex for **XYZ instead of **/*XYZ. | 1316 # legible by returning the regex for **XYZ instead of **/*XYZ. |
1230 return '.*' + globre[len('[^/]*'):] + globsuffix | 1317 return '.*' + globre[len('[^/]*') :] + globsuffix |
1231 return '(?:|.*/)' + globre + globsuffix | 1318 return '(?:|.*/)' + globre + globsuffix |
1232 if kind == 'relre': | 1319 if kind == 'relre': |
1233 if pat.startswith('^'): | 1320 if pat.startswith('^'): |
1234 return pat | 1321 return pat |
1235 return '.*' + pat | 1322 return '.*' + pat |
1236 if kind in ('glob', 'rootglob'): | 1323 if kind in ('glob', 'rootglob'): |
1237 return _globre(pat) + globsuffix | 1324 return _globre(pat) + globsuffix |
1238 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat)) | 1325 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat)) |
1239 | 1326 |
1327 | |
1240 def _buildmatch(kindpats, globsuffix, root): | 1328 def _buildmatch(kindpats, globsuffix, root): |
1241 '''Return regexp string and a matcher function for kindpats. | 1329 '''Return regexp string and a matcher function for kindpats. |
1242 globsuffix is appended to the regexp of globs.''' | 1330 globsuffix is appended to the regexp of globs.''' |
1243 matchfuncs = [] | 1331 matchfuncs = [] |
1244 | 1332 |
1245 subincludes, kindpats = _expandsubinclude(kindpats, root) | 1333 subincludes, kindpats = _expandsubinclude(kindpats, root) |
1246 if subincludes: | 1334 if subincludes: |
1247 submatchers = {} | 1335 submatchers = {} |
1336 | |
1248 def matchsubinclude(f): | 1337 def matchsubinclude(f): |
1249 for prefix, matcherargs in subincludes: | 1338 for prefix, matcherargs in subincludes: |
1250 if f.startswith(prefix): | 1339 if f.startswith(prefix): |
1251 mf = submatchers.get(prefix) | 1340 mf = submatchers.get(prefix) |
1252 if mf is None: | 1341 if mf is None: |
1253 mf = match(*matcherargs) | 1342 mf = match(*matcherargs) |
1254 submatchers[prefix] = mf | 1343 submatchers[prefix] = mf |
1255 | 1344 |
1256 if mf(f[len(prefix):]): | 1345 if mf(f[len(prefix) :]): |
1257 return True | 1346 return True |
1258 return False | 1347 return False |
1348 | |
1259 matchfuncs.append(matchsubinclude) | 1349 matchfuncs.append(matchsubinclude) |
1260 | 1350 |
1261 regex = '' | 1351 regex = '' |
1262 if kindpats: | 1352 if kindpats: |
1263 if all(k == 'rootfilesin' for k, p, s in kindpats): | 1353 if all(k == 'rootfilesin' for k, p, s in kindpats): |
1264 dirs = {p for k, p, s in kindpats} | 1354 dirs = {p for k, p, s in kindpats} |
1355 | |
1265 def mf(f): | 1356 def mf(f): |
1266 i = f.rfind('/') | 1357 i = f.rfind('/') |
1267 if i >= 0: | 1358 if i >= 0: |
1268 dir = f[:i] | 1359 dir = f[:i] |
1269 else: | 1360 else: |
1270 dir = '.' | 1361 dir = '.' |
1271 return dir in dirs | 1362 return dir in dirs |
1363 | |
1272 regex = b'rootfilesin: %s' % stringutil.pprint(list(sorted(dirs))) | 1364 regex = b'rootfilesin: %s' % stringutil.pprint(list(sorted(dirs))) |
1273 matchfuncs.append(mf) | 1365 matchfuncs.append(mf) |
1274 else: | 1366 else: |
1275 regex, mf = _buildregexmatch(kindpats, globsuffix) | 1367 regex, mf = _buildregexmatch(kindpats, globsuffix) |
1276 matchfuncs.append(mf) | 1368 matchfuncs.append(mf) |
1278 if len(matchfuncs) == 1: | 1370 if len(matchfuncs) == 1: |
1279 return regex, matchfuncs[0] | 1371 return regex, matchfuncs[0] |
1280 else: | 1372 else: |
1281 return regex, lambda f: any(mf(f) for mf in matchfuncs) | 1373 return regex, lambda f: any(mf(f) for mf in matchfuncs) |
1282 | 1374 |
1375 | |
1283 MAX_RE_SIZE = 20000 | 1376 MAX_RE_SIZE = 20000 |
1377 | |
1284 | 1378 |
1285 def _joinregexes(regexps): | 1379 def _joinregexes(regexps): |
1286 """gather multiple regular expressions into a single one""" | 1380 """gather multiple regular expressions into a single one""" |
1287 return '|'.join(regexps) | 1381 return '|'.join(regexps) |
1382 | |
1288 | 1383 |
1289 def _buildregexmatch(kindpats, globsuffix): | 1384 def _buildregexmatch(kindpats, globsuffix): |
1290 """Build a match function from a list of kinds and kindpats, | 1385 """Build a match function from a list of kinds and kindpats, |
1291 return regexp string and a matcher function. | 1386 return regexp string and a matcher function. |
1292 | 1387 |
1330 for k, p, s in kindpats: | 1425 for k, p, s in kindpats: |
1331 try: | 1426 try: |
1332 _rematcher(_regex(k, p, globsuffix)) | 1427 _rematcher(_regex(k, p, globsuffix)) |
1333 except re.error: | 1428 except re.error: |
1334 if s: | 1429 if s: |
1335 raise error.Abort(_("%s: invalid pattern (%s): %s") % | 1430 raise error.Abort( |
1336 (s, k, p)) | 1431 _("%s: invalid pattern (%s): %s") % (s, k, p) |
1432 ) | |
1337 else: | 1433 else: |
1338 raise error.Abort(_("invalid pattern (%s): %s") % (k, p)) | 1434 raise error.Abort(_("invalid pattern (%s): %s") % (k, p)) |
1339 raise error.Abort(_("invalid pattern")) | 1435 raise error.Abort(_("invalid pattern")) |
1436 | |
1340 | 1437 |
1341 def _patternrootsanddirs(kindpats): | 1438 def _patternrootsanddirs(kindpats): |
1342 '''Returns roots and directories corresponding to each pattern. | 1439 '''Returns roots and directories corresponding to each pattern. |
1343 | 1440 |
1344 This calculates the roots and directories exactly matching the patterns and | 1441 This calculates the roots and directories exactly matching the patterns and |
1347 directories. | 1444 directories. |
1348 ''' | 1445 ''' |
1349 r = [] | 1446 r = [] |
1350 d = [] | 1447 d = [] |
1351 for kind, pat, source in kindpats: | 1448 for kind, pat, source in kindpats: |
1352 if kind in ('glob', 'rootglob'): # find the non-glob prefix | 1449 if kind in ('glob', 'rootglob'): # find the non-glob prefix |
1353 root = [] | 1450 root = [] |
1354 for p in pat.split('/'): | 1451 for p in pat.split('/'): |
1355 if '[' in p or '{' in p or '*' in p or '?' in p: | 1452 if '[' in p or '{' in p or '*' in p or '?' in p: |
1356 break | 1453 break |
1357 root.append(p) | 1454 root.append(p) |
1362 r.append(pat) | 1459 r.append(pat) |
1363 elif kind in ('rootfilesin',): | 1460 elif kind in ('rootfilesin',): |
1364 if pat == '.': | 1461 if pat == '.': |
1365 pat = '' | 1462 pat = '' |
1366 d.append(pat) | 1463 d.append(pat) |
1367 else: # relglob, re, relre | 1464 else: # relglob, re, relre |
1368 r.append('') | 1465 r.append('') |
1369 return r, d | 1466 return r, d |
1467 | |
1370 | 1468 |
1371 def _roots(kindpats): | 1469 def _roots(kindpats): |
1372 '''Returns root directories to match recursively from the given patterns.''' | 1470 '''Returns root directories to match recursively from the given patterns.''' |
1373 roots, dirs = _patternrootsanddirs(kindpats) | 1471 roots, dirs = _patternrootsanddirs(kindpats) |
1374 return roots | 1472 return roots |
1473 | |
1375 | 1474 |
1376 def _rootsdirsandparents(kindpats): | 1475 def _rootsdirsandparents(kindpats): |
1377 '''Returns roots and exact directories from patterns. | 1476 '''Returns roots and exact directories from patterns. |
1378 | 1477 |
1379 `roots` are directories to match recursively, `dirs` should | 1478 `roots` are directories to match recursively, `dirs` should |
1414 # returning. | 1513 # returning. |
1415 # FIXME: all uses of this function do not need anything in 'roots' and | 1514 # FIXME: all uses of this function do not need anything in 'roots' and |
1416 # 'dirs' to also be in 'parents', consider removing them before returning. | 1515 # 'dirs' to also be in 'parents', consider removing them before returning. |
1417 return r, d, p | 1516 return r, d, p |
1418 | 1517 |
1518 | |
1419 def _explicitfiles(kindpats): | 1519 def _explicitfiles(kindpats): |
1420 '''Returns the potential explicit filenames from the patterns. | 1520 '''Returns the potential explicit filenames from the patterns. |
1421 | 1521 |
1422 >>> _explicitfiles([(b'path', b'foo/bar', b'')]) | 1522 >>> _explicitfiles([(b'path', b'foo/bar', b'')]) |
1423 ['foo/bar'] | 1523 ['foo/bar'] |
1427 # Keep only the pattern kinds where one can specify filenames (vs only | 1527 # Keep only the pattern kinds where one can specify filenames (vs only |
1428 # directory names). | 1528 # directory names). |
1429 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)] | 1529 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)] |
1430 return _roots(filable) | 1530 return _roots(filable) |
1431 | 1531 |
1532 | |
1432 def _prefix(kindpats): | 1533 def _prefix(kindpats): |
1433 '''Whether all the patterns match a prefix (i.e. recursively)''' | 1534 '''Whether all the patterns match a prefix (i.e. recursively)''' |
1434 for kind, pat, source in kindpats: | 1535 for kind, pat, source in kindpats: |
1435 if kind not in ('path', 'relpath'): | 1536 if kind not in ('path', 'relpath'): |
1436 return False | 1537 return False |
1437 return True | 1538 return True |
1438 | 1539 |
1540 | |
1439 _commentre = None | 1541 _commentre = None |
1542 | |
1440 | 1543 |
1441 def readpatternfile(filepath, warn, sourceinfo=False): | 1544 def readpatternfile(filepath, warn, sourceinfo=False): |
1442 '''parse a pattern file, returning a list of | 1545 '''parse a pattern file, returning a list of |
1443 patterns. These patterns should be given to compile() | 1546 patterns. These patterns should be given to compile() |
1444 to be validated and converted into a match function. | 1547 to be validated and converted into a match function. |
1462 This is useful to debug ignore patterns. | 1565 This is useful to debug ignore patterns. |
1463 ''' | 1566 ''' |
1464 | 1567 |
1465 if rustmod is not None: | 1568 if rustmod is not None: |
1466 result, warnings = rustmod.read_pattern_file( | 1569 result, warnings = rustmod.read_pattern_file( |
1467 filepath, | 1570 filepath, bool(warn), sourceinfo, |
1468 bool(warn), | |
1469 sourceinfo, | |
1470 ) | 1571 ) |
1471 | 1572 |
1472 for warning_params in warnings: | 1573 for warning_params in warnings: |
1473 # Can't be easily emitted from Rust, because it would require | 1574 # Can't be easily emitted from Rust, because it would require |
1474 # a mechanism for both gettext and calling the `warn` function. | 1575 # a mechanism for both gettext and calling the `warn` function. |
1494 if not _commentre: | 1595 if not _commentre: |
1495 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*') | 1596 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*') |
1496 # remove comments prefixed by an even number of escapes | 1597 # remove comments prefixed by an even number of escapes |
1497 m = _commentre.search(line) | 1598 m = _commentre.search(line) |
1498 if m: | 1599 if m: |
1499 line = line[:m.end(1)] | 1600 line = line[: m.end(1)] |
1500 # fixup properly escaped comments that survived the above | 1601 # fixup properly escaped comments that survived the above |
1501 line = line.replace("\\#", "#") | 1602 line = line.replace("\\#", "#") |
1502 line = line.rstrip() | 1603 line = line.rstrip() |
1503 if not line: | 1604 if not line: |
1504 continue | 1605 continue |
1507 s = line[7:].strip() | 1608 s = line[7:].strip() |
1508 try: | 1609 try: |
1509 syntax = syntaxes[s] | 1610 syntax = syntaxes[s] |
1510 except KeyError: | 1611 except KeyError: |
1511 if warn: | 1612 if warn: |
1512 warn(_("%s: ignoring invalid syntax '%s'\n") % | 1613 warn( |
1513 (filepath, s)) | 1614 _("%s: ignoring invalid syntax '%s'\n") % (filepath, s) |
1615 ) | |
1514 continue | 1616 continue |
1515 | 1617 |
1516 linesyntax = syntax | 1618 linesyntax = syntax |
1517 for s, rels in syntaxes.iteritems(): | 1619 for s, rels in syntaxes.iteritems(): |
1518 if line.startswith(rels): | 1620 if line.startswith(rels): |
1519 linesyntax = rels | 1621 linesyntax = rels |
1520 line = line[len(rels):] | 1622 line = line[len(rels) :] |
1521 break | 1623 break |
1522 elif line.startswith(s+':'): | 1624 elif line.startswith(s + ':'): |
1523 linesyntax = rels | 1625 linesyntax = rels |
1524 line = line[len(s) + 1:] | 1626 line = line[len(s) + 1 :] |
1525 break | 1627 break |
1526 if sourceinfo: | 1628 if sourceinfo: |
1527 patterns.append((linesyntax + line, lineno, line)) | 1629 patterns.append((linesyntax + line, lineno, line)) |
1528 else: | 1630 else: |
1529 patterns.append(linesyntax + line) | 1631 patterns.append(linesyntax + line) |