##// END OF EJS Templates
match: remove unnecessary '^' from regexes...
Martin von Zweigbergk -
r33357:a21819f4 default
parent child Browse files
Show More
@@ -1,1036 +1,1036
1 1 # match.py - filename matching
2 2 #
3 3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import copy
11 11 import os
12 12 import re
13 13
14 14 from .i18n import _
15 15 from . import (
16 16 error,
17 17 pathutil,
18 18 util,
19 19 )
20 20
21 21 propertycache = util.propertycache
22 22
23 23 def _rematcher(regex):
24 24 '''compile the regexp with the best available regexp engine and return a
25 25 matcher function'''
26 26 m = util.re.compile(regex)
27 27 try:
28 28 # slightly faster, provided by facebook's re2 bindings
29 29 return m.test_match
30 30 except AttributeError:
31 31 return m.match
32 32
33 33 def _expandsets(kindpats, ctx, listsubrepos):
34 34 '''Returns the kindpats list with the 'set' patterns expanded.'''
35 35 fset = set()
36 36 other = []
37 37
38 38 for kind, pat, source in kindpats:
39 39 if kind == 'set':
40 40 if not ctx:
41 41 raise error.ProgrammingError("fileset expression with no "
42 42 "context")
43 43 s = ctx.getfileset(pat)
44 44 fset.update(s)
45 45
46 46 if listsubrepos:
47 47 for subpath in ctx.substate:
48 48 s = ctx.sub(subpath).getfileset(pat)
49 49 fset.update(subpath + '/' + f for f in s)
50 50
51 51 continue
52 52 other.append((kind, pat, source))
53 53 return fset, other
54 54
55 55 def _expandsubinclude(kindpats, root):
56 56 '''Returns the list of subinclude matcher args and the kindpats without the
57 57 subincludes in it.'''
58 58 relmatchers = []
59 59 other = []
60 60
61 61 for kind, pat, source in kindpats:
62 62 if kind == 'subinclude':
63 63 sourceroot = pathutil.dirname(util.normpath(source))
64 64 pat = util.pconvert(pat)
65 65 path = pathutil.join(sourceroot, pat)
66 66
67 67 newroot = pathutil.dirname(path)
68 68 matcherargs = (newroot, '', [], ['include:%s' % path])
69 69
70 70 prefix = pathutil.canonpath(root, root, newroot)
71 71 if prefix:
72 72 prefix += '/'
73 73 relmatchers.append((prefix, matcherargs))
74 74 else:
75 75 other.append((kind, pat, source))
76 76
77 77 return relmatchers, other
78 78
79 79 def _kindpatsalwaysmatch(kindpats):
80 80 """"Checks whether the kindspats match everything, as e.g.
81 81 'relpath:.' does.
82 82 """
83 83 for kind, pat, source in kindpats:
84 84 if pat != '' or kind not in ['relpath', 'glob']:
85 85 return False
86 86 return True
87 87
88 88 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
89 89 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
90 90 badfn=None, icasefs=False):
91 91 """build an object to match a set of file patterns
92 92
93 93 arguments:
94 94 root - the canonical root of the tree you're matching against
95 95 cwd - the current working directory, if relevant
96 96 patterns - patterns to find
97 97 include - patterns to include (unless they are excluded)
98 98 exclude - patterns to exclude (even if they are included)
99 99 default - if a pattern in patterns has no explicit type, assume this one
100 100 exact - patterns are actually filenames (include/exclude still apply)
101 101 warn - optional function used for printing warnings
102 102 badfn - optional bad() callback for this matcher instead of the default
103 103 icasefs - make a matcher for wdir on case insensitive filesystems, which
104 104 normalizes the given patterns to the case in the filesystem
105 105
106 106 a pattern is one of:
107 107 'glob:<glob>' - a glob relative to cwd
108 108 're:<regexp>' - a regular expression
109 109 'path:<path>' - a path relative to repository root, which is matched
110 110 recursively
111 111 'rootfilesin:<path>' - a path relative to repository root, which is
112 112 matched non-recursively (will not match subdirectories)
113 113 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
114 114 'relpath:<path>' - a path relative to cwd
115 115 'relre:<regexp>' - a regexp that needn't match the start of a name
116 116 'set:<fileset>' - a fileset expression
117 117 'include:<path>' - a file of patterns to read and include
118 118 'subinclude:<path>' - a file of patterns to match against files under
119 119 the same directory
120 120 '<something>' - a pattern of the specified default type
121 121 """
122 122 normalize = _donormalize
123 123 if icasefs:
124 124 if exact:
125 125 raise error.ProgrammingError("a case-insensitive exact matcher "
126 126 "doesn't make sense")
127 127 dirstate = ctx.repo().dirstate
128 128 dsnormalize = dirstate.normalize
129 129
130 130 def normalize(patterns, default, root, cwd, auditor, warn):
131 131 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
132 132 kindpats = []
133 133 for kind, pats, source in kp:
134 134 if kind not in ('re', 'relre'): # regex can't be normalized
135 135 p = pats
136 136 pats = dsnormalize(pats)
137 137
138 138 # Preserve the original to handle a case only rename.
139 139 if p != pats and p in dirstate:
140 140 kindpats.append((kind, p, source))
141 141
142 142 kindpats.append((kind, pats, source))
143 143 return kindpats
144 144
145 145 if exact:
146 146 m = exactmatcher(root, cwd, patterns, badfn)
147 147 elif patterns:
148 148 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
149 149 if _kindpatsalwaysmatch(kindpats):
150 150 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
151 151 else:
152 152 m = patternmatcher(root, cwd, kindpats, ctx=ctx,
153 153 listsubrepos=listsubrepos, badfn=badfn)
154 154 else:
155 155 # It's a little strange that no patterns means to match everything.
156 156 # Consider changing this to match nothing (probably using nevermatcher).
157 157 m = alwaysmatcher(root, cwd, badfn)
158 158
159 159 if include:
160 160 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
161 161 im = includematcher(root, cwd, kindpats, ctx=ctx,
162 162 listsubrepos=listsubrepos, badfn=None)
163 163 m = intersectmatchers(m, im)
164 164 if exclude:
165 165 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
166 166 em = includematcher(root, cwd, kindpats, ctx=ctx,
167 167 listsubrepos=listsubrepos, badfn=None)
168 168 m = differencematcher(m, em)
169 169 return m
170 170
171 171 def exact(root, cwd, files, badfn=None):
172 172 return exactmatcher(root, cwd, files, badfn=badfn)
173 173
174 174 def always(root, cwd):
175 175 return alwaysmatcher(root, cwd)
176 176
177 177 def never(root, cwd):
178 178 return nevermatcher(root, cwd)
179 179
180 180 def badmatch(match, badfn):
181 181 """Make a copy of the given matcher, replacing its bad method with the given
182 182 one.
183 183 """
184 184 m = copy.copy(match)
185 185 m.bad = badfn
186 186 return m
187 187
188 188 def _donormalize(patterns, default, root, cwd, auditor, warn):
189 189 '''Convert 'kind:pat' from the patterns list to tuples with kind and
190 190 normalized and rooted patterns and with listfiles expanded.'''
191 191 kindpats = []
192 192 for kind, pat in [_patsplit(p, default) for p in patterns]:
193 193 if kind in ('glob', 'relpath'):
194 194 pat = pathutil.canonpath(root, cwd, pat, auditor)
195 195 elif kind in ('relglob', 'path', 'rootfilesin'):
196 196 pat = util.normpath(pat)
197 197 elif kind in ('listfile', 'listfile0'):
198 198 try:
199 199 files = util.readfile(pat)
200 200 if kind == 'listfile0':
201 201 files = files.split('\0')
202 202 else:
203 203 files = files.splitlines()
204 204 files = [f for f in files if f]
205 205 except EnvironmentError:
206 206 raise error.Abort(_("unable to read file list (%s)") % pat)
207 207 for k, p, source in _donormalize(files, default, root, cwd,
208 208 auditor, warn):
209 209 kindpats.append((k, p, pat))
210 210 continue
211 211 elif kind == 'include':
212 212 try:
213 213 fullpath = os.path.join(root, util.localpath(pat))
214 214 includepats = readpatternfile(fullpath, warn)
215 215 for k, p, source in _donormalize(includepats, default,
216 216 root, cwd, auditor, warn):
217 217 kindpats.append((k, p, source or pat))
218 218 except error.Abort as inst:
219 219 raise error.Abort('%s: %s' % (pat, inst[0]))
220 220 except IOError as inst:
221 221 if warn:
222 222 warn(_("skipping unreadable pattern file '%s': %s\n") %
223 223 (pat, inst.strerror))
224 224 continue
225 225 # else: re or relre - which cannot be normalized
226 226 kindpats.append((kind, pat, ''))
227 227 return kindpats
228 228
229 229 class basematcher(object):
230 230
231 231 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
232 232 self._root = root
233 233 self._cwd = cwd
234 234 if badfn is not None:
235 235 self.bad = badfn
236 236 self._relativeuipath = relativeuipath
237 237
238 238 def __call__(self, fn):
239 239 return self.matchfn(fn)
240 240 def __iter__(self):
241 241 for f in self._files:
242 242 yield f
243 243 # Callbacks related to how the matcher is used by dirstate.walk.
244 244 # Subscribers to these events must monkeypatch the matcher object.
245 245 def bad(self, f, msg):
246 246 '''Callback from dirstate.walk for each explicit file that can't be
247 247 found/accessed, with an error message.'''
248 248 pass
249 249
250 250 # If an explicitdir is set, it will be called when an explicitly listed
251 251 # directory is visited.
252 252 explicitdir = None
253 253
254 254 # If an traversedir is set, it will be called when a directory discovered
255 255 # by recursive traversal is visited.
256 256 traversedir = None
257 257
258 258 def abs(self, f):
259 259 '''Convert a repo path back to path that is relative to the root of the
260 260 matcher.'''
261 261 return f
262 262
263 263 def rel(self, f):
264 264 '''Convert repo path back to path that is relative to cwd of matcher.'''
265 265 return util.pathto(self._root, self._cwd, f)
266 266
267 267 def uipath(self, f):
268 268 '''Convert repo path to a display path. If patterns or -I/-X were used
269 269 to create this matcher, the display path will be relative to cwd.
270 270 Otherwise it is relative to the root of the repo.'''
271 271 return (self._relativeuipath and self.rel(f)) or self.abs(f)
272 272
273 273 @propertycache
274 274 def _files(self):
275 275 return []
276 276
277 277 def files(self):
278 278 '''Explicitly listed files or patterns or roots:
279 279 if no patterns or .always(): empty list,
280 280 if exact: list exact files,
281 281 if not .anypats(): list all files and dirs,
282 282 else: optimal roots'''
283 283 return self._files
284 284
285 285 @propertycache
286 286 def _fileset(self):
287 287 return set(self._files)
288 288
289 289 def exact(self, f):
290 290 '''Returns True if f is in .files().'''
291 291 return f in self._fileset
292 292
293 293 def matchfn(self, f):
294 294 return False
295 295
296 296 def visitdir(self, dir):
297 297 '''Decides whether a directory should be visited based on whether it
298 298 has potential matches in it or one of its subdirectories. This is
299 299 based on the match's primary, included, and excluded patterns.
300 300
301 301 Returns the string 'all' if the given directory and all subdirectories
302 302 should be visited. Otherwise returns True or False indicating whether
303 303 the given directory should be visited.
304 304
305 305 This function's behavior is undefined if it has returned False for
306 306 one of the dir's parent directories.
307 307 '''
308 308 return False
309 309
310 310 def anypats(self):
311 311 '''Matcher uses patterns or include/exclude.'''
312 312 return False
313 313
314 314 def always(self):
315 315 '''Matcher will match everything and .files() will be empty
316 316 - optimization might be possible and necessary.'''
317 317 return False
318 318
319 319 def isexact(self):
320 320 return False
321 321
322 322 def prefix(self):
323 323 return not self.always() and not self.isexact() and not self.anypats()
324 324
325 325 class alwaysmatcher(basematcher):
326 326 '''Matches everything.'''
327 327
328 328 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
329 329 super(alwaysmatcher, self).__init__(root, cwd, badfn,
330 330 relativeuipath=relativeuipath)
331 331
332 332 def always(self):
333 333 return True
334 334
335 335 def matchfn(self, f):
336 336 return True
337 337
338 338 def visitdir(self, dir):
339 339 return 'all'
340 340
341 341 def __repr__(self):
342 342 return '<alwaysmatcher>'
343 343
344 344 class nevermatcher(basematcher):
345 345 '''Matches nothing.'''
346 346
347 347 def __init__(self, root, cwd, badfn=None):
348 348 super(nevermatcher, self).__init__(root, cwd, badfn)
349 349
350 350 def __repr__(self):
351 351 return '<nevermatcher>'
352 352
353 353 class patternmatcher(basematcher):
354 354
355 355 def __init__(self, root, cwd, kindpats, ctx=None, listsubrepos=False,
356 356 badfn=None):
357 357 super(patternmatcher, self).__init__(root, cwd, badfn)
358 358
359 359 self._files = _explicitfiles(kindpats)
360 360 self._anypats = _anypats(kindpats)
361 361 self._pats, self.matchfn = _buildmatch(ctx, kindpats, '$', listsubrepos,
362 362 root)
363 363
364 364 @propertycache
365 365 def _dirs(self):
366 366 return set(util.dirs(self._fileset)) | {'.'}
367 367
368 368 def visitdir(self, dir):
369 369 if self.prefix() and dir in self._fileset:
370 370 return 'all'
371 371 return ('.' in self._fileset or
372 372 dir in self._fileset or
373 373 dir in self._dirs or
374 374 any(parentdir in self._fileset
375 375 for parentdir in util.finddirs(dir)))
376 376
377 377 def anypats(self):
378 378 return self._anypats
379 379
380 380 def __repr__(self):
381 381 return ('<patternmatcher patterns=%r>' % self._pats)
382 382
383 383 class includematcher(basematcher):
384 384
385 385 def __init__(self, root, cwd, kindpats, ctx=None, listsubrepos=False,
386 386 badfn=None):
387 387 super(includematcher, self).__init__(root, cwd, badfn)
388 388
389 389 self._pats, self.matchfn = _buildmatch(ctx, kindpats, '(?:/|$)',
390 390 listsubrepos, root)
391 391 self._anypats = _anypats(kindpats)
392 392 roots, dirs = _rootsanddirs(kindpats)
393 393 # roots are directories which are recursively included.
394 394 self._roots = set(roots)
395 395 # dirs are directories which are non-recursively included.
396 396 self._dirs = set(dirs)
397 397
398 398 def visitdir(self, dir):
399 399 if not self._anypats and dir in self._roots:
400 400 # The condition above is essentially self.prefix() for includes
401 401 return 'all'
402 402 return ('.' in self._roots or
403 403 dir in self._roots or
404 404 dir in self._dirs or
405 405 any(parentdir in self._roots
406 406 for parentdir in util.finddirs(dir)))
407 407
408 408 def anypats(self):
409 409 return True
410 410
411 411 def __repr__(self):
412 412 return ('<includematcher includes=%r>' % self._pats)
413 413
414 414 class exactmatcher(basematcher):
415 415 '''Matches the input files exactly. They are interpreted as paths, not
416 416 patterns (so no kind-prefixes).
417 417 '''
418 418
419 419 def __init__(self, root, cwd, files, badfn=None):
420 420 super(exactmatcher, self).__init__(root, cwd, badfn)
421 421
422 422 if isinstance(files, list):
423 423 self._files = files
424 424 else:
425 425 self._files = list(files)
426 426
427 427 matchfn = basematcher.exact
428 428
429 429 @propertycache
430 430 def _dirs(self):
431 431 return set(util.dirs(self._fileset)) | {'.'}
432 432
433 433 def visitdir(self, dir):
434 434 return dir in self._dirs
435 435
436 436 def isexact(self):
437 437 return True
438 438
439 439 def __repr__(self):
440 440 return ('<exactmatcher files=%r>' % self._files)
441 441
442 442 class differencematcher(basematcher):
443 443 '''Composes two matchers by matching if the first matches and the second
444 444 does not. Well, almost... If the user provides a pattern like "-X foo foo",
445 445 Mercurial actually does match "foo" against that. That's because exact
446 446 matches are treated specially. So, since this differencematcher is used for
447 447 excludes, it needs to special-case exact matching.
448 448
449 449 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
450 450 traversedir) are ignored.
451 451
452 452 TODO: If we want to keep the behavior described above for exact matches, we
453 453 should consider instead treating the above case something like this:
454 454 union(exact(foo), difference(pattern(foo), include(foo)))
455 455 '''
456 456 def __init__(self, m1, m2):
457 457 super(differencematcher, self).__init__(m1._root, m1._cwd)
458 458 self._m1 = m1
459 459 self._m2 = m2
460 460 self.bad = m1.bad
461 461 self.explicitdir = m1.explicitdir
462 462 self.traversedir = m1.traversedir
463 463
464 464 def matchfn(self, f):
465 465 return self._m1(f) and (not self._m2(f) or self._m1.exact(f))
466 466
467 467 @propertycache
468 468 def _files(self):
469 469 if self.isexact():
470 470 return [f for f in self._m1.files() if self(f)]
471 471 # If m1 is not an exact matcher, we can't easily figure out the set of
472 472 # files, because its files() are not always files. For example, if
473 473 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
474 474 # want to remove "dir" from the set even though it would match m2,
475 475 # because the "dir" in m1 may not be a file.
476 476 return self._m1.files()
477 477
478 478 def visitdir(self, dir):
479 479 if self._m2.visitdir(dir) == 'all':
480 480 # There's a bug here: If m1 matches file 'dir/file' and m2 excludes
481 481 # 'dir' (recursively), we should still visit 'dir' due to the
482 482 # exception we have for exact matches.
483 483 return False
484 484 return bool(self._m1.visitdir(dir))
485 485
486 486 def isexact(self):
487 487 return self._m1.isexact()
488 488
489 489 def anypats(self):
490 490 return self._m1.anypats() or self._m2.anypats()
491 491
492 492 def __repr__(self):
493 493 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
494 494
495 495 def intersectmatchers(m1, m2):
496 496 '''Composes two matchers by matching if both of them match.
497 497
498 498 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
499 499 traversedir) are ignored.
500 500 '''
501 501 if m1 is None or m2 is None:
502 502 return m1 or m2
503 503 if m1.always():
504 504 m = copy.copy(m2)
505 505 # TODO: Consider encapsulating these things in a class so there's only
506 506 # one thing to copy from m1.
507 507 m.bad = m1.bad
508 508 m.explicitdir = m1.explicitdir
509 509 m.traversedir = m1.traversedir
510 510 m.abs = m1.abs
511 511 m.rel = m1.rel
512 512 m._relativeuipath |= m1._relativeuipath
513 513 return m
514 514 if m2.always():
515 515 m = copy.copy(m1)
516 516 m._relativeuipath |= m2._relativeuipath
517 517 return m
518 518 return intersectionmatcher(m1, m2)
519 519
520 520 class intersectionmatcher(basematcher):
521 521 def __init__(self, m1, m2):
522 522 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
523 523 self._m1 = m1
524 524 self._m2 = m2
525 525 self.bad = m1.bad
526 526 self.explicitdir = m1.explicitdir
527 527 self.traversedir = m1.traversedir
528 528
529 529 @propertycache
530 530 def _files(self):
531 531 if self.isexact():
532 532 m1, m2 = self._m1, self._m2
533 533 if not m1.isexact():
534 534 m1, m2 = m2, m1
535 535 return [f for f in m1.files() if m2(f)]
536 536 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
537 537 # the set of files, because their files() are not always files. For
538 538 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
539 539 # "path:dir2", we don't want to remove "dir2" from the set.
540 540 return self._m1.files() + self._m2.files()
541 541
542 542 def matchfn(self, f):
543 543 return self._m1(f) and self._m2(f)
544 544
545 545 def visitdir(self, dir):
546 546 visit1 = self._m1.visitdir(dir)
547 547 if visit1 == 'all':
548 548 return self._m2.visitdir(dir)
549 549 # bool() because visit1=True + visit2='all' should not be 'all'
550 550 return bool(visit1 and self._m2.visitdir(dir))
551 551
552 552 def always(self):
553 553 return self._m1.always() and self._m2.always()
554 554
555 555 def isexact(self):
556 556 return self._m1.isexact() or self._m2.isexact()
557 557
558 558 def anypats(self):
559 559 return self._m1.anypats() or self._m2.anypats()
560 560
561 561 def __repr__(self):
562 562 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
563 563
564 564 class subdirmatcher(basematcher):
565 565 """Adapt a matcher to work on a subdirectory only.
566 566
567 567 The paths are remapped to remove/insert the path as needed:
568 568
569 569 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
570 570 >>> m2 = subdirmatcher('sub', m1)
571 571 >>> bool(m2('a.txt'))
572 572 False
573 573 >>> bool(m2('b.txt'))
574 574 True
575 575 >>> bool(m2.matchfn('a.txt'))
576 576 False
577 577 >>> bool(m2.matchfn('b.txt'))
578 578 True
579 579 >>> m2.files()
580 580 ['b.txt']
581 581 >>> m2.exact('b.txt')
582 582 True
583 583 >>> util.pconvert(m2.rel('b.txt'))
584 584 'sub/b.txt'
585 585 >>> def bad(f, msg):
586 586 ... print "%s: %s" % (f, msg)
587 587 >>> m1.bad = bad
588 588 >>> m2.bad('x.txt', 'No such file')
589 589 sub/x.txt: No such file
590 590 >>> m2.abs('c.txt')
591 591 'sub/c.txt'
592 592 """
593 593
594 594 def __init__(self, path, matcher):
595 595 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
596 596 self._path = path
597 597 self._matcher = matcher
598 598 self._always = matcher.always()
599 599
600 600 self._files = [f[len(path) + 1:] for f in matcher._files
601 601 if f.startswith(path + "/")]
602 602
603 603 # If the parent repo had a path to this subrepo and the matcher is
604 604 # a prefix matcher, this submatcher always matches.
605 605 if matcher.prefix():
606 606 self._always = any(f == path for f in matcher._files)
607 607
608 608 def bad(self, f, msg):
609 609 self._matcher.bad(self._path + "/" + f, msg)
610 610
611 611 def abs(self, f):
612 612 return self._matcher.abs(self._path + "/" + f)
613 613
614 614 def rel(self, f):
615 615 return self._matcher.rel(self._path + "/" + f)
616 616
617 617 def uipath(self, f):
618 618 return self._matcher.uipath(self._path + "/" + f)
619 619
620 620 def matchfn(self, f):
621 621 # Some information is lost in the superclass's constructor, so we
622 622 # can not accurately create the matching function for the subdirectory
623 623 # from the inputs. Instead, we override matchfn() and visitdir() to
624 624 # call the original matcher with the subdirectory path prepended.
625 625 return self._matcher.matchfn(self._path + "/" + f)
626 626
627 627 def visitdir(self, dir):
628 628 if dir == '.':
629 629 dir = self._path
630 630 else:
631 631 dir = self._path + "/" + dir
632 632 return self._matcher.visitdir(dir)
633 633
634 634 def always(self):
635 635 return self._always
636 636
637 637 def anypats(self):
638 638 return self._matcher.anypats()
639 639
640 640 def __repr__(self):
641 641 return ('<subdirmatcher path=%r, matcher=%r>' %
642 642 (self._path, self._matcher))
643 643
644 644 class forceincludematcher(basematcher):
645 645 """A matcher that returns true for any of the forced includes before testing
646 646 against the actual matcher."""
647 647 def __init__(self, matcher, includes):
648 648 self._matcher = matcher
649 649 self._includes = includes
650 650
651 651 def __call__(self, value):
652 652 return value in self._includes or self._matcher(value)
653 653
654 654 def anypats(self):
655 655 return True
656 656
657 657 def prefix(self):
658 658 return False
659 659
660 660 def __repr__(self):
661 661 return ('<forceincludematcher matcher=%r, includes=%r>' %
662 662 (self._matcher, sorted(self._includes)))
663 663
664 664 class unionmatcher(basematcher):
665 665 """A matcher that is the union of several matchers."""
666 666 def __init__(self, matchers):
667 667 self._matchers = matchers
668 668
669 669 def __call__(self, value):
670 670 for match in self._matchers:
671 671 if match(value):
672 672 return True
673 673 return False
674 674
675 675 def anypats(self):
676 676 return True
677 677
678 678 def prefix(self):
679 679 return False
680 680
681 681 def __repr__(self):
682 682 return ('<unionmatcher matchers=%r>' % self._matchers)
683 683
684 684 class negatematcher(basematcher):
685 685 def __init__(self, matcher):
686 686 self._matcher = matcher
687 687
688 688 def __call__(self, value):
689 689 return not self._matcher(value)
690 690
691 691 def anypats(self):
692 692 return True
693 693
694 694 def __repr__(self):
695 695 return ('<negatematcher matcher=%r>' % self._matcher)
696 696
697 697 def patkind(pattern, default=None):
698 698 '''If pattern is 'kind:pat' with a known kind, return kind.'''
699 699 return _patsplit(pattern, default)[0]
700 700
701 701 def _patsplit(pattern, default):
702 702 """Split a string into the optional pattern kind prefix and the actual
703 703 pattern."""
704 704 if ':' in pattern:
705 705 kind, pat = pattern.split(':', 1)
706 706 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
707 707 'listfile', 'listfile0', 'set', 'include', 'subinclude',
708 708 'rootfilesin'):
709 709 return kind, pat
710 710 return default, pattern
711 711
712 712 def _globre(pat):
713 713 r'''Convert an extended glob string to a regexp string.
714 714
715 715 >>> print _globre(r'?')
716 716 .
717 717 >>> print _globre(r'*')
718 718 [^/]*
719 719 >>> print _globre(r'**')
720 720 .*
721 721 >>> print _globre(r'**/a')
722 722 (?:.*/)?a
723 723 >>> print _globre(r'a/**/b')
724 724 a\/(?:.*/)?b
725 725 >>> print _globre(r'[a*?!^][^b][!c]')
726 726 [a*?!^][\^b][^c]
727 727 >>> print _globre(r'{a,b}')
728 728 (?:a|b)
729 729 >>> print _globre(r'.\*\?')
730 730 \.\*\?
731 731 '''
732 732 i, n = 0, len(pat)
733 733 res = ''
734 734 group = 0
735 735 escape = util.re.escape
736 736 def peek():
737 737 return i < n and pat[i:i + 1]
738 738 while i < n:
739 739 c = pat[i:i + 1]
740 740 i += 1
741 741 if c not in '*?[{},\\':
742 742 res += escape(c)
743 743 elif c == '*':
744 744 if peek() == '*':
745 745 i += 1
746 746 if peek() == '/':
747 747 i += 1
748 748 res += '(?:.*/)?'
749 749 else:
750 750 res += '.*'
751 751 else:
752 752 res += '[^/]*'
753 753 elif c == '?':
754 754 res += '.'
755 755 elif c == '[':
756 756 j = i
757 757 if j < n and pat[j:j + 1] in '!]':
758 758 j += 1
759 759 while j < n and pat[j:j + 1] != ']':
760 760 j += 1
761 761 if j >= n:
762 762 res += '\\['
763 763 else:
764 764 stuff = pat[i:j].replace('\\','\\\\')
765 765 i = j + 1
766 766 if stuff[0:1] == '!':
767 767 stuff = '^' + stuff[1:]
768 768 elif stuff[0:1] == '^':
769 769 stuff = '\\' + stuff
770 770 res = '%s[%s]' % (res, stuff)
771 771 elif c == '{':
772 772 group += 1
773 773 res += '(?:'
774 774 elif c == '}' and group:
775 775 res += ')'
776 776 group -= 1
777 777 elif c == ',' and group:
778 778 res += '|'
779 779 elif c == '\\':
780 780 p = peek()
781 781 if p:
782 782 i += 1
783 783 res += escape(p)
784 784 else:
785 785 res += escape(c)
786 786 else:
787 787 res += escape(c)
788 788 return res
789 789
790 790 def _regex(kind, pat, globsuffix):
791 791 '''Convert a (normalized) pattern of any kind into a regular expression.
792 792 globsuffix is appended to the regexp of globs.'''
793 793 if not pat:
794 794 return ''
795 795 if kind == 're':
796 796 return pat
797 797 if kind == 'path':
798 798 if pat == '.':
799 799 return ''
800 return '^' + util.re.escape(pat) + '(?:/|$)'
800 return util.re.escape(pat) + '(?:/|$)'
801 801 if kind == 'rootfilesin':
802 802 if pat == '.':
803 803 escaped = ''
804 804 else:
805 805 # Pattern is a directory name.
806 806 escaped = util.re.escape(pat) + '/'
807 807 # Anything after the pattern must be a non-directory.
808 return '^' + escaped + '[^/]+$'
808 return escaped + '[^/]+$'
809 809 if kind == 'relglob':
810 810 return '(?:|.*/)' + _globre(pat) + globsuffix
811 811 if kind == 'relpath':
812 812 return util.re.escape(pat) + '(?:/|$)'
813 813 if kind == 'relre':
814 814 if pat.startswith('^'):
815 815 return pat
816 816 return '.*' + pat
817 817 return _globre(pat) + globsuffix
818 818
819 819 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
820 820 '''Return regexp string and a matcher function for kindpats.
821 821 globsuffix is appended to the regexp of globs.'''
822 822 matchfuncs = []
823 823
824 824 subincludes, kindpats = _expandsubinclude(kindpats, root)
825 825 if subincludes:
826 826 submatchers = {}
827 827 def matchsubinclude(f):
828 828 for prefix, matcherargs in subincludes:
829 829 if f.startswith(prefix):
830 830 mf = submatchers.get(prefix)
831 831 if mf is None:
832 832 mf = match(*matcherargs)
833 833 submatchers[prefix] = mf
834 834
835 835 if mf(f[len(prefix):]):
836 836 return True
837 837 return False
838 838 matchfuncs.append(matchsubinclude)
839 839
840 840 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
841 841 if fset:
842 842 matchfuncs.append(fset.__contains__)
843 843
844 844 regex = ''
845 845 if kindpats:
846 846 regex, mf = _buildregexmatch(kindpats, globsuffix)
847 847 matchfuncs.append(mf)
848 848
849 849 if len(matchfuncs) == 1:
850 850 return regex, matchfuncs[0]
851 851 else:
852 852 return regex, lambda f: any(mf(f) for mf in matchfuncs)
853 853
854 854 def _buildregexmatch(kindpats, globsuffix):
855 855 """Build a match function from a list of kinds and kindpats,
856 856 return regexp string and a matcher function."""
857 857 try:
858 858 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
859 859 for (k, p, s) in kindpats])
860 860 if len(regex) > 20000:
861 861 raise OverflowError
862 862 return regex, _rematcher(regex)
863 863 except OverflowError:
864 864 # We're using a Python with a tiny regex engine and we
865 865 # made it explode, so we'll divide the pattern list in two
866 866 # until it works
867 867 l = len(kindpats)
868 868 if l < 2:
869 869 raise
870 870 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
871 871 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
872 872 return regex, lambda s: a(s) or b(s)
873 873 except re.error:
874 874 for k, p, s in kindpats:
875 875 try:
876 876 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
877 877 except re.error:
878 878 if s:
879 879 raise error.Abort(_("%s: invalid pattern (%s): %s") %
880 880 (s, k, p))
881 881 else:
882 882 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
883 883 raise error.Abort(_("invalid pattern"))
884 884
885 885 def _patternrootsanddirs(kindpats):
886 886 '''Returns roots and directories corresponding to each pattern.
887 887
888 888 This calculates the roots and directories exactly matching the patterns and
889 889 returns a tuple of (roots, dirs) for each. It does not return other
890 890 directories which may also need to be considered, like the parent
891 891 directories.
892 892 '''
893 893 r = []
894 894 d = []
895 895 for kind, pat, source in kindpats:
896 896 if kind == 'glob': # find the non-glob prefix
897 897 root = []
898 898 for p in pat.split('/'):
899 899 if '[' in p or '{' in p or '*' in p or '?' in p:
900 900 break
901 901 root.append(p)
902 902 r.append('/'.join(root) or '.')
903 903 elif kind in ('relpath', 'path'):
904 904 r.append(pat or '.')
905 905 elif kind in ('rootfilesin',):
906 906 d.append(pat or '.')
907 907 else: # relglob, re, relre
908 908 r.append('.')
909 909 return r, d
910 910
911 911 def _roots(kindpats):
912 912 '''Returns root directories to match recursively from the given patterns.'''
913 913 roots, dirs = _patternrootsanddirs(kindpats)
914 914 return roots
915 915
916 916 def _rootsanddirs(kindpats):
917 917 '''Returns roots and exact directories from patterns.
918 918
919 919 roots are directories to match recursively, whereas exact directories should
920 920 be matched non-recursively. The returned (roots, dirs) tuple will also
921 921 include directories that need to be implicitly considered as either, such as
922 922 parent directories.
923 923
924 924 >>> _rootsanddirs(\
925 925 [('glob', 'g/h/*', ''), ('glob', 'g/h', ''), ('glob', 'g*', '')])
926 926 (['g/h', 'g/h', '.'], ['g', '.'])
927 927 >>> _rootsanddirs(\
928 928 [('rootfilesin', 'g/h', ''), ('rootfilesin', '', '')])
929 929 ([], ['g/h', '.', 'g', '.'])
930 930 >>> _rootsanddirs(\
931 931 [('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
932 932 (['r', 'p/p', '.'], ['p', '.'])
933 933 >>> _rootsanddirs(\
934 934 [('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
935 935 (['.', '.', '.'], ['.'])
936 936 '''
937 937 r, d = _patternrootsanddirs(kindpats)
938 938
939 939 # Append the parents as non-recursive/exact directories, since they must be
940 940 # scanned to get to either the roots or the other exact directories.
941 941 d.extend(util.dirs(d))
942 942 d.extend(util.dirs(r))
943 943 # util.dirs() does not include the root directory, so add it manually
944 944 d.append('.')
945 945
946 946 return r, d
947 947
948 948 def _explicitfiles(kindpats):
949 949 '''Returns the potential explicit filenames from the patterns.
950 950
951 951 >>> _explicitfiles([('path', 'foo/bar', '')])
952 952 ['foo/bar']
953 953 >>> _explicitfiles([('rootfilesin', 'foo/bar', '')])
954 954 []
955 955 '''
956 956 # Keep only the pattern kinds where one can specify filenames (vs only
957 957 # directory names).
958 958 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
959 959 return _roots(filable)
960 960
961 961 def _anypats(kindpats):
962 962 for kind, pat, source in kindpats:
963 963 if kind in ('glob', 're', 'relglob', 'relre', 'set', 'rootfilesin'):
964 964 return True
965 965
966 966 _commentre = None
967 967
968 968 def readpatternfile(filepath, warn, sourceinfo=False):
969 969 '''parse a pattern file, returning a list of
970 970 patterns. These patterns should be given to compile()
971 971 to be validated and converted into a match function.
972 972
973 973 trailing white space is dropped.
974 974 the escape character is backslash.
975 975 comments start with #.
976 976 empty lines are skipped.
977 977
978 978 lines can be of the following formats:
979 979
980 980 syntax: regexp # defaults following lines to non-rooted regexps
981 981 syntax: glob # defaults following lines to non-rooted globs
982 982 re:pattern # non-rooted regular expression
983 983 glob:pattern # non-rooted glob
984 984 pattern # pattern of the current default type
985 985
986 986 if sourceinfo is set, returns a list of tuples:
987 987 (pattern, lineno, originalline). This is useful to debug ignore patterns.
988 988 '''
989 989
990 990 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
991 991 'include': 'include', 'subinclude': 'subinclude'}
992 992 syntax = 'relre:'
993 993 patterns = []
994 994
995 995 fp = open(filepath, 'rb')
996 996 for lineno, line in enumerate(util.iterfile(fp), start=1):
997 997 if "#" in line:
998 998 global _commentre
999 999 if not _commentre:
1000 1000 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1001 1001 # remove comments prefixed by an even number of escapes
1002 1002 m = _commentre.search(line)
1003 1003 if m:
1004 1004 line = line[:m.end(1)]
1005 1005 # fixup properly escaped comments that survived the above
1006 1006 line = line.replace("\\#", "#")
1007 1007 line = line.rstrip()
1008 1008 if not line:
1009 1009 continue
1010 1010
1011 1011 if line.startswith('syntax:'):
1012 1012 s = line[7:].strip()
1013 1013 try:
1014 1014 syntax = syntaxes[s]
1015 1015 except KeyError:
1016 1016 if warn:
1017 1017 warn(_("%s: ignoring invalid syntax '%s'\n") %
1018 1018 (filepath, s))
1019 1019 continue
1020 1020
1021 1021 linesyntax = syntax
1022 1022 for s, rels in syntaxes.iteritems():
1023 1023 if line.startswith(rels):
1024 1024 linesyntax = rels
1025 1025 line = line[len(rels):]
1026 1026 break
1027 1027 elif line.startswith(s+':'):
1028 1028 linesyntax = rels
1029 1029 line = line[len(s) + 1:]
1030 1030 break
1031 1031 if sourceinfo:
1032 1032 patterns.append((linesyntax + line, lineno, line))
1033 1033 else:
1034 1034 patterns.append(linesyntax + line)
1035 1035 fp.close()
1036 1036 return patterns
@@ -1,518 +1,518
1 1 $ hg init t
2 2 $ cd t
3 3 $ mkdir -p beans
4 4 $ for b in kidney navy turtle borlotti black pinto; do
5 5 > echo $b > beans/$b
6 6 > done
7 7 $ mkdir -p mammals/Procyonidae
8 8 $ for m in cacomistle coatimundi raccoon; do
9 9 > echo $m > mammals/Procyonidae/$m
10 10 > done
11 11 $ echo skunk > mammals/skunk
12 12 $ echo fennel > fennel
13 13 $ echo fenugreek > fenugreek
14 14 $ echo fiddlehead > fiddlehead
15 15 $ hg addremove
16 16 adding beans/black
17 17 adding beans/borlotti
18 18 adding beans/kidney
19 19 adding beans/navy
20 20 adding beans/pinto
21 21 adding beans/turtle
22 22 adding fennel
23 23 adding fenugreek
24 24 adding fiddlehead
25 25 adding mammals/Procyonidae/cacomistle
26 26 adding mammals/Procyonidae/coatimundi
27 27 adding mammals/Procyonidae/raccoon
28 28 adding mammals/skunk
29 29 $ hg commit -m "commit #0"
30 30
31 31 $ hg debugwalk
32 32 matcher: <alwaysmatcher>
33 33 f beans/black beans/black
34 34 f beans/borlotti beans/borlotti
35 35 f beans/kidney beans/kidney
36 36 f beans/navy beans/navy
37 37 f beans/pinto beans/pinto
38 38 f beans/turtle beans/turtle
39 39 f fennel fennel
40 40 f fenugreek fenugreek
41 41 f fiddlehead fiddlehead
42 42 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
43 43 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
44 44 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
45 45 f mammals/skunk mammals/skunk
46 46 $ hg debugwalk -I.
47 47 matcher: <includematcher includes='(?:)'>
48 48 f beans/black beans/black
49 49 f beans/borlotti beans/borlotti
50 50 f beans/kidney beans/kidney
51 51 f beans/navy beans/navy
52 52 f beans/pinto beans/pinto
53 53 f beans/turtle beans/turtle
54 54 f fennel fennel
55 55 f fenugreek fenugreek
56 56 f fiddlehead fiddlehead
57 57 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
58 58 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
59 59 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
60 60 f mammals/skunk mammals/skunk
61 61
62 62 $ cd mammals
63 63 $ hg debugwalk
64 64 matcher: <alwaysmatcher>
65 65 f beans/black ../beans/black
66 66 f beans/borlotti ../beans/borlotti
67 67 f beans/kidney ../beans/kidney
68 68 f beans/navy ../beans/navy
69 69 f beans/pinto ../beans/pinto
70 70 f beans/turtle ../beans/turtle
71 71 f fennel ../fennel
72 72 f fenugreek ../fenugreek
73 73 f fiddlehead ../fiddlehead
74 74 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
75 75 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
76 76 f mammals/Procyonidae/raccoon Procyonidae/raccoon
77 77 f mammals/skunk skunk
78 78 $ hg debugwalk -X ../beans
79 79 matcher: <differencematcher m1=<alwaysmatcher>, m2=<includematcher includes='(?:beans(?:/|$))'>>
80 80 f fennel ../fennel
81 81 f fenugreek ../fenugreek
82 82 f fiddlehead ../fiddlehead
83 83 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
84 84 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
85 85 f mammals/Procyonidae/raccoon Procyonidae/raccoon
86 86 f mammals/skunk skunk
87 87 $ hg debugwalk -I '*k'
88 88 matcher: <includematcher includes='(?:mammals\\/[^/]*k(?:/|$))'>
89 89 f mammals/skunk skunk
90 90 $ hg debugwalk -I 'glob:*k'
91 91 matcher: <includematcher includes='(?:mammals\\/[^/]*k(?:/|$))'>
92 92 f mammals/skunk skunk
93 93 $ hg debugwalk -I 'relglob:*k'
94 94 matcher: <includematcher includes='(?:(?:|.*/)[^/]*k(?:/|$))'>
95 95 f beans/black ../beans/black
96 96 f fenugreek ../fenugreek
97 97 f mammals/skunk skunk
98 98 $ hg debugwalk -I 'relglob:*k' .
99 99 matcher: <intersectionmatcher m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, m2=<includematcher includes='(?:(?:|.*/)[^/]*k(?:/|$))'>>
100 100 f mammals/skunk skunk
101 101 $ hg debugwalk -I 're:.*k$'
102 102 matcher: <includematcher includes='(?:.*k$)'>
103 103 f beans/black ../beans/black
104 104 f fenugreek ../fenugreek
105 105 f mammals/skunk skunk
106 106 $ hg debugwalk -I 'relre:.*k$'
107 107 matcher: <includematcher includes='(?:.*.*k$)'>
108 108 f beans/black ../beans/black
109 109 f fenugreek ../fenugreek
110 110 f mammals/skunk skunk
111 111 $ hg debugwalk -I 'path:beans'
112 matcher: <includematcher includes='(?:^beans(?:/|$))'>
112 matcher: <includematcher includes='(?:beans(?:/|$))'>
113 113 f beans/black ../beans/black
114 114 f beans/borlotti ../beans/borlotti
115 115 f beans/kidney ../beans/kidney
116 116 f beans/navy ../beans/navy
117 117 f beans/pinto ../beans/pinto
118 118 f beans/turtle ../beans/turtle
119 119 $ hg debugwalk -I 'relpath:detour/../../beans'
120 120 matcher: <includematcher includes='(?:beans(?:/|$))'>
121 121 f beans/black ../beans/black
122 122 f beans/borlotti ../beans/borlotti
123 123 f beans/kidney ../beans/kidney
124 124 f beans/navy ../beans/navy
125 125 f beans/pinto ../beans/pinto
126 126 f beans/turtle ../beans/turtle
127 127
128 128 $ hg debugwalk 'rootfilesin:'
129 matcher: <patternmatcher patterns='(?:^[^/]+$)'>
129 matcher: <patternmatcher patterns='(?:[^/]+$)'>
130 130 f fennel ../fennel
131 131 f fenugreek ../fenugreek
132 132 f fiddlehead ../fiddlehead
133 133 $ hg debugwalk -I 'rootfilesin:'
134 matcher: <includematcher includes='(?:^[^/]+$)'>
134 matcher: <includematcher includes='(?:[^/]+$)'>
135 135 f fennel ../fennel
136 136 f fenugreek ../fenugreek
137 137 f fiddlehead ../fiddlehead
138 138 $ hg debugwalk 'rootfilesin:.'
139 matcher: <patternmatcher patterns='(?:^[^/]+$)'>
139 matcher: <patternmatcher patterns='(?:[^/]+$)'>
140 140 f fennel ../fennel
141 141 f fenugreek ../fenugreek
142 142 f fiddlehead ../fiddlehead
143 143 $ hg debugwalk -I 'rootfilesin:.'
144 matcher: <includematcher includes='(?:^[^/]+$)'>
144 matcher: <includematcher includes='(?:[^/]+$)'>
145 145 f fennel ../fennel
146 146 f fenugreek ../fenugreek
147 147 f fiddlehead ../fiddlehead
148 148 $ hg debugwalk -X 'rootfilesin:'
149 matcher: <differencematcher m1=<alwaysmatcher>, m2=<includematcher includes='(?:^[^/]+$)'>>
149 matcher: <differencematcher m1=<alwaysmatcher>, m2=<includematcher includes='(?:[^/]+$)'>>
150 150 f beans/black ../beans/black
151 151 f beans/borlotti ../beans/borlotti
152 152 f beans/kidney ../beans/kidney
153 153 f beans/navy ../beans/navy
154 154 f beans/pinto ../beans/pinto
155 155 f beans/turtle ../beans/turtle
156 156 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
157 157 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
158 158 f mammals/Procyonidae/raccoon Procyonidae/raccoon
159 159 f mammals/skunk skunk
160 160 $ hg debugwalk 'rootfilesin:fennel'
161 matcher: <patternmatcher patterns='(?:^fennel/[^/]+$)'>
161 matcher: <patternmatcher patterns='(?:fennel/[^/]+$)'>
162 162 $ hg debugwalk -I 'rootfilesin:fennel'
163 matcher: <includematcher includes='(?:^fennel/[^/]+$)'>
163 matcher: <includematcher includes='(?:fennel/[^/]+$)'>
164 164 $ hg debugwalk 'rootfilesin:skunk'
165 matcher: <patternmatcher patterns='(?:^skunk/[^/]+$)'>
165 matcher: <patternmatcher patterns='(?:skunk/[^/]+$)'>
166 166 $ hg debugwalk -I 'rootfilesin:skunk'
167 matcher: <includematcher includes='(?:^skunk/[^/]+$)'>
167 matcher: <includematcher includes='(?:skunk/[^/]+$)'>
168 168 $ hg debugwalk 'rootfilesin:beans'
169 matcher: <patternmatcher patterns='(?:^beans/[^/]+$)'>
169 matcher: <patternmatcher patterns='(?:beans/[^/]+$)'>
170 170 f beans/black ../beans/black
171 171 f beans/borlotti ../beans/borlotti
172 172 f beans/kidney ../beans/kidney
173 173 f beans/navy ../beans/navy
174 174 f beans/pinto ../beans/pinto
175 175 f beans/turtle ../beans/turtle
176 176 $ hg debugwalk -I 'rootfilesin:beans'
177 matcher: <includematcher includes='(?:^beans/[^/]+$)'>
177 matcher: <includematcher includes='(?:beans/[^/]+$)'>
178 178 f beans/black ../beans/black
179 179 f beans/borlotti ../beans/borlotti
180 180 f beans/kidney ../beans/kidney
181 181 f beans/navy ../beans/navy
182 182 f beans/pinto ../beans/pinto
183 183 f beans/turtle ../beans/turtle
184 184 $ hg debugwalk 'rootfilesin:mammals'
185 matcher: <patternmatcher patterns='(?:^mammals/[^/]+$)'>
185 matcher: <patternmatcher patterns='(?:mammals/[^/]+$)'>
186 186 f mammals/skunk skunk
187 187 $ hg debugwalk -I 'rootfilesin:mammals'
188 matcher: <includematcher includes='(?:^mammals/[^/]+$)'>
188 matcher: <includematcher includes='(?:mammals/[^/]+$)'>
189 189 f mammals/skunk skunk
190 190 $ hg debugwalk 'rootfilesin:mammals/'
191 matcher: <patternmatcher patterns='(?:^mammals/[^/]+$)'>
191 matcher: <patternmatcher patterns='(?:mammals/[^/]+$)'>
192 192 f mammals/skunk skunk
193 193 $ hg debugwalk -I 'rootfilesin:mammals/'
194 matcher: <includematcher includes='(?:^mammals/[^/]+$)'>
194 matcher: <includematcher includes='(?:mammals/[^/]+$)'>
195 195 f mammals/skunk skunk
196 196 $ hg debugwalk -X 'rootfilesin:mammals'
197 matcher: <differencematcher m1=<alwaysmatcher>, m2=<includematcher includes='(?:^mammals/[^/]+$)'>>
197 matcher: <differencematcher m1=<alwaysmatcher>, m2=<includematcher includes='(?:mammals/[^/]+$)'>>
198 198 f beans/black ../beans/black
199 199 f beans/borlotti ../beans/borlotti
200 200 f beans/kidney ../beans/kidney
201 201 f beans/navy ../beans/navy
202 202 f beans/pinto ../beans/pinto
203 203 f beans/turtle ../beans/turtle
204 204 f fennel ../fennel
205 205 f fenugreek ../fenugreek
206 206 f fiddlehead ../fiddlehead
207 207 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
208 208 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
209 209 f mammals/Procyonidae/raccoon Procyonidae/raccoon
210 210
211 211 $ hg debugwalk .
212 212 matcher: <patternmatcher patterns='(?:mammals(?:/|$))'>
213 213 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
214 214 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
215 215 f mammals/Procyonidae/raccoon Procyonidae/raccoon
216 216 f mammals/skunk skunk
217 217 $ hg debugwalk -I.
218 218 matcher: <includematcher includes='(?:mammals(?:/|$))'>
219 219 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
220 220 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
221 221 f mammals/Procyonidae/raccoon Procyonidae/raccoon
222 222 f mammals/skunk skunk
223 223 $ hg debugwalk Procyonidae
224 224 matcher: <patternmatcher patterns='(?:mammals\\/Procyonidae(?:/|$))'>
225 225 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
226 226 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
227 227 f mammals/Procyonidae/raccoon Procyonidae/raccoon
228 228
229 229 $ cd Procyonidae
230 230 $ hg debugwalk .
231 231 matcher: <patternmatcher patterns='(?:mammals\\/Procyonidae(?:/|$))'>
232 232 f mammals/Procyonidae/cacomistle cacomistle
233 233 f mammals/Procyonidae/coatimundi coatimundi
234 234 f mammals/Procyonidae/raccoon raccoon
235 235 $ hg debugwalk ..
236 236 matcher: <patternmatcher patterns='(?:mammals(?:/|$))'>
237 237 f mammals/Procyonidae/cacomistle cacomistle
238 238 f mammals/Procyonidae/coatimundi coatimundi
239 239 f mammals/Procyonidae/raccoon raccoon
240 240 f mammals/skunk ../skunk
241 241 $ cd ..
242 242
243 243 $ hg debugwalk ../beans
244 244 matcher: <patternmatcher patterns='(?:beans(?:/|$))'>
245 245 f beans/black ../beans/black
246 246 f beans/borlotti ../beans/borlotti
247 247 f beans/kidney ../beans/kidney
248 248 f beans/navy ../beans/navy
249 249 f beans/pinto ../beans/pinto
250 250 f beans/turtle ../beans/turtle
251 251 $ hg debugwalk .
252 252 matcher: <patternmatcher patterns='(?:mammals(?:/|$))'>
253 253 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
254 254 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
255 255 f mammals/Procyonidae/raccoon Procyonidae/raccoon
256 256 f mammals/skunk skunk
257 257 $ hg debugwalk .hg
258 258 abort: path 'mammals/.hg' is inside nested repo 'mammals' (glob)
259 259 [255]
260 260 $ hg debugwalk ../.hg
261 261 abort: path contains illegal component: .hg
262 262 [255]
263 263 $ cd ..
264 264
265 265 $ hg debugwalk -Ibeans
266 266 matcher: <includematcher includes='(?:beans(?:/|$))'>
267 267 f beans/black beans/black
268 268 f beans/borlotti beans/borlotti
269 269 f beans/kidney beans/kidney
270 270 f beans/navy beans/navy
271 271 f beans/pinto beans/pinto
272 272 f beans/turtle beans/turtle
273 273 $ hg debugwalk -I '{*,{b,m}*/*}k'
274 274 matcher: <includematcher includes='(?:(?:[^/]*|(?:b|m)[^/]*\\/[^/]*)k(?:/|$))'>
275 275 f beans/black beans/black
276 276 f fenugreek fenugreek
277 277 f mammals/skunk mammals/skunk
278 278 $ hg debugwalk -Ibeans mammals
279 279 matcher: <intersectionmatcher m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>>
280 280 $ hg debugwalk -Inon-existent
281 281 matcher: <includematcher includes='(?:non\\-existent(?:/|$))'>
282 282 $ hg debugwalk -Inon-existent -Ibeans/black
283 283 matcher: <includematcher includes='(?:non\\-existent(?:/|$)|beans\\/black(?:/|$))'>
284 284 f beans/black beans/black
285 285 $ hg debugwalk -Ibeans beans/black
286 286 matcher: <intersectionmatcher m1=<patternmatcher patterns='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>>
287 287 f beans/black beans/black exact
288 288 $ hg debugwalk -Ibeans/black beans
289 289 matcher: <intersectionmatcher m1=<patternmatcher patterns='(?:beans(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>>
290 290 f beans/black beans/black
291 291 $ hg debugwalk -Xbeans/black beans
292 292 matcher: <differencematcher m1=<patternmatcher patterns='(?:beans(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>>
293 293 f beans/borlotti beans/borlotti
294 294 f beans/kidney beans/kidney
295 295 f beans/navy beans/navy
296 296 f beans/pinto beans/pinto
297 297 f beans/turtle beans/turtle
298 298 $ hg debugwalk -Xbeans/black -Ibeans
299 299 matcher: <differencematcher m1=<includematcher includes='(?:beans(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>>
300 300 f beans/borlotti beans/borlotti
301 301 f beans/kidney beans/kidney
302 302 f beans/navy beans/navy
303 303 f beans/pinto beans/pinto
304 304 f beans/turtle beans/turtle
305 305 $ hg debugwalk -Xbeans/black beans/black
306 306 matcher: <differencematcher m1=<patternmatcher patterns='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>>
307 307 f beans/black beans/black exact
308 308 $ hg debugwalk -Xbeans/black -Ibeans/black
309 309 matcher: <differencematcher m1=<includematcher includes='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans\\/black(?:/|$))'>>
310 310 $ hg debugwalk -Xbeans beans/black
311 311 matcher: <differencematcher m1=<patternmatcher patterns='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>>
312 312 f beans/black beans/black exact
313 313 $ hg debugwalk -Xbeans -Ibeans/black
314 314 matcher: <differencematcher m1=<includematcher includes='(?:beans\\/black(?:/|$))'>, m2=<includematcher includes='(?:beans(?:/|$))'>>
315 315 $ hg debugwalk 'glob:mammals/../beans/b*'
316 316 matcher: <patternmatcher patterns='(?:beans\\/b[^/]*$)'>
317 317 f beans/black beans/black
318 318 f beans/borlotti beans/borlotti
319 319 $ hg debugwalk '-X*/Procyonidae' mammals
320 320 matcher: <differencematcher m1=<patternmatcher patterns='(?:mammals(?:/|$))'>, m2=<includematcher includes='(?:[^/]*\\/Procyonidae(?:/|$))'>>
321 321 f mammals/skunk mammals/skunk
322 322 $ hg debugwalk path:mammals
323 matcher: <patternmatcher patterns='(?:^mammals(?:/|$))'>
323 matcher: <patternmatcher patterns='(?:mammals(?:/|$))'>
324 324 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
325 325 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
326 326 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
327 327 f mammals/skunk mammals/skunk
328 328 $ hg debugwalk ..
329 329 abort: .. not under root '$TESTTMP/t' (glob)
330 330 [255]
331 331 $ hg debugwalk beans/../..
332 332 abort: beans/../.. not under root '$TESTTMP/t' (glob)
333 333 [255]
334 334 $ hg debugwalk .hg
335 335 abort: path contains illegal component: .hg
336 336 [255]
337 337 $ hg debugwalk beans/../.hg
338 338 abort: path contains illegal component: .hg
339 339 [255]
340 340 $ hg debugwalk beans/../.hg/data
341 341 abort: path contains illegal component: .hg/data (glob)
342 342 [255]
343 343 $ hg debugwalk beans/.hg
344 344 abort: path 'beans/.hg' is inside nested repo 'beans' (glob)
345 345 [255]
346 346
347 347 Test absolute paths:
348 348
349 349 $ hg debugwalk `pwd`/beans
350 350 matcher: <patternmatcher patterns='(?:beans(?:/|$))'>
351 351 f beans/black beans/black
352 352 f beans/borlotti beans/borlotti
353 353 f beans/kidney beans/kidney
354 354 f beans/navy beans/navy
355 355 f beans/pinto beans/pinto
356 356 f beans/turtle beans/turtle
357 357 $ hg debugwalk `pwd`/..
358 358 abort: $TESTTMP/t/.. not under root '$TESTTMP/t' (glob)
359 359 [255]
360 360
361 361 Test patterns:
362 362
363 363 $ hg debugwalk glob:\*
364 364 matcher: <patternmatcher patterns='(?:[^/]*$)'>
365 365 f fennel fennel
366 366 f fenugreek fenugreek
367 367 f fiddlehead fiddlehead
368 368 #if eol-in-paths
369 369 $ echo glob:glob > glob:glob
370 370 $ hg addremove
371 371 adding glob:glob
372 372 warning: filename contains ':', which is reserved on Windows: 'glob:glob'
373 373 $ hg debugwalk glob:\*
374 374 matcher: <patternmatcher patterns='(?:[^/]*$)'>
375 375 f fennel fennel
376 376 f fenugreek fenugreek
377 377 f fiddlehead fiddlehead
378 378 f glob:glob glob:glob
379 379 $ hg debugwalk glob:glob
380 380 matcher: <patternmatcher patterns='(?:glob$)'>
381 381 glob: No such file or directory
382 382 $ hg debugwalk glob:glob:glob
383 383 matcher: <patternmatcher patterns='(?:glob\\:glob$)'>
384 384 f glob:glob glob:glob exact
385 385 $ hg debugwalk path:glob:glob
386 matcher: <patternmatcher patterns='(?:^glob\\:glob(?:/|$))'>
386 matcher: <patternmatcher patterns='(?:glob\\:glob(?:/|$))'>
387 387 f glob:glob glob:glob exact
388 388 $ rm glob:glob
389 389 $ hg addremove
390 390 removing glob:glob
391 391 #endif
392 392
393 393 $ hg debugwalk 'glob:**e'
394 394 matcher: <patternmatcher patterns='(?:.*e$)'>
395 395 f beans/turtle beans/turtle
396 396 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
397 397
398 398 $ hg debugwalk 're:.*[kb]$'
399 399 matcher: <patternmatcher patterns='(?:.*[kb]$)'>
400 400 f beans/black beans/black
401 401 f fenugreek fenugreek
402 402 f mammals/skunk mammals/skunk
403 403
404 404 $ hg debugwalk path:beans/black
405 matcher: <patternmatcher patterns='(?:^beans\\/black(?:/|$))'>
405 matcher: <patternmatcher patterns='(?:beans\\/black(?:/|$))'>
406 406 f beans/black beans/black exact
407 407 $ hg debugwalk path:beans//black
408 matcher: <patternmatcher patterns='(?:^beans\\/black(?:/|$))'>
408 matcher: <patternmatcher patterns='(?:beans\\/black(?:/|$))'>
409 409 f beans/black beans/black exact
410 410
411 411 $ hg debugwalk relglob:Procyonidae
412 412 matcher: <patternmatcher patterns='(?:(?:|.*/)Procyonidae$)'>
413 413 $ hg debugwalk 'relglob:Procyonidae/**'
414 414 matcher: <patternmatcher patterns='(?:(?:|.*/)Procyonidae\\/.*$)'>
415 415 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
416 416 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
417 417 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
418 418 $ hg debugwalk 'relglob:Procyonidae/**' fennel
419 419 matcher: <patternmatcher patterns='(?:(?:|.*/)Procyonidae\\/.*$|fennel(?:/|$))'>
420 420 f fennel fennel exact
421 421 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
422 422 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
423 423 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
424 424 $ hg debugwalk beans 'glob:beans/*'
425 425 matcher: <patternmatcher patterns='(?:beans(?:/|$)|beans\\/[^/]*$)'>
426 426 f beans/black beans/black
427 427 f beans/borlotti beans/borlotti
428 428 f beans/kidney beans/kidney
429 429 f beans/navy beans/navy
430 430 f beans/pinto beans/pinto
431 431 f beans/turtle beans/turtle
432 432 $ hg debugwalk 'glob:mamm**'
433 433 matcher: <patternmatcher patterns='(?:mamm.*$)'>
434 434 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
435 435 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
436 436 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
437 437 f mammals/skunk mammals/skunk
438 438 $ hg debugwalk 'glob:mamm**' fennel
439 439 matcher: <patternmatcher patterns='(?:mamm.*$|fennel(?:/|$))'>
440 440 f fennel fennel exact
441 441 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
442 442 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
443 443 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
444 444 f mammals/skunk mammals/skunk
445 445 $ hg debugwalk 'glob:j*'
446 446 matcher: <patternmatcher patterns='(?:j[^/]*$)'>
447 447 $ hg debugwalk NOEXIST
448 448 matcher: <patternmatcher patterns='(?:NOEXIST(?:/|$))'>
449 449 NOEXIST: * (glob)
450 450
451 451 #if fifo
452 452 $ mkfifo fifo
453 453 $ hg debugwalk fifo
454 454 matcher: <patternmatcher patterns='(?:fifo(?:/|$))'>
455 455 fifo: unsupported file type (type is fifo)
456 456 #endif
457 457
458 458 $ rm fenugreek
459 459 $ hg debugwalk fenugreek
460 460 matcher: <patternmatcher patterns='(?:fenugreek(?:/|$))'>
461 461 f fenugreek fenugreek exact
462 462 $ hg rm fenugreek
463 463 $ hg debugwalk fenugreek
464 464 matcher: <patternmatcher patterns='(?:fenugreek(?:/|$))'>
465 465 f fenugreek fenugreek exact
466 466 $ touch new
467 467 $ hg debugwalk new
468 468 matcher: <patternmatcher patterns='(?:new(?:/|$))'>
469 469 f new new exact
470 470
471 471 $ mkdir ignored
472 472 $ touch ignored/file
473 473 $ echo '^ignored$' > .hgignore
474 474 $ hg debugwalk ignored
475 475 matcher: <patternmatcher patterns='(?:ignored(?:/|$))'>
476 476 $ hg debugwalk ignored/file
477 477 matcher: <patternmatcher patterns='(?:ignored\\/file(?:/|$))'>
478 478 f ignored/file ignored/file exact
479 479
480 480 Test listfile and listfile0
481 481
482 482 $ $PYTHON -c "file('listfile0', 'wb').write('fenugreek\0new\0')"
483 483 $ hg debugwalk -I 'listfile0:listfile0'
484 484 matcher: <includematcher includes='(?:fenugreek(?:/|$)|new(?:/|$))'>
485 485 f fenugreek fenugreek
486 486 f new new
487 487 $ $PYTHON -c "file('listfile', 'wb').write('fenugreek\nnew\r\nmammals/skunk\n')"
488 488 $ hg debugwalk -I 'listfile:listfile'
489 489 matcher: <includematcher includes='(?:fenugreek(?:/|$)|new(?:/|$)|mammals\\/skunk(?:/|$))'>
490 490 f fenugreek fenugreek
491 491 f mammals/skunk mammals/skunk
492 492 f new new
493 493
494 494 $ cd ..
495 495 $ hg debugwalk -R t t/mammals/skunk
496 496 matcher: <patternmatcher patterns='(?:mammals\\/skunk(?:/|$))'>
497 497 f mammals/skunk t/mammals/skunk exact
498 498 $ mkdir t2
499 499 $ cd t2
500 500 $ hg debugwalk -R ../t ../t/mammals/skunk
501 501 matcher: <patternmatcher patterns='(?:mammals\\/skunk(?:/|$))'>
502 502 f mammals/skunk ../t/mammals/skunk exact
503 503 $ hg debugwalk --cwd ../t mammals/skunk
504 504 matcher: <patternmatcher patterns='(?:mammals\\/skunk(?:/|$))'>
505 505 f mammals/skunk mammals/skunk exact
506 506
507 507 $ cd ..
508 508
509 509 Test split patterns on overflow
510 510
511 511 $ cd t
512 512 $ echo fennel > overflow.list
513 513 $ $PYTHON -c "for i in xrange(20000 / 100): print 'x' * 100" >> overflow.list
514 514 $ echo fenugreek >> overflow.list
515 515 $ hg debugwalk 'listfile:overflow.list' 2>&1 | egrep -v '(^matcher: |^xxx)'
516 516 f fennel fennel exact
517 517 f fenugreek fenugreek exact
518 518 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now