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