##// END OF EJS Templates
match: handle excludes using new differencematcher...
Martin von Zweigbergk -
r32465:a83a7d27 default
parent child Browse files
Show More
@@ -1,836 +1,897 b''
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, 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 return matcher(root, cwd, normalize, patterns, include=include,
146 exclude=exclude, default=default, exact=exact,
147 auditor=auditor, ctx=ctx, listsubrepos=listsubrepos,
148 warn=warn, badfn=badfn)
145 m = matcher(root, cwd, normalize, patterns, include=include, exclude=None,
146 default=default, exact=exact, auditor=auditor, ctx=ctx,
147 listsubrepos=listsubrepos, warn=warn, badfn=badfn)
148 if exclude:
149 em = matcher(root, cwd, normalize, [], include=exclude, exclude=None,
150 default=default, exact=False, auditor=auditor, ctx=ctx,
151 listsubrepos=listsubrepos, warn=warn, badfn=None)
152 m = differencematcher(m, em)
153 return m
149 154
150 155 def exact(root, cwd, files, badfn=None):
151 156 return match(root, cwd, files, exact=True, badfn=badfn)
152 157
153 158 def always(root, cwd):
154 159 return match(root, cwd, [])
155 160
156 161 def badmatch(match, badfn):
157 162 """Make a copy of the given matcher, replacing its bad method with the given
158 163 one.
159 164 """
160 165 m = copy.copy(match)
161 166 m.bad = badfn
162 167 return m
163 168
164 169 def _donormalize(patterns, default, root, cwd, auditor, warn):
165 170 '''Convert 'kind:pat' from the patterns list to tuples with kind and
166 171 normalized and rooted patterns and with listfiles expanded.'''
167 172 kindpats = []
168 173 for kind, pat in [_patsplit(p, default) for p in patterns]:
169 174 if kind in ('glob', 'relpath'):
170 175 pat = pathutil.canonpath(root, cwd, pat, auditor)
171 176 elif kind in ('relglob', 'path', 'rootfilesin'):
172 177 pat = util.normpath(pat)
173 178 elif kind in ('listfile', 'listfile0'):
174 179 try:
175 180 files = util.readfile(pat)
176 181 if kind == 'listfile0':
177 182 files = files.split('\0')
178 183 else:
179 184 files = files.splitlines()
180 185 files = [f for f in files if f]
181 186 except EnvironmentError:
182 187 raise error.Abort(_("unable to read file list (%s)") % pat)
183 188 for k, p, source in _donormalize(files, default, root, cwd,
184 189 auditor, warn):
185 190 kindpats.append((k, p, pat))
186 191 continue
187 192 elif kind == 'include':
188 193 try:
189 194 fullpath = os.path.join(root, util.localpath(pat))
190 195 includepats = readpatternfile(fullpath, warn)
191 196 for k, p, source in _donormalize(includepats, default,
192 197 root, cwd, auditor, warn):
193 198 kindpats.append((k, p, source or pat))
194 199 except error.Abort as inst:
195 200 raise error.Abort('%s: %s' % (pat, inst[0]))
196 201 except IOError as inst:
197 202 if warn:
198 203 warn(_("skipping unreadable pattern file '%s': %s\n") %
199 204 (pat, inst.strerror))
200 205 continue
201 206 # else: re or relre - which cannot be normalized
202 207 kindpats.append((kind, pat, ''))
203 208 return kindpats
204 209
205 210 class basematcher(object):
206 211
207 212 def __init__(self, root, cwd, badfn=None):
208 213 self._root = root
209 214 self._cwd = cwd
210 215 if badfn is not None:
211 216 self.bad = badfn
212 217
213 218 def __call__(self, fn):
214 219 return self.matchfn(fn)
215 220 def __iter__(self):
216 221 for f in self._files:
217 222 yield f
218 223 # Callbacks related to how the matcher is used by dirstate.walk.
219 224 # Subscribers to these events must monkeypatch the matcher object.
220 225 def bad(self, f, msg):
221 226 '''Callback from dirstate.walk for each explicit file that can't be
222 227 found/accessed, with an error message.'''
223 228 pass
224 229
225 230 # If an explicitdir is set, it will be called when an explicitly listed
226 231 # directory is visited.
227 232 explicitdir = None
228 233
229 234 # If an traversedir is set, it will be called when a directory discovered
230 235 # by recursive traversal is visited.
231 236 traversedir = None
232 237
233 238 def abs(self, f):
234 239 '''Convert a repo path back to path that is relative to the root of the
235 240 matcher.'''
236 241 return f
237 242
238 243 def rel(self, f):
239 244 '''Convert repo path back to path that is relative to cwd of matcher.'''
240 245 return util.pathto(self._root, self._cwd, f)
241 246
242 247 def uipath(self, f):
243 248 '''Convert repo path to a display path. If patterns or -I/-X were used
244 249 to create this matcher, the display path will be relative to cwd.
245 250 Otherwise it is relative to the root of the repo.'''
246 251 return self.rel(f)
247 252
248 253 @propertycache
249 254 def _files(self):
250 255 return []
251 256
252 257 def files(self):
253 258 '''Explicitly listed files or patterns or roots:
254 259 if no patterns or .always(): empty list,
255 260 if exact: list exact files,
256 261 if not .anypats(): list all files and dirs,
257 262 else: optimal roots'''
258 263 return self._files
259 264
260 265 @propertycache
261 266 def _fileset(self):
262 267 return set(self._files)
263 268
264 269 def exact(self, f):
265 270 '''Returns True if f is in .files().'''
266 271 return f in self._fileset
267 272
268 273 def matchfn(self, f):
269 274 return False
270 275
271 276 def visitdir(self, dir):
272 277 '''Decides whether a directory should be visited based on whether it
273 278 has potential matches in it or one of its subdirectories. This is
274 279 based on the match's primary, included, and excluded patterns.
275 280
276 281 Returns the string 'all' if the given directory and all subdirectories
277 282 should be visited. Otherwise returns True or False indicating whether
278 283 the given directory should be visited.
279 284
280 285 This function's behavior is undefined if it has returned False for
281 286 one of the dir's parent directories.
282 287 '''
283 288 return False
284 289
285 290 def anypats(self):
286 291 '''Matcher uses patterns or include/exclude.'''
287 292 return False
288 293
289 294 def always(self):
290 295 '''Matcher will match everything and .files() will be empty
291 296 - optimization might be possible and necessary.'''
292 297 return False
293 298
294 299 def isexact(self):
295 300 return False
296 301
297 302 def prefix(self):
298 303 return not self.always() and not self.isexact() and not self.anypats()
299 304
300 305 class matcher(basematcher):
301 306
302 307 def __init__(self, root, cwd, normalize, patterns, include=None,
303 308 exclude=None, default='glob', exact=False, auditor=None,
304 309 ctx=None, listsubrepos=False, warn=None, badfn=None):
305 310 super(matcher, self).__init__(root, cwd, badfn)
306 311 if include is None:
307 312 include = []
308 313 if exclude is None:
309 314 exclude = []
310 315
311 316 self._anypats = bool(include or exclude)
312 317 self._anyincludepats = False
313 318 self._always = False
314 319 self._pathrestricted = bool(include or exclude or patterns)
315 320 self.patternspat = None
316 321 self.includepat = None
317 322 self.excludepat = None
318 323
319 324 # roots are directories which are recursively included/excluded.
320 325 self._includeroots = set()
321 326 self._excluderoots = set()
322 327 # dirs are directories which are non-recursively included.
323 328 self._includedirs = set()
324 329
325 330 matchfns = []
326 331 if include:
327 332 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
328 333 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
329 334 listsubrepos, root)
330 335 self._anyincludepats = _anypats(kindpats)
331 336 roots, dirs = _rootsanddirs(kindpats)
332 337 self._includeroots.update(roots)
333 338 self._includedirs.update(dirs)
334 339 matchfns.append(im)
335 340 if exclude:
336 341 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
337 342 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
338 343 listsubrepos, root)
339 344 if not _anypats(kindpats):
340 345 # Only consider recursive excludes as such - if a non-recursive
341 346 # exclude is used, we must still recurse into the excluded
342 347 # directory, at least to find subdirectories. In such a case,
343 348 # the regex still won't match the non-recursively-excluded
344 349 # files.
345 350 self._excluderoots.update(_roots(kindpats))
346 351 matchfns.append(lambda f: not em(f))
347 352 if exact:
348 353 if isinstance(patterns, list):
349 354 self._files = patterns
350 355 else:
351 356 self._files = list(patterns)
352 357 matchfns.append(self.exact)
353 358 elif patterns:
354 359 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
355 360 if not _kindpatsalwaysmatch(kindpats):
356 361 self._files = _explicitfiles(kindpats)
357 362 self._anypats = self._anypats or _anypats(kindpats)
358 363 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
359 364 listsubrepos, root)
360 365 matchfns.append(pm)
361 366
362 367 if not matchfns:
363 368 m = util.always
364 369 self._always = True
365 370 elif len(matchfns) == 1:
366 371 m = matchfns[0]
367 372 else:
368 373 def m(f):
369 374 for matchfn in matchfns:
370 375 if not matchfn(f):
371 376 return False
372 377 return True
373 378
374 379 self.matchfn = m
375 380
376 381 def uipath(self, f):
377 382 return (self._pathrestricted and self.rel(f)) or self.abs(f)
378 383
379 384 @propertycache
380 385 def _dirs(self):
381 386 return set(util.dirs(self._fileset)) | {'.'}
382 387
383 388 def visitdir(self, dir):
384 389 if self.prefix() and dir in self._fileset:
385 390 return 'all'
386 391 if dir in self._excluderoots:
387 392 return False
388 393 if self._includeroots or self._includedirs:
389 394 if (not self._anyincludepats and
390 395 not self._excluderoots and
391 396 dir in self._includeroots):
392 397 # The condition above is essentially self.prefix() for includes
393 398 return 'all'
394 399 if ('.' not in self._includeroots and
395 400 dir not in self._includeroots and
396 401 dir not in self._includedirs and
397 402 not any(parent in self._includeroots
398 403 for parent in util.finddirs(dir))):
399 404 return False
400 405 return (not self._fileset or
401 406 '.' in self._fileset or
402 407 dir in self._fileset or
403 408 dir in self._dirs or
404 409 any(parentdir in self._fileset
405 410 for parentdir in util.finddirs(dir)))
406 411
407 412 def anypats(self):
408 413 return self._anypats
409 414
410 415 def always(self):
411 416 return self._always
412 417
413 418 def isexact(self):
414 419 return self.matchfn == self.exact
415 420
416 421 def __repr__(self):
417 422 return ('<matcher files=%r, patterns=%r, includes=%r, excludes=%r>' %
418 423 (self._files, self.patternspat, self.includepat,
419 424 self.excludepat))
420 425
426 class differencematcher(basematcher):
427 '''Composes two matchers by matching if the first matches and the second
428 does not. Well, almost... If the user provides a pattern like "-X foo foo",
429 Mercurial actually does match "foo" against that. That's because exact
430 matches are treated specially. So, since this differencematcher is used for
431 excludes, it needs to special-case exact matching.
432
433 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
434 traversedir) are ignored.
435
436 TODO: If we want to keep the behavior described above for exact matches, we
437 should consider instead treating the above case something like this:
438 union(exact(foo), difference(pattern(foo), include(foo)))
439 '''
440 def __init__(self, m1, m2):
441 super(differencematcher, self).__init__(m1._root, m1._cwd)
442 self._m1 = m1
443 self._m2 = m2
444 self.bad = m1.bad
445 self.explicitdir = m1.explicitdir
446 self.traversedir = m1.traversedir
447
448 def matchfn(self, f):
449 return self._m1(f) and (not self._m2(f) or self._m1.exact(f))
450
451 @propertycache
452 def _files(self):
453 if self.isexact():
454 return [f for f in self._m1.files() if self(f)]
455 # If m1 is not an exact matcher, we can't easily figure out the set of
456 # files, because its files() are not always files. For example, if
457 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
458 # want to remove "dir" from the set even though it would match m2,
459 # because the "dir" in m1 may not be a file.
460 return self._m1.files()
461
462 def visitdir(self, dir):
463 if self._m2.visitdir(dir) == 'all':
464 # There's a bug here: If m1 matches file 'dir/file' and m2 excludes
465 # 'dir' (recursively), we should still visit 'dir' due to the
466 # exception we have for exact matches.
467 return False
468 return bool(self._m1.visitdir(dir))
469
470 def isexact(self):
471 return self._m1.isexact()
472
473 def anypats(self):
474 return self._m1.anypats() or self._m2.anypats()
475
476 def prefix(self):
477 return not self.always() and not self.isexact() and not self.anypats()
478
479 def __repr__(self):
480 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
481
421 482 class subdirmatcher(basematcher):
422 483 """Adapt a matcher to work on a subdirectory only.
423 484
424 485 The paths are remapped to remove/insert the path as needed:
425 486
426 487 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
427 488 >>> m2 = subdirmatcher('sub', m1)
428 489 >>> bool(m2('a.txt'))
429 490 False
430 491 >>> bool(m2('b.txt'))
431 492 True
432 493 >>> bool(m2.matchfn('a.txt'))
433 494 False
434 495 >>> bool(m2.matchfn('b.txt'))
435 496 True
436 497 >>> m2.files()
437 498 ['b.txt']
438 499 >>> m2.exact('b.txt')
439 500 True
440 501 >>> util.pconvert(m2.rel('b.txt'))
441 502 'sub/b.txt'
442 503 >>> def bad(f, msg):
443 504 ... print "%s: %s" % (f, msg)
444 505 >>> m1.bad = bad
445 506 >>> m2.bad('x.txt', 'No such file')
446 507 sub/x.txt: No such file
447 508 >>> m2.abs('c.txt')
448 509 'sub/c.txt'
449 510 """
450 511
451 512 def __init__(self, path, matcher):
452 513 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
453 514 self._path = path
454 515 self._matcher = matcher
455 516 self._always = matcher.always()
456 517
457 518 self._files = [f[len(path) + 1:] for f in matcher._files
458 519 if f.startswith(path + "/")]
459 520
460 521 # If the parent repo had a path to this subrepo and the matcher is
461 522 # a prefix matcher, this submatcher always matches.
462 523 if matcher.prefix():
463 524 self._always = any(f == path for f in matcher._files)
464 525
465 526 def bad(self, f, msg):
466 527 self._matcher.bad(self._path + "/" + f, msg)
467 528
468 529 def abs(self, f):
469 530 return self._matcher.abs(self._path + "/" + f)
470 531
471 532 def rel(self, f):
472 533 return self._matcher.rel(self._path + "/" + f)
473 534
474 535 def uipath(self, f):
475 536 return self._matcher.uipath(self._path + "/" + f)
476 537
477 538 def matchfn(self, f):
478 539 # Some information is lost in the superclass's constructor, so we
479 540 # can not accurately create the matching function for the subdirectory
480 541 # from the inputs. Instead, we override matchfn() and visitdir() to
481 542 # call the original matcher with the subdirectory path prepended.
482 543 return self._matcher.matchfn(self._path + "/" + f)
483 544
484 545 def visitdir(self, dir):
485 546 if dir == '.':
486 547 dir = self._path
487 548 else:
488 549 dir = self._path + "/" + dir
489 550 return self._matcher.visitdir(dir)
490 551
491 552 def always(self):
492 553 return self._always
493 554
494 555 def anypats(self):
495 556 return self._matcher.anypats()
496 557
497 558 def patkind(pattern, default=None):
498 559 '''If pattern is 'kind:pat' with a known kind, return kind.'''
499 560 return _patsplit(pattern, default)[0]
500 561
501 562 def _patsplit(pattern, default):
502 563 """Split a string into the optional pattern kind prefix and the actual
503 564 pattern."""
504 565 if ':' in pattern:
505 566 kind, pat = pattern.split(':', 1)
506 567 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
507 568 'listfile', 'listfile0', 'set', 'include', 'subinclude',
508 569 'rootfilesin'):
509 570 return kind, pat
510 571 return default, pattern
511 572
512 573 def _globre(pat):
513 574 r'''Convert an extended glob string to a regexp string.
514 575
515 576 >>> print _globre(r'?')
516 577 .
517 578 >>> print _globre(r'*')
518 579 [^/]*
519 580 >>> print _globre(r'**')
520 581 .*
521 582 >>> print _globre(r'**/a')
522 583 (?:.*/)?a
523 584 >>> print _globre(r'a/**/b')
524 585 a\/(?:.*/)?b
525 586 >>> print _globre(r'[a*?!^][^b][!c]')
526 587 [a*?!^][\^b][^c]
527 588 >>> print _globre(r'{a,b}')
528 589 (?:a|b)
529 590 >>> print _globre(r'.\*\?')
530 591 \.\*\?
531 592 '''
532 593 i, n = 0, len(pat)
533 594 res = ''
534 595 group = 0
535 596 escape = util.re.escape
536 597 def peek():
537 598 return i < n and pat[i:i + 1]
538 599 while i < n:
539 600 c = pat[i:i + 1]
540 601 i += 1
541 602 if c not in '*?[{},\\':
542 603 res += escape(c)
543 604 elif c == '*':
544 605 if peek() == '*':
545 606 i += 1
546 607 if peek() == '/':
547 608 i += 1
548 609 res += '(?:.*/)?'
549 610 else:
550 611 res += '.*'
551 612 else:
552 613 res += '[^/]*'
553 614 elif c == '?':
554 615 res += '.'
555 616 elif c == '[':
556 617 j = i
557 618 if j < n and pat[j:j + 1] in '!]':
558 619 j += 1
559 620 while j < n and pat[j:j + 1] != ']':
560 621 j += 1
561 622 if j >= n:
562 623 res += '\\['
563 624 else:
564 625 stuff = pat[i:j].replace('\\','\\\\')
565 626 i = j + 1
566 627 if stuff[0:1] == '!':
567 628 stuff = '^' + stuff[1:]
568 629 elif stuff[0:1] == '^':
569 630 stuff = '\\' + stuff
570 631 res = '%s[%s]' % (res, stuff)
571 632 elif c == '{':
572 633 group += 1
573 634 res += '(?:'
574 635 elif c == '}' and group:
575 636 res += ')'
576 637 group -= 1
577 638 elif c == ',' and group:
578 639 res += '|'
579 640 elif c == '\\':
580 641 p = peek()
581 642 if p:
582 643 i += 1
583 644 res += escape(p)
584 645 else:
585 646 res += escape(c)
586 647 else:
587 648 res += escape(c)
588 649 return res
589 650
590 651 def _regex(kind, pat, globsuffix):
591 652 '''Convert a (normalized) pattern of any kind into a regular expression.
592 653 globsuffix is appended to the regexp of globs.'''
593 654 if not pat:
594 655 return ''
595 656 if kind == 're':
596 657 return pat
597 658 if kind == 'path':
598 659 if pat == '.':
599 660 return ''
600 661 return '^' + util.re.escape(pat) + '(?:/|$)'
601 662 if kind == 'rootfilesin':
602 663 if pat == '.':
603 664 escaped = ''
604 665 else:
605 666 # Pattern is a directory name.
606 667 escaped = util.re.escape(pat) + '/'
607 668 # Anything after the pattern must be a non-directory.
608 669 return '^' + escaped + '[^/]+$'
609 670 if kind == 'relglob':
610 671 return '(?:|.*/)' + _globre(pat) + globsuffix
611 672 if kind == 'relpath':
612 673 return util.re.escape(pat) + '(?:/|$)'
613 674 if kind == 'relre':
614 675 if pat.startswith('^'):
615 676 return pat
616 677 return '.*' + pat
617 678 return _globre(pat) + globsuffix
618 679
619 680 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
620 681 '''Return regexp string and a matcher function for kindpats.
621 682 globsuffix is appended to the regexp of globs.'''
622 683 matchfuncs = []
623 684
624 685 subincludes, kindpats = _expandsubinclude(kindpats, root)
625 686 if subincludes:
626 687 submatchers = {}
627 688 def matchsubinclude(f):
628 689 for prefix, matcherargs in subincludes:
629 690 if f.startswith(prefix):
630 691 mf = submatchers.get(prefix)
631 692 if mf is None:
632 693 mf = match(*matcherargs)
633 694 submatchers[prefix] = mf
634 695
635 696 if mf(f[len(prefix):]):
636 697 return True
637 698 return False
638 699 matchfuncs.append(matchsubinclude)
639 700
640 701 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
641 702 if fset:
642 703 matchfuncs.append(fset.__contains__)
643 704
644 705 regex = ''
645 706 if kindpats:
646 707 regex, mf = _buildregexmatch(kindpats, globsuffix)
647 708 matchfuncs.append(mf)
648 709
649 710 if len(matchfuncs) == 1:
650 711 return regex, matchfuncs[0]
651 712 else:
652 713 return regex, lambda f: any(mf(f) for mf in matchfuncs)
653 714
654 715 def _buildregexmatch(kindpats, globsuffix):
655 716 """Build a match function from a list of kinds and kindpats,
656 717 return regexp string and a matcher function."""
657 718 try:
658 719 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
659 720 for (k, p, s) in kindpats])
660 721 if len(regex) > 20000:
661 722 raise OverflowError
662 723 return regex, _rematcher(regex)
663 724 except OverflowError:
664 725 # We're using a Python with a tiny regex engine and we
665 726 # made it explode, so we'll divide the pattern list in two
666 727 # until it works
667 728 l = len(kindpats)
668 729 if l < 2:
669 730 raise
670 731 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
671 732 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
672 733 return regex, lambda s: a(s) or b(s)
673 734 except re.error:
674 735 for k, p, s in kindpats:
675 736 try:
676 737 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
677 738 except re.error:
678 739 if s:
679 740 raise error.Abort(_("%s: invalid pattern (%s): %s") %
680 741 (s, k, p))
681 742 else:
682 743 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
683 744 raise error.Abort(_("invalid pattern"))
684 745
685 746 def _patternrootsanddirs(kindpats):
686 747 '''Returns roots and directories corresponding to each pattern.
687 748
688 749 This calculates the roots and directories exactly matching the patterns and
689 750 returns a tuple of (roots, dirs) for each. It does not return other
690 751 directories which may also need to be considered, like the parent
691 752 directories.
692 753 '''
693 754 r = []
694 755 d = []
695 756 for kind, pat, source in kindpats:
696 757 if kind == 'glob': # find the non-glob prefix
697 758 root = []
698 759 for p in pat.split('/'):
699 760 if '[' in p or '{' in p or '*' in p or '?' in p:
700 761 break
701 762 root.append(p)
702 763 r.append('/'.join(root) or '.')
703 764 elif kind in ('relpath', 'path'):
704 765 r.append(pat or '.')
705 766 elif kind in ('rootfilesin',):
706 767 d.append(pat or '.')
707 768 else: # relglob, re, relre
708 769 r.append('.')
709 770 return r, d
710 771
711 772 def _roots(kindpats):
712 773 '''Returns root directories to match recursively from the given patterns.'''
713 774 roots, dirs = _patternrootsanddirs(kindpats)
714 775 return roots
715 776
716 777 def _rootsanddirs(kindpats):
717 778 '''Returns roots and exact directories from patterns.
718 779
719 780 roots are directories to match recursively, whereas exact directories should
720 781 be matched non-recursively. The returned (roots, dirs) tuple will also
721 782 include directories that need to be implicitly considered as either, such as
722 783 parent directories.
723 784
724 785 >>> _rootsanddirs(\
725 786 [('glob', 'g/h/*', ''), ('glob', 'g/h', ''), ('glob', 'g*', '')])
726 787 (['g/h', 'g/h', '.'], ['g', '.'])
727 788 >>> _rootsanddirs(\
728 789 [('rootfilesin', 'g/h', ''), ('rootfilesin', '', '')])
729 790 ([], ['g/h', '.', 'g', '.'])
730 791 >>> _rootsanddirs(\
731 792 [('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
732 793 (['r', 'p/p', '.'], ['p', '.'])
733 794 >>> _rootsanddirs(\
734 795 [('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
735 796 (['.', '.', '.'], ['.'])
736 797 '''
737 798 r, d = _patternrootsanddirs(kindpats)
738 799
739 800 # Append the parents as non-recursive/exact directories, since they must be
740 801 # scanned to get to either the roots or the other exact directories.
741 802 d.extend(util.dirs(d))
742 803 d.extend(util.dirs(r))
743 804 # util.dirs() does not include the root directory, so add it manually
744 805 d.append('.')
745 806
746 807 return r, d
747 808
748 809 def _explicitfiles(kindpats):
749 810 '''Returns the potential explicit filenames from the patterns.
750 811
751 812 >>> _explicitfiles([('path', 'foo/bar', '')])
752 813 ['foo/bar']
753 814 >>> _explicitfiles([('rootfilesin', 'foo/bar', '')])
754 815 []
755 816 '''
756 817 # Keep only the pattern kinds where one can specify filenames (vs only
757 818 # directory names).
758 819 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
759 820 return _roots(filable)
760 821
761 822 def _anypats(kindpats):
762 823 for kind, pat, source in kindpats:
763 824 if kind in ('glob', 're', 'relglob', 'relre', 'set', 'rootfilesin'):
764 825 return True
765 826
766 827 _commentre = None
767 828
768 829 def readpatternfile(filepath, warn, sourceinfo=False):
769 830 '''parse a pattern file, returning a list of
770 831 patterns. These patterns should be given to compile()
771 832 to be validated and converted into a match function.
772 833
773 834 trailing white space is dropped.
774 835 the escape character is backslash.
775 836 comments start with #.
776 837 empty lines are skipped.
777 838
778 839 lines can be of the following formats:
779 840
780 841 syntax: regexp # defaults following lines to non-rooted regexps
781 842 syntax: glob # defaults following lines to non-rooted globs
782 843 re:pattern # non-rooted regular expression
783 844 glob:pattern # non-rooted glob
784 845 pattern # pattern of the current default type
785 846
786 847 if sourceinfo is set, returns a list of tuples:
787 848 (pattern, lineno, originalline). This is useful to debug ignore patterns.
788 849 '''
789 850
790 851 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
791 852 'include': 'include', 'subinclude': 'subinclude'}
792 853 syntax = 'relre:'
793 854 patterns = []
794 855
795 856 fp = open(filepath, 'rb')
796 857 for lineno, line in enumerate(util.iterfile(fp), start=1):
797 858 if "#" in line:
798 859 global _commentre
799 860 if not _commentre:
800 861 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
801 862 # remove comments prefixed by an even number of escapes
802 863 m = _commentre.search(line)
803 864 if m:
804 865 line = line[:m.end(1)]
805 866 # fixup properly escaped comments that survived the above
806 867 line = line.replace("\\#", "#")
807 868 line = line.rstrip()
808 869 if not line:
809 870 continue
810 871
811 872 if line.startswith('syntax:'):
812 873 s = line[7:].strip()
813 874 try:
814 875 syntax = syntaxes[s]
815 876 except KeyError:
816 877 if warn:
817 878 warn(_("%s: ignoring invalid syntax '%s'\n") %
818 879 (filepath, s))
819 880 continue
820 881
821 882 linesyntax = syntax
822 883 for s, rels in syntaxes.iteritems():
823 884 if line.startswith(rels):
824 885 linesyntax = rels
825 886 line = line[len(rels):]
826 887 break
827 888 elif line.startswith(s+':'):
828 889 linesyntax = rels
829 890 line = line[len(s) + 1:]
830 891 break
831 892 if sourceinfo:
832 893 patterns.append((linesyntax + line, lineno, line))
833 894 else:
834 895 patterns.append(linesyntax + line)
835 896 fp.close()
836 897 return patterns
@@ -1,518 +1,518 b''
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: <matcher files=[], patterns=None, includes=None, excludes=None>
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: <matcher files=[], patterns=None, includes='(?:)', excludes=None>
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: <matcher files=[], patterns=None, includes=None, excludes=None>
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 matcher: <matcher files=[], patterns=None, includes=None, excludes='(?:beans(?:/|$))'>
79 matcher: <differencematcher m1=<matcher files=[], patterns=None, includes=None, excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans(?:/|$))', excludes=None>>
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: <matcher files=[], patterns=None, includes='(?:mammals\\/[^/]*k(?:/|$))', excludes=None>
89 89 f mammals/skunk skunk
90 90 $ hg debugwalk -I 'glob:*k'
91 91 matcher: <matcher files=[], patterns=None, includes='(?:mammals\\/[^/]*k(?:/|$))', excludes=None>
92 92 f mammals/skunk skunk
93 93 $ hg debugwalk -I 'relglob:*k'
94 94 matcher: <matcher files=[], patterns=None, includes='(?:(?:|.*/)[^/]*k(?:/|$))', excludes=None>
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: <matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes='(?:(?:|.*/)[^/]*k(?:/|$))', excludes=None>
100 100 f mammals/skunk skunk
101 101 $ hg debugwalk -I 're:.*k$'
102 102 matcher: <matcher files=[], patterns=None, includes='(?:.*k$)', excludes=None>
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: <matcher files=[], patterns=None, includes='(?:.*.*k$)', excludes=None>
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 112 matcher: <matcher files=[], patterns=None, includes='(?:^beans(?:/|$))', excludes=None>
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: <matcher files=[], patterns=None, includes='(?:beans(?:/|$))', excludes=None>
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 129 matcher: <matcher files=[], patterns='(?:^[^/]+$)', includes=None, excludes=None>
130 130 f fennel ../fennel
131 131 f fenugreek ../fenugreek
132 132 f fiddlehead ../fiddlehead
133 133 $ hg debugwalk -I 'rootfilesin:'
134 134 matcher: <matcher files=[], patterns=None, includes='(?:^[^/]+$)', excludes=None>
135 135 f fennel ../fennel
136 136 f fenugreek ../fenugreek
137 137 f fiddlehead ../fiddlehead
138 138 $ hg debugwalk 'rootfilesin:.'
139 139 matcher: <matcher files=[], patterns='(?:^[^/]+$)', includes=None, excludes=None>
140 140 f fennel ../fennel
141 141 f fenugreek ../fenugreek
142 142 f fiddlehead ../fiddlehead
143 143 $ hg debugwalk -I 'rootfilesin:.'
144 144 matcher: <matcher files=[], patterns=None, includes='(?:^[^/]+$)', excludes=None>
145 145 f fennel ../fennel
146 146 f fenugreek ../fenugreek
147 147 f fiddlehead ../fiddlehead
148 148 $ hg debugwalk -X 'rootfilesin:'
149 matcher: <matcher files=[], patterns=None, includes=None, excludes='(?:^[^/]+$)'>
149 matcher: <differencematcher m1=<matcher files=[], patterns=None, includes=None, excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:^[^/]+$)', excludes=None>>
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 161 matcher: <matcher files=[], patterns='(?:^fennel/[^/]+$)', includes=None, excludes=None>
162 162 $ hg debugwalk -I 'rootfilesin:fennel'
163 163 matcher: <matcher files=[], patterns=None, includes='(?:^fennel/[^/]+$)', excludes=None>
164 164 $ hg debugwalk 'rootfilesin:skunk'
165 165 matcher: <matcher files=[], patterns='(?:^skunk/[^/]+$)', includes=None, excludes=None>
166 166 $ hg debugwalk -I 'rootfilesin:skunk'
167 167 matcher: <matcher files=[], patterns=None, includes='(?:^skunk/[^/]+$)', excludes=None>
168 168 $ hg debugwalk 'rootfilesin:beans'
169 169 matcher: <matcher files=[], patterns='(?:^beans/[^/]+$)', includes=None, excludes=None>
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 177 matcher: <matcher files=[], patterns=None, includes='(?:^beans/[^/]+$)', excludes=None>
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 185 matcher: <matcher files=[], patterns='(?:^mammals/[^/]+$)', includes=None, excludes=None>
186 186 f mammals/skunk skunk
187 187 $ hg debugwalk -I 'rootfilesin:mammals'
188 188 matcher: <matcher files=[], patterns=None, includes='(?:^mammals/[^/]+$)', excludes=None>
189 189 f mammals/skunk skunk
190 190 $ hg debugwalk 'rootfilesin:mammals/'
191 191 matcher: <matcher files=[], patterns='(?:^mammals/[^/]+$)', includes=None, excludes=None>
192 192 f mammals/skunk skunk
193 193 $ hg debugwalk -I 'rootfilesin:mammals/'
194 194 matcher: <matcher files=[], patterns=None, includes='(?:^mammals/[^/]+$)', excludes=None>
195 195 f mammals/skunk skunk
196 196 $ hg debugwalk -X 'rootfilesin:mammals'
197 matcher: <matcher files=[], patterns=None, includes=None, excludes='(?:^mammals/[^/]+$)'>
197 matcher: <differencematcher m1=<matcher files=[], patterns=None, includes=None, excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:^mammals/[^/]+$)', excludes=None>>
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: <matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes=None, excludes=None>
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: <matcher files=[], patterns=None, includes='(?:mammals(?:/|$))', excludes=None>
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: <matcher files=['mammals/Procyonidae'], patterns='(?:mammals\\/Procyonidae(?:/|$))', includes=None, excludes=None>
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: <matcher files=['mammals/Procyonidae'], patterns='(?:mammals\\/Procyonidae(?:/|$))', includes=None, excludes=None>
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: <matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes=None, excludes=None>
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: <matcher files=['beans'], patterns='(?:beans(?:/|$))', includes=None, excludes=None>
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: <matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes=None, excludes=None>
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: <matcher files=[], patterns=None, includes='(?:beans(?:/|$))', excludes=None>
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: <matcher files=[], patterns=None, includes='(?:(?:[^/]*|(?:b|m)[^/]*\\/[^/]*)k(?:/|$))', excludes=None>
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: <matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes='(?:beans(?:/|$))', excludes=None>
280 280 $ hg debugwalk -Inon-existent
281 281 matcher: <matcher files=[], patterns=None, includes='(?:non\\-existent(?:/|$))', excludes=None>
282 282 $ hg debugwalk -Inon-existent -Ibeans/black
283 283 matcher: <matcher files=[], patterns=None, includes='(?:non\\-existent(?:/|$)|beans\\/black(?:/|$))', excludes=None>
284 284 f beans/black beans/black
285 285 $ hg debugwalk -Ibeans beans/black
286 286 matcher: <matcher files=['beans/black'], patterns='(?:beans\\/black(?:/|$))', includes='(?:beans(?:/|$))', excludes=None>
287 287 f beans/black beans/black exact
288 288 $ hg debugwalk -Ibeans/black beans
289 289 matcher: <matcher files=['beans'], patterns='(?:beans(?:/|$))', includes='(?:beans\\/black(?:/|$))', excludes=None>
290 290 f beans/black beans/black
291 291 $ hg debugwalk -Xbeans/black beans
292 matcher: <matcher files=['beans'], patterns='(?:beans(?:/|$))', includes=None, excludes='(?:beans\\/black(?:/|$))'>
292 matcher: <differencematcher m1=<matcher files=['beans'], patterns='(?:beans(?:/|$))', includes=None, excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))', excludes=None>>
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 matcher: <matcher files=[], patterns=None, includes='(?:beans(?:/|$))', excludes='(?:beans\\/black(?:/|$))'>
299 matcher: <differencematcher m1=<matcher files=[], patterns=None, includes='(?:beans(?:/|$))', excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))', excludes=None>>
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 matcher: <matcher files=['beans/black'], patterns='(?:beans\\/black(?:/|$))', includes=None, excludes='(?:beans\\/black(?:/|$))'>
306 matcher: <differencematcher m1=<matcher files=['beans/black'], patterns='(?:beans\\/black(?:/|$))', includes=None, excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))', excludes=None>>
307 307 f beans/black beans/black exact
308 308 $ hg debugwalk -Xbeans/black -Ibeans/black
309 matcher: <matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))', excludes='(?:beans\\/black(?:/|$))'>
309 matcher: <differencematcher m1=<matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))', excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))', excludes=None>>
310 310 $ hg debugwalk -Xbeans beans/black
311 matcher: <matcher files=['beans/black'], patterns='(?:beans\\/black(?:/|$))', includes=None, excludes='(?:beans(?:/|$))'>
311 matcher: <differencematcher m1=<matcher files=['beans/black'], patterns='(?:beans\\/black(?:/|$))', includes=None, excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans(?:/|$))', excludes=None>>
312 312 f beans/black beans/black exact
313 313 $ hg debugwalk -Xbeans -Ibeans/black
314 matcher: <matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))', excludes='(?:beans(?:/|$))'>
314 matcher: <differencematcher m1=<matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))', excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans(?:/|$))', excludes=None>>
315 315 $ hg debugwalk 'glob:mammals/../beans/b*'
316 316 matcher: <matcher files=['beans'], patterns='(?:beans\\/b[^/]*$)', includes=None, excludes=None>
317 317 f beans/black beans/black
318 318 f beans/borlotti beans/borlotti
319 319 $ hg debugwalk '-X*/Procyonidae' mammals
320 matcher: <matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes=None, excludes='(?:[^/]*\\/Procyonidae(?:/|$))'>
320 matcher: <differencematcher m1=<matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes=None, excludes=None>, m2=<matcher files=[], patterns=None, includes='(?:[^/]*\\/Procyonidae(?:/|$))', excludes=None>>
321 321 f mammals/skunk mammals/skunk
322 322 $ hg debugwalk path:mammals
323 323 matcher: <matcher files=['mammals'], patterns='(?:^mammals(?:/|$))', includes=None, excludes=None>
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: <matcher files=['beans'], patterns='(?:beans(?:/|$))', includes=None, excludes=None>
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: <matcher files=['.'], patterns='(?:[^/]*$)', includes=None, excludes=None>
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: <matcher files=['.'], patterns='(?:[^/]*$)', includes=None, excludes=None>
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: <matcher files=['glob'], patterns='(?:glob$)', includes=None, excludes=None>
381 381 glob: No such file or directory
382 382 $ hg debugwalk glob:glob:glob
383 383 matcher: <matcher files=['glob:glob'], patterns='(?:glob\\:glob$)', includes=None, excludes=None>
384 384 f glob:glob glob:glob exact
385 385 $ hg debugwalk path:glob:glob
386 386 matcher: <matcher files=['glob:glob'], patterns='(?:^glob\\:glob(?:/|$))', includes=None, excludes=None>
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: <matcher files=['.'], patterns='(?:.*e$)', includes=None, excludes=None>
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: <matcher files=['.'], patterns='(?:.*[kb]$)', includes=None, excludes=None>
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 405 matcher: <matcher files=['beans/black'], patterns='(?:^beans\\/black(?:/|$))', includes=None, excludes=None>
406 406 f beans/black beans/black exact
407 407 $ hg debugwalk path:beans//black
408 408 matcher: <matcher files=['beans/black'], patterns='(?:^beans\\/black(?:/|$))', includes=None, excludes=None>
409 409 f beans/black beans/black exact
410 410
411 411 $ hg debugwalk relglob:Procyonidae
412 412 matcher: <matcher files=['.'], patterns='(?:(?:|.*/)Procyonidae$)', includes=None, excludes=None>
413 413 $ hg debugwalk 'relglob:Procyonidae/**'
414 414 matcher: <matcher files=['.'], patterns='(?:(?:|.*/)Procyonidae\\/.*$)', includes=None, excludes=None>
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: <matcher files=['.', 'fennel'], patterns='(?:(?:|.*/)Procyonidae\\/.*$|fennel(?:/|$))', includes=None, excludes=None>
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: <matcher files=['beans', 'beans'], patterns='(?:beans(?:/|$)|beans\\/[^/]*$)', includes=None, excludes=None>
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: <matcher files=['.'], patterns='(?:mamm.*$)', includes=None, excludes=None>
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: <matcher files=['.', 'fennel'], patterns='(?:mamm.*$|fennel(?:/|$))', includes=None, excludes=None>
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: <matcher files=['.'], patterns='(?:j[^/]*$)', includes=None, excludes=None>
447 447 $ hg debugwalk NOEXIST
448 448 matcher: <matcher files=['NOEXIST'], patterns='(?:NOEXIST(?:/|$))', includes=None, excludes=None>
449 449 NOEXIST: * (glob)
450 450
451 451 #if fifo
452 452 $ mkfifo fifo
453 453 $ hg debugwalk fifo
454 454 matcher: <matcher files=['fifo'], patterns='(?:fifo(?:/|$))', includes=None, excludes=None>
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: <matcher files=['fenugreek'], patterns='(?:fenugreek(?:/|$))', includes=None, excludes=None>
461 461 f fenugreek fenugreek exact
462 462 $ hg rm fenugreek
463 463 $ hg debugwalk fenugreek
464 464 matcher: <matcher files=['fenugreek'], patterns='(?:fenugreek(?:/|$))', includes=None, excludes=None>
465 465 f fenugreek fenugreek exact
466 466 $ touch new
467 467 $ hg debugwalk new
468 468 matcher: <matcher files=['new'], patterns='(?:new(?:/|$))', includes=None, excludes=None>
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: <matcher files=['ignored'], patterns='(?:ignored(?:/|$))', includes=None, excludes=None>
476 476 $ hg debugwalk ignored/file
477 477 matcher: <matcher files=['ignored/file'], patterns='(?:ignored\\/file(?:/|$))', includes=None, excludes=None>
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: <matcher files=[], patterns=None, includes='(?:fenugreek(?:/|$)|new(?:/|$))', excludes=None>
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: <matcher files=[], patterns=None, includes='(?:fenugreek(?:/|$)|new(?:/|$)|mammals\\/skunk(?:/|$))', excludes=None>
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: <matcher files=['mammals/skunk'], patterns='(?:mammals\\/skunk(?:/|$))', includes=None, excludes=None>
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: <matcher files=['mammals/skunk'], patterns='(?:mammals\\/skunk(?:/|$))', includes=None, excludes=None>
502 502 f mammals/skunk ../t/mammals/skunk exact
503 503 $ hg debugwalk --cwd ../t mammals/skunk
504 504 matcher: <matcher files=['mammals/skunk'], patterns='(?:mammals\\/skunk(?:/|$))', includes=None, excludes=None>
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