##// END OF EJS Templates
match: drop unnecessary wrapping of regex in group...
Martin von Zweigbergk -
r40818:3984409e default
parent child Browse files
Show More
@@ -1,1412 +1,1411 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, print_function
9 9
10 10 import copy
11 11 import itertools
12 12 import os
13 13 import re
14 14
15 15 from .i18n import _
16 16 from . import (
17 17 encoding,
18 18 error,
19 19 pathutil,
20 20 pycompat,
21 21 util,
22 22 )
23 23 from .utils import (
24 24 stringutil,
25 25 )
26 26
27 27 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
28 28 'listfile', 'listfile0', 'set', 'include', 'subinclude',
29 29 'rootfilesin')
30 30 cwdrelativepatternkinds = ('relpath', 'glob')
31 31
32 32 propertycache = util.propertycache
33 33
34 34 def _rematcher(regex):
35 35 '''compile the regexp with the best available regexp engine and return a
36 36 matcher function'''
37 37 m = util.re.compile(regex)
38 38 try:
39 39 # slightly faster, provided by facebook's re2 bindings
40 40 return m.test_match
41 41 except AttributeError:
42 42 return m.match
43 43
44 44 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
45 45 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
46 46 matchers = []
47 47 other = []
48 48
49 49 for kind, pat, source in kindpats:
50 50 if kind == 'set':
51 51 if not ctx:
52 52 raise error.ProgrammingError("fileset expression with no "
53 53 "context")
54 54 matchers.append(ctx.matchfileset(pat, badfn=badfn))
55 55
56 56 if listsubrepos:
57 57 for subpath in ctx.substate:
58 58 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
59 59 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
60 60 matchers.append(pm)
61 61
62 62 continue
63 63 other.append((kind, pat, source))
64 64 return matchers, other
65 65
66 66 def _expandsubinclude(kindpats, root):
67 67 '''Returns the list of subinclude matcher args and the kindpats without the
68 68 subincludes in it.'''
69 69 relmatchers = []
70 70 other = []
71 71
72 72 for kind, pat, source in kindpats:
73 73 if kind == 'subinclude':
74 74 sourceroot = pathutil.dirname(util.normpath(source))
75 75 pat = util.pconvert(pat)
76 76 path = pathutil.join(sourceroot, pat)
77 77
78 78 newroot = pathutil.dirname(path)
79 79 matcherargs = (newroot, '', [], ['include:%s' % path])
80 80
81 81 prefix = pathutil.canonpath(root, root, newroot)
82 82 if prefix:
83 83 prefix += '/'
84 84 relmatchers.append((prefix, matcherargs))
85 85 else:
86 86 other.append((kind, pat, source))
87 87
88 88 return relmatchers, other
89 89
90 90 def _kindpatsalwaysmatch(kindpats):
91 91 """"Checks whether the kindspats match everything, as e.g.
92 92 'relpath:.' does.
93 93 """
94 94 for kind, pat, source in kindpats:
95 95 if pat != '' or kind not in ['relpath', 'glob']:
96 96 return False
97 97 return True
98 98
99 99 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
100 100 listsubrepos=False, badfn=None):
101 101 matchers = []
102 102 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
103 103 listsubrepos=listsubrepos, badfn=badfn)
104 104 if kindpats:
105 105 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
106 106 badfn=badfn)
107 107 matchers.append(m)
108 108 if fms:
109 109 matchers.extend(fms)
110 110 if not matchers:
111 111 return nevermatcher(root, cwd, badfn=badfn)
112 112 if len(matchers) == 1:
113 113 return matchers[0]
114 114 return unionmatcher(matchers)
115 115
116 116 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
117 117 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
118 118 badfn=None, icasefs=False):
119 119 """build an object to match a set of file patterns
120 120
121 121 arguments:
122 122 root - the canonical root of the tree you're matching against
123 123 cwd - the current working directory, if relevant
124 124 patterns - patterns to find
125 125 include - patterns to include (unless they are excluded)
126 126 exclude - patterns to exclude (even if they are included)
127 127 default - if a pattern in patterns has no explicit type, assume this one
128 128 exact - patterns are actually filenames (include/exclude still apply)
129 129 warn - optional function used for printing warnings
130 130 badfn - optional bad() callback for this matcher instead of the default
131 131 icasefs - make a matcher for wdir on case insensitive filesystems, which
132 132 normalizes the given patterns to the case in the filesystem
133 133
134 134 a pattern is one of:
135 135 'glob:<glob>' - a glob relative to cwd
136 136 're:<regexp>' - a regular expression
137 137 'path:<path>' - a path relative to repository root, which is matched
138 138 recursively
139 139 'rootfilesin:<path>' - a path relative to repository root, which is
140 140 matched non-recursively (will not match subdirectories)
141 141 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
142 142 'relpath:<path>' - a path relative to cwd
143 143 'relre:<regexp>' - a regexp that needn't match the start of a name
144 144 'set:<fileset>' - a fileset expression
145 145 'include:<path>' - a file of patterns to read and include
146 146 'subinclude:<path>' - a file of patterns to match against files under
147 147 the same directory
148 148 '<something>' - a pattern of the specified default type
149 149 """
150 150 normalize = _donormalize
151 151 if icasefs:
152 152 if exact:
153 153 raise error.ProgrammingError("a case-insensitive exact matcher "
154 154 "doesn't make sense")
155 155 dirstate = ctx.repo().dirstate
156 156 dsnormalize = dirstate.normalize
157 157
158 158 def normalize(patterns, default, root, cwd, auditor, warn):
159 159 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
160 160 kindpats = []
161 161 for kind, pats, source in kp:
162 162 if kind not in ('re', 'relre'): # regex can't be normalized
163 163 p = pats
164 164 pats = dsnormalize(pats)
165 165
166 166 # Preserve the original to handle a case only rename.
167 167 if p != pats and p in dirstate:
168 168 kindpats.append((kind, p, source))
169 169
170 170 kindpats.append((kind, pats, source))
171 171 return kindpats
172 172
173 173 if exact:
174 174 m = exactmatcher(root, cwd, patterns, badfn)
175 175 elif patterns:
176 176 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
177 177 if _kindpatsalwaysmatch(kindpats):
178 178 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
179 179 else:
180 180 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
181 181 ctx=ctx, listsubrepos=listsubrepos,
182 182 badfn=badfn)
183 183 else:
184 184 # It's a little strange that no patterns means to match everything.
185 185 # Consider changing this to match nothing (probably using nevermatcher).
186 186 m = alwaysmatcher(root, cwd, badfn)
187 187
188 188 if include:
189 189 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
190 190 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
191 191 listsubrepos=listsubrepos, badfn=None)
192 192 m = intersectmatchers(m, im)
193 193 if exclude:
194 194 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
195 195 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
196 196 listsubrepos=listsubrepos, badfn=None)
197 197 m = differencematcher(m, em)
198 198 return m
199 199
200 200 def exact(root, cwd, files, badfn=None):
201 201 return exactmatcher(root, cwd, files, badfn=badfn)
202 202
203 203 def always(root, cwd):
204 204 return alwaysmatcher(root, cwd)
205 205
206 206 def never(root, cwd):
207 207 return nevermatcher(root, cwd)
208 208
209 209 def badmatch(match, badfn):
210 210 """Make a copy of the given matcher, replacing its bad method with the given
211 211 one.
212 212 """
213 213 m = copy.copy(match)
214 214 m.bad = badfn
215 215 return m
216 216
217 217 def _donormalize(patterns, default, root, cwd, auditor, warn):
218 218 '''Convert 'kind:pat' from the patterns list to tuples with kind and
219 219 normalized and rooted patterns and with listfiles expanded.'''
220 220 kindpats = []
221 221 for kind, pat in [_patsplit(p, default) for p in patterns]:
222 222 if kind in cwdrelativepatternkinds:
223 223 pat = pathutil.canonpath(root, cwd, pat, auditor)
224 224 elif kind in ('relglob', 'path', 'rootfilesin'):
225 225 pat = util.normpath(pat)
226 226 elif kind in ('listfile', 'listfile0'):
227 227 try:
228 228 files = util.readfile(pat)
229 229 if kind == 'listfile0':
230 230 files = files.split('\0')
231 231 else:
232 232 files = files.splitlines()
233 233 files = [f for f in files if f]
234 234 except EnvironmentError:
235 235 raise error.Abort(_("unable to read file list (%s)") % pat)
236 236 for k, p, source in _donormalize(files, default, root, cwd,
237 237 auditor, warn):
238 238 kindpats.append((k, p, pat))
239 239 continue
240 240 elif kind == 'include':
241 241 try:
242 242 fullpath = os.path.join(root, util.localpath(pat))
243 243 includepats = readpatternfile(fullpath, warn)
244 244 for k, p, source in _donormalize(includepats, default,
245 245 root, cwd, auditor, warn):
246 246 kindpats.append((k, p, source or pat))
247 247 except error.Abort as inst:
248 248 raise error.Abort('%s: %s' % (pat, inst[0]))
249 249 except IOError as inst:
250 250 if warn:
251 251 warn(_("skipping unreadable pattern file '%s': %s\n") %
252 252 (pat, stringutil.forcebytestr(inst.strerror)))
253 253 continue
254 254 # else: re or relre - which cannot be normalized
255 255 kindpats.append((kind, pat, ''))
256 256 return kindpats
257 257
258 258 class basematcher(object):
259 259
260 260 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
261 261 self._root = root
262 262 self._cwd = cwd
263 263 if badfn is not None:
264 264 self.bad = badfn
265 265 self._relativeuipath = relativeuipath
266 266
267 267 def __call__(self, fn):
268 268 return self.matchfn(fn)
269 269 def __iter__(self):
270 270 for f in self._files:
271 271 yield f
272 272 # Callbacks related to how the matcher is used by dirstate.walk.
273 273 # Subscribers to these events must monkeypatch the matcher object.
274 274 def bad(self, f, msg):
275 275 '''Callback from dirstate.walk for each explicit file that can't be
276 276 found/accessed, with an error message.'''
277 277
278 278 # If an explicitdir is set, it will be called when an explicitly listed
279 279 # directory is visited.
280 280 explicitdir = None
281 281
282 282 # If an traversedir is set, it will be called when a directory discovered
283 283 # by recursive traversal is visited.
284 284 traversedir = None
285 285
286 286 def abs(self, f):
287 287 '''Convert a repo path back to path that is relative to the root of the
288 288 matcher.'''
289 289 return f
290 290
291 291 def rel(self, f):
292 292 '''Convert repo path back to path that is relative to cwd of matcher.'''
293 293 return util.pathto(self._root, self._cwd, f)
294 294
295 295 def uipath(self, f):
296 296 '''Convert repo path to a display path. If patterns or -I/-X were used
297 297 to create this matcher, the display path will be relative to cwd.
298 298 Otherwise it is relative to the root of the repo.'''
299 299 return (self._relativeuipath and self.rel(f)) or self.abs(f)
300 300
301 301 @propertycache
302 302 def _files(self):
303 303 return []
304 304
305 305 def files(self):
306 306 '''Explicitly listed files or patterns or roots:
307 307 if no patterns or .always(): empty list,
308 308 if exact: list exact files,
309 309 if not .anypats(): list all files and dirs,
310 310 else: optimal roots'''
311 311 return self._files
312 312
313 313 @propertycache
314 314 def _fileset(self):
315 315 return set(self._files)
316 316
317 317 def exact(self, f):
318 318 '''Returns True if f is in .files().'''
319 319 return f in self._fileset
320 320
321 321 def matchfn(self, f):
322 322 return False
323 323
324 324 def visitdir(self, dir):
325 325 '''Decides whether a directory should be visited based on whether it
326 326 has potential matches in it or one of its subdirectories. This is
327 327 based on the match's primary, included, and excluded patterns.
328 328
329 329 Returns the string 'all' if the given directory and all subdirectories
330 330 should be visited. Otherwise returns True or False indicating whether
331 331 the given directory should be visited.
332 332 '''
333 333 return True
334 334
335 335 def visitchildrenset(self, dir):
336 336 '''Decides whether a directory should be visited based on whether it
337 337 has potential matches in it or one of its subdirectories, and
338 338 potentially lists which subdirectories of that directory should be
339 339 visited. This is based on the match's primary, included, and excluded
340 340 patterns.
341 341
342 342 This function is very similar to 'visitdir', and the following mapping
343 343 can be applied:
344 344
345 345 visitdir | visitchildrenlist
346 346 ----------+-------------------
347 347 False | set()
348 348 'all' | 'all'
349 349 True | 'this' OR non-empty set of subdirs -or files- to visit
350 350
351 351 Example:
352 352 Assume matchers ['path:foo/bar', 'rootfilesin:qux'], we would return
353 353 the following values (assuming the implementation of visitchildrenset
354 354 is capable of recognizing this; some implementations are not).
355 355
356 356 '.' -> {'foo', 'qux'}
357 357 'baz' -> set()
358 358 'foo' -> {'bar'}
359 359 # Ideally this would be 'all', but since the prefix nature of matchers
360 360 # is applied to the entire matcher, we have to downgrade this to
361 361 # 'this' due to the non-prefix 'rootfilesin'-kind matcher being mixed
362 362 # in.
363 363 'foo/bar' -> 'this'
364 364 'qux' -> 'this'
365 365
366 366 Important:
367 367 Most matchers do not know if they're representing files or
368 368 directories. They see ['path:dir/f'] and don't know whether 'f' is a
369 369 file or a directory, so visitchildrenset('dir') for most matchers will
370 370 return {'f'}, but if the matcher knows it's a file (like exactmatcher
371 371 does), it may return 'this'. Do not rely on the return being a set
372 372 indicating that there are no files in this dir to investigate (or
373 373 equivalently that if there are files to investigate in 'dir' that it
374 374 will always return 'this').
375 375 '''
376 376 return 'this'
377 377
378 378 def always(self):
379 379 '''Matcher will match everything and .files() will be empty --
380 380 optimization might be possible.'''
381 381 return False
382 382
383 383 def isexact(self):
384 384 '''Matcher will match exactly the list of files in .files() --
385 385 optimization might be possible.'''
386 386 return False
387 387
388 388 def prefix(self):
389 389 '''Matcher will match the paths in .files() recursively --
390 390 optimization might be possible.'''
391 391 return False
392 392
393 393 def anypats(self):
394 394 '''None of .always(), .isexact(), and .prefix() is true --
395 395 optimizations will be difficult.'''
396 396 return not self.always() and not self.isexact() and not self.prefix()
397 397
398 398 class alwaysmatcher(basematcher):
399 399 '''Matches everything.'''
400 400
401 401 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
402 402 super(alwaysmatcher, self).__init__(root, cwd, badfn,
403 403 relativeuipath=relativeuipath)
404 404
405 405 def always(self):
406 406 return True
407 407
408 408 def matchfn(self, f):
409 409 return True
410 410
411 411 def visitdir(self, dir):
412 412 return 'all'
413 413
414 414 def visitchildrenset(self, dir):
415 415 return 'all'
416 416
417 417 def __repr__(self):
418 418 return r'<alwaysmatcher>'
419 419
420 420 class nevermatcher(basematcher):
421 421 '''Matches nothing.'''
422 422
423 423 def __init__(self, root, cwd, badfn=None):
424 424 super(nevermatcher, self).__init__(root, cwd, badfn)
425 425
426 426 # It's a little weird to say that the nevermatcher is an exact matcher
427 427 # or a prefix matcher, but it seems to make sense to let callers take
428 428 # fast paths based on either. There will be no exact matches, nor any
429 429 # prefixes (files() returns []), so fast paths iterating over them should
430 430 # be efficient (and correct).
431 431 def isexact(self):
432 432 return True
433 433
434 434 def prefix(self):
435 435 return True
436 436
437 437 def visitdir(self, dir):
438 438 return False
439 439
440 440 def visitchildrenset(self, dir):
441 441 return set()
442 442
443 443 def __repr__(self):
444 444 return r'<nevermatcher>'
445 445
446 446 class predicatematcher(basematcher):
447 447 """A matcher adapter for a simple boolean function"""
448 448
449 449 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
450 450 super(predicatematcher, self).__init__(root, cwd, badfn)
451 451 self.matchfn = predfn
452 452 self._predrepr = predrepr
453 453
454 454 @encoding.strmethod
455 455 def __repr__(self):
456 456 s = (stringutil.buildrepr(self._predrepr)
457 457 or pycompat.byterepr(self.matchfn))
458 458 return '<predicatenmatcher pred=%s>' % s
459 459
460 460 class patternmatcher(basematcher):
461 461
462 462 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
463 463 super(patternmatcher, self).__init__(root, cwd, badfn)
464 464
465 465 self._files = _explicitfiles(kindpats)
466 466 self._prefix = _prefix(kindpats)
467 467 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
468 468 root)
469 469
470 470 @propertycache
471 471 def _dirs(self):
472 472 return set(util.dirs(self._fileset)) | {'.'}
473 473
474 474 def visitdir(self, dir):
475 475 if self._prefix and dir in self._fileset:
476 476 return 'all'
477 477 return ('.' in self._fileset or
478 478 dir in self._fileset or
479 479 dir in self._dirs or
480 480 any(parentdir in self._fileset
481 481 for parentdir in util.finddirs(dir)))
482 482
483 483 def visitchildrenset(self, dir):
484 484 ret = self.visitdir(dir)
485 485 if ret is True:
486 486 return 'this'
487 487 elif not ret:
488 488 return set()
489 489 assert ret == 'all'
490 490 return 'all'
491 491
492 492 def prefix(self):
493 493 return self._prefix
494 494
495 495 @encoding.strmethod
496 496 def __repr__(self):
497 497 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
498 498
499 499 # This is basically a reimplementation of util.dirs that stores the children
500 500 # instead of just a count of them, plus a small optional optimization to avoid
501 501 # some directories we don't need.
502 502 class _dirchildren(object):
503 503 def __init__(self, paths, onlyinclude=None):
504 504 self._dirs = {}
505 505 self._onlyinclude = onlyinclude or []
506 506 addpath = self.addpath
507 507 for f in paths:
508 508 addpath(f)
509 509
510 510 def addpath(self, path):
511 511 if path == '.':
512 512 return
513 513 dirs = self._dirs
514 514 findsplitdirs = _dirchildren._findsplitdirs
515 515 for d, b in findsplitdirs(path):
516 516 if d not in self._onlyinclude:
517 517 continue
518 518 dirs.setdefault(d, set()).add(b)
519 519
520 520 @staticmethod
521 521 def _findsplitdirs(path):
522 522 # yields (dirname, basename) tuples, walking back to the root. This is
523 523 # very similar to util.finddirs, except:
524 524 # - produces a (dirname, basename) tuple, not just 'dirname'
525 525 # - includes root dir
526 526 # Unlike manifest._splittopdir, this does not suffix `dirname` with a
527 527 # slash, and produces '.' for the root instead of ''.
528 528 oldpos = len(path)
529 529 pos = path.rfind('/')
530 530 while pos != -1:
531 531 yield path[:pos], path[pos + 1:oldpos]
532 532 oldpos = pos
533 533 pos = path.rfind('/', 0, pos)
534 534 yield '.', path[:oldpos]
535 535
536 536 def get(self, path):
537 537 return self._dirs.get(path, set())
538 538
539 539 class includematcher(basematcher):
540 540
541 541 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
542 542 super(includematcher, self).__init__(root, cwd, badfn)
543 543
544 544 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
545 545 listsubrepos, root)
546 546 self._prefix = _prefix(kindpats)
547 547 roots, dirs, parents = _rootsdirsandparents(kindpats)
548 548 # roots are directories which are recursively included.
549 549 self._roots = set(roots)
550 550 # dirs are directories which are non-recursively included.
551 551 self._dirs = set(dirs)
552 552 # parents are directories which are non-recursively included because
553 553 # they are needed to get to items in _dirs or _roots.
554 554 self._parents = set(parents)
555 555
556 556 def visitdir(self, dir):
557 557 if self._prefix and dir in self._roots:
558 558 return 'all'
559 559 return ('.' in self._roots or
560 560 dir in self._roots or
561 561 dir in self._dirs or
562 562 dir in self._parents or
563 563 any(parentdir in self._roots
564 564 for parentdir in util.finddirs(dir)))
565 565
566 566 @propertycache
567 567 def _allparentschildren(self):
568 568 # It may seem odd that we add dirs, roots, and parents, and then
569 569 # restrict to only parents. This is to catch the case of:
570 570 # dirs = ['foo/bar']
571 571 # parents = ['foo']
572 572 # if we asked for the children of 'foo', but had only added
573 573 # self._parents, we wouldn't be able to respond ['bar'].
574 574 return _dirchildren(
575 575 itertools.chain(self._dirs, self._roots, self._parents),
576 576 onlyinclude=self._parents)
577 577
578 578 def visitchildrenset(self, dir):
579 579 if self._prefix and dir in self._roots:
580 580 return 'all'
581 581 # Note: this does *not* include the 'dir in self._parents' case from
582 582 # visitdir, that's handled below.
583 583 if ('.' in self._roots or
584 584 dir in self._roots or
585 585 dir in self._dirs or
586 586 any(parentdir in self._roots
587 587 for parentdir in util.finddirs(dir))):
588 588 return 'this'
589 589
590 590 if dir in self._parents:
591 591 return self._allparentschildren.get(dir) or set()
592 592 return set()
593 593
594 594 @encoding.strmethod
595 595 def __repr__(self):
596 596 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
597 597
598 598 class exactmatcher(basematcher):
599 599 '''Matches the input files exactly. They are interpreted as paths, not
600 600 patterns (so no kind-prefixes).
601 601 '''
602 602
603 603 def __init__(self, root, cwd, files, badfn=None):
604 604 super(exactmatcher, self).__init__(root, cwd, badfn)
605 605
606 606 if isinstance(files, list):
607 607 self._files = files
608 608 else:
609 609 self._files = list(files)
610 610
611 611 matchfn = basematcher.exact
612 612
613 613 @propertycache
614 614 def _dirs(self):
615 615 return set(util.dirs(self._fileset)) | {'.'}
616 616
617 617 def visitdir(self, dir):
618 618 return dir in self._dirs
619 619
620 620 def visitchildrenset(self, dir):
621 621 if not self._fileset or dir not in self._dirs:
622 622 return set()
623 623
624 624 candidates = self._fileset | self._dirs - {'.'}
625 625 if dir != '.':
626 626 d = dir + '/'
627 627 candidates = set(c[len(d):] for c in candidates if
628 628 c.startswith(d))
629 629 # self._dirs includes all of the directories, recursively, so if
630 630 # we're attempting to match foo/bar/baz.txt, it'll have '.', 'foo',
631 631 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a
632 632 # '/' in it, indicating a it's for a subdir-of-a-subdir; the
633 633 # immediate subdir will be in there without a slash.
634 634 ret = {c for c in candidates if '/' not in c}
635 635 # We really do not expect ret to be empty, since that would imply that
636 636 # there's something in _dirs that didn't have a file in _fileset.
637 637 assert ret
638 638 return ret
639 639
640 640 def isexact(self):
641 641 return True
642 642
643 643 @encoding.strmethod
644 644 def __repr__(self):
645 645 return ('<exactmatcher files=%r>' % self._files)
646 646
647 647 class differencematcher(basematcher):
648 648 '''Composes two matchers by matching if the first matches and the second
649 649 does not.
650 650
651 651 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
652 652 traversedir) are ignored.
653 653 '''
654 654 def __init__(self, m1, m2):
655 655 super(differencematcher, self).__init__(m1._root, m1._cwd)
656 656 self._m1 = m1
657 657 self._m2 = m2
658 658 self.bad = m1.bad
659 659 self.explicitdir = m1.explicitdir
660 660 self.traversedir = m1.traversedir
661 661
662 662 def matchfn(self, f):
663 663 return self._m1(f) and not self._m2(f)
664 664
665 665 @propertycache
666 666 def _files(self):
667 667 if self.isexact():
668 668 return [f for f in self._m1.files() if self(f)]
669 669 # If m1 is not an exact matcher, we can't easily figure out the set of
670 670 # files, because its files() are not always files. For example, if
671 671 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
672 672 # want to remove "dir" from the set even though it would match m2,
673 673 # because the "dir" in m1 may not be a file.
674 674 return self._m1.files()
675 675
676 676 def visitdir(self, dir):
677 677 if self._m2.visitdir(dir) == 'all':
678 678 return False
679 679 return bool(self._m1.visitdir(dir))
680 680
681 681 def visitchildrenset(self, dir):
682 682 m2_set = self._m2.visitchildrenset(dir)
683 683 if m2_set == 'all':
684 684 return set()
685 685 m1_set = self._m1.visitchildrenset(dir)
686 686 # Possible values for m1: 'all', 'this', set(...), set()
687 687 # Possible values for m2: 'this', set(...), set()
688 688 # If m2 has nothing under here that we care about, return m1, even if
689 689 # it's 'all'. This is a change in behavior from visitdir, which would
690 690 # return True, not 'all', for some reason.
691 691 if not m2_set:
692 692 return m1_set
693 693 if m1_set in ['all', 'this']:
694 694 # Never return 'all' here if m2_set is any kind of non-empty (either
695 695 # 'this' or set(foo)), since m2 might return set() for a
696 696 # subdirectory.
697 697 return 'this'
698 698 # Possible values for m1: set(...), set()
699 699 # Possible values for m2: 'this', set(...)
700 700 # We ignore m2's set results. They're possibly incorrect:
701 701 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset('.'):
702 702 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd
703 703 # return set(), which is *not* correct, we still need to visit 'dir'!
704 704 return m1_set
705 705
706 706 def isexact(self):
707 707 return self._m1.isexact()
708 708
709 709 @encoding.strmethod
710 710 def __repr__(self):
711 711 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
712 712
713 713 def intersectmatchers(m1, m2):
714 714 '''Composes two matchers by matching if both of them match.
715 715
716 716 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
717 717 traversedir) are ignored.
718 718 '''
719 719 if m1 is None or m2 is None:
720 720 return m1 or m2
721 721 if m1.always():
722 722 m = copy.copy(m2)
723 723 # TODO: Consider encapsulating these things in a class so there's only
724 724 # one thing to copy from m1.
725 725 m.bad = m1.bad
726 726 m.explicitdir = m1.explicitdir
727 727 m.traversedir = m1.traversedir
728 728 m.abs = m1.abs
729 729 m.rel = m1.rel
730 730 m._relativeuipath |= m1._relativeuipath
731 731 return m
732 732 if m2.always():
733 733 m = copy.copy(m1)
734 734 m._relativeuipath |= m2._relativeuipath
735 735 return m
736 736 return intersectionmatcher(m1, m2)
737 737
738 738 class intersectionmatcher(basematcher):
739 739 def __init__(self, m1, m2):
740 740 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
741 741 self._m1 = m1
742 742 self._m2 = m2
743 743 self.bad = m1.bad
744 744 self.explicitdir = m1.explicitdir
745 745 self.traversedir = m1.traversedir
746 746
747 747 @propertycache
748 748 def _files(self):
749 749 if self.isexact():
750 750 m1, m2 = self._m1, self._m2
751 751 if not m1.isexact():
752 752 m1, m2 = m2, m1
753 753 return [f for f in m1.files() if m2(f)]
754 754 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
755 755 # the set of files, because their files() are not always files. For
756 756 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
757 757 # "path:dir2", we don't want to remove "dir2" from the set.
758 758 return self._m1.files() + self._m2.files()
759 759
760 760 def matchfn(self, f):
761 761 return self._m1(f) and self._m2(f)
762 762
763 763 def visitdir(self, dir):
764 764 visit1 = self._m1.visitdir(dir)
765 765 if visit1 == 'all':
766 766 return self._m2.visitdir(dir)
767 767 # bool() because visit1=True + visit2='all' should not be 'all'
768 768 return bool(visit1 and self._m2.visitdir(dir))
769 769
770 770 def visitchildrenset(self, dir):
771 771 m1_set = self._m1.visitchildrenset(dir)
772 772 if not m1_set:
773 773 return set()
774 774 m2_set = self._m2.visitchildrenset(dir)
775 775 if not m2_set:
776 776 return set()
777 777
778 778 if m1_set == 'all':
779 779 return m2_set
780 780 elif m2_set == 'all':
781 781 return m1_set
782 782
783 783 if m1_set == 'this' or m2_set == 'this':
784 784 return 'this'
785 785
786 786 assert isinstance(m1_set, set) and isinstance(m2_set, set)
787 787 return m1_set.intersection(m2_set)
788 788
789 789 def always(self):
790 790 return self._m1.always() and self._m2.always()
791 791
792 792 def isexact(self):
793 793 return self._m1.isexact() or self._m2.isexact()
794 794
795 795 @encoding.strmethod
796 796 def __repr__(self):
797 797 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
798 798
799 799 class subdirmatcher(basematcher):
800 800 """Adapt a matcher to work on a subdirectory only.
801 801
802 802 The paths are remapped to remove/insert the path as needed:
803 803
804 804 >>> from . import pycompat
805 805 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
806 806 >>> m2 = subdirmatcher(b'sub', m1)
807 807 >>> bool(m2(b'a.txt'))
808 808 False
809 809 >>> bool(m2(b'b.txt'))
810 810 True
811 811 >>> bool(m2.matchfn(b'a.txt'))
812 812 False
813 813 >>> bool(m2.matchfn(b'b.txt'))
814 814 True
815 815 >>> m2.files()
816 816 ['b.txt']
817 817 >>> m2.exact(b'b.txt')
818 818 True
819 819 >>> util.pconvert(m2.rel(b'b.txt'))
820 820 'sub/b.txt'
821 821 >>> def bad(f, msg):
822 822 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
823 823 >>> m1.bad = bad
824 824 >>> m2.bad(b'x.txt', b'No such file')
825 825 sub/x.txt: No such file
826 826 >>> m2.abs(b'c.txt')
827 827 'sub/c.txt'
828 828 """
829 829
830 830 def __init__(self, path, matcher):
831 831 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
832 832 self._path = path
833 833 self._matcher = matcher
834 834 self._always = matcher.always()
835 835
836 836 self._files = [f[len(path) + 1:] for f in matcher._files
837 837 if f.startswith(path + "/")]
838 838
839 839 # If the parent repo had a path to this subrepo and the matcher is
840 840 # a prefix matcher, this submatcher always matches.
841 841 if matcher.prefix():
842 842 self._always = any(f == path for f in matcher._files)
843 843
844 844 def bad(self, f, msg):
845 845 self._matcher.bad(self._path + "/" + f, msg)
846 846
847 847 def abs(self, f):
848 848 return self._matcher.abs(self._path + "/" + f)
849 849
850 850 def rel(self, f):
851 851 return self._matcher.rel(self._path + "/" + f)
852 852
853 853 def uipath(self, f):
854 854 return self._matcher.uipath(self._path + "/" + f)
855 855
856 856 def matchfn(self, f):
857 857 # Some information is lost in the superclass's constructor, so we
858 858 # can not accurately create the matching function for the subdirectory
859 859 # from the inputs. Instead, we override matchfn() and visitdir() to
860 860 # call the original matcher with the subdirectory path prepended.
861 861 return self._matcher.matchfn(self._path + "/" + f)
862 862
863 863 def visitdir(self, dir):
864 864 if dir == '.':
865 865 dir = self._path
866 866 else:
867 867 dir = self._path + "/" + dir
868 868 return self._matcher.visitdir(dir)
869 869
870 870 def visitchildrenset(self, dir):
871 871 if dir == '.':
872 872 dir = self._path
873 873 else:
874 874 dir = self._path + "/" + dir
875 875 return self._matcher.visitchildrenset(dir)
876 876
877 877 def always(self):
878 878 return self._always
879 879
880 880 def prefix(self):
881 881 return self._matcher.prefix() and not self._always
882 882
883 883 @encoding.strmethod
884 884 def __repr__(self):
885 885 return ('<subdirmatcher path=%r, matcher=%r>' %
886 886 (self._path, self._matcher))
887 887
888 888 class prefixdirmatcher(basematcher):
889 889 """Adapt a matcher to work on a parent directory.
890 890
891 891 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
892 892 traversedir) are ignored.
893 893
894 894 The prefix path should usually be the relative path from the root of
895 895 this matcher to the root of the wrapped matcher.
896 896
897 897 >>> m1 = match(util.localpath(b'root/d/e'), b'f', [b'../a.txt', b'b.txt'])
898 898 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
899 899 >>> bool(m2(b'a.txt'),)
900 900 False
901 901 >>> bool(m2(b'd/e/a.txt'))
902 902 True
903 903 >>> bool(m2(b'd/e/b.txt'))
904 904 False
905 905 >>> m2.files()
906 906 ['d/e/a.txt', 'd/e/f/b.txt']
907 907 >>> m2.exact(b'd/e/a.txt')
908 908 True
909 909 >>> m2.visitdir(b'd')
910 910 True
911 911 >>> m2.visitdir(b'd/e')
912 912 True
913 913 >>> m2.visitdir(b'd/e/f')
914 914 True
915 915 >>> m2.visitdir(b'd/e/g')
916 916 False
917 917 >>> m2.visitdir(b'd/ef')
918 918 False
919 919 """
920 920
921 921 def __init__(self, root, cwd, path, matcher, badfn=None):
922 922 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
923 923 if not path:
924 924 raise error.ProgrammingError('prefix path must not be empty')
925 925 self._path = path
926 926 self._pathprefix = path + '/'
927 927 self._matcher = matcher
928 928
929 929 @propertycache
930 930 def _files(self):
931 931 return [self._pathprefix + f for f in self._matcher._files]
932 932
933 933 def matchfn(self, f):
934 934 if not f.startswith(self._pathprefix):
935 935 return False
936 936 return self._matcher.matchfn(f[len(self._pathprefix):])
937 937
938 938 @propertycache
939 939 def _pathdirs(self):
940 940 return set(util.finddirs(self._path)) | {'.'}
941 941
942 942 def visitdir(self, dir):
943 943 if dir == self._path:
944 944 return self._matcher.visitdir('.')
945 945 if dir.startswith(self._pathprefix):
946 946 return self._matcher.visitdir(dir[len(self._pathprefix):])
947 947 return dir in self._pathdirs
948 948
949 949 def visitchildrenset(self, dir):
950 950 if dir == self._path:
951 951 return self._matcher.visitchildrenset('.')
952 952 if dir.startswith(self._pathprefix):
953 953 return self._matcher.visitchildrenset(dir[len(self._pathprefix):])
954 954 if dir in self._pathdirs:
955 955 return 'this'
956 956 return set()
957 957
958 958 def isexact(self):
959 959 return self._matcher.isexact()
960 960
961 961 def prefix(self):
962 962 return self._matcher.prefix()
963 963
964 964 @encoding.strmethod
965 965 def __repr__(self):
966 966 return ('<prefixdirmatcher path=%r, matcher=%r>'
967 967 % (pycompat.bytestr(self._path), self._matcher))
968 968
969 969 class unionmatcher(basematcher):
970 970 """A matcher that is the union of several matchers.
971 971
972 972 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
973 973 taken from the first matcher.
974 974 """
975 975
976 976 def __init__(self, matchers):
977 977 m1 = matchers[0]
978 978 super(unionmatcher, self).__init__(m1._root, m1._cwd)
979 979 self.explicitdir = m1.explicitdir
980 980 self.traversedir = m1.traversedir
981 981 self._matchers = matchers
982 982
983 983 def matchfn(self, f):
984 984 for match in self._matchers:
985 985 if match(f):
986 986 return True
987 987 return False
988 988
989 989 def visitdir(self, dir):
990 990 r = False
991 991 for m in self._matchers:
992 992 v = m.visitdir(dir)
993 993 if v == 'all':
994 994 return v
995 995 r |= v
996 996 return r
997 997
998 998 def visitchildrenset(self, dir):
999 999 r = set()
1000 1000 this = False
1001 1001 for m in self._matchers:
1002 1002 v = m.visitchildrenset(dir)
1003 1003 if not v:
1004 1004 continue
1005 1005 if v == 'all':
1006 1006 return v
1007 1007 if this or v == 'this':
1008 1008 this = True
1009 1009 # don't break, we might have an 'all' in here.
1010 1010 continue
1011 1011 assert isinstance(v, set)
1012 1012 r = r.union(v)
1013 1013 if this:
1014 1014 return 'this'
1015 1015 return r
1016 1016
1017 1017 @encoding.strmethod
1018 1018 def __repr__(self):
1019 1019 return ('<unionmatcher matchers=%r>' % self._matchers)
1020 1020
1021 1021 def patkind(pattern, default=None):
1022 1022 '''If pattern is 'kind:pat' with a known kind, return kind.'''
1023 1023 return _patsplit(pattern, default)[0]
1024 1024
1025 1025 def _patsplit(pattern, default):
1026 1026 """Split a string into the optional pattern kind prefix and the actual
1027 1027 pattern."""
1028 1028 if ':' in pattern:
1029 1029 kind, pat = pattern.split(':', 1)
1030 1030 if kind in allpatternkinds:
1031 1031 return kind, pat
1032 1032 return default, pattern
1033 1033
1034 1034 def _globre(pat):
1035 1035 r'''Convert an extended glob string to a regexp string.
1036 1036
1037 1037 >>> from . import pycompat
1038 1038 >>> def bprint(s):
1039 1039 ... print(pycompat.sysstr(s))
1040 1040 >>> bprint(_globre(br'?'))
1041 1041 .
1042 1042 >>> bprint(_globre(br'*'))
1043 1043 [^/]*
1044 1044 >>> bprint(_globre(br'**'))
1045 1045 .*
1046 1046 >>> bprint(_globre(br'**/a'))
1047 1047 (?:.*/)?a
1048 1048 >>> bprint(_globre(br'a/**/b'))
1049 1049 a/(?:.*/)?b
1050 1050 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
1051 1051 [a*?!^][\^b][^c]
1052 1052 >>> bprint(_globre(br'{a,b}'))
1053 1053 (?:a|b)
1054 1054 >>> bprint(_globre(br'.\*\?'))
1055 1055 \.\*\?
1056 1056 '''
1057 1057 i, n = 0, len(pat)
1058 1058 res = ''
1059 1059 group = 0
1060 1060 escape = util.stringutil.regexbytesescapemap.get
1061 1061 def peek():
1062 1062 return i < n and pat[i:i + 1]
1063 1063 while i < n:
1064 1064 c = pat[i:i + 1]
1065 1065 i += 1
1066 1066 if c not in '*?[{},\\':
1067 1067 res += escape(c, c)
1068 1068 elif c == '*':
1069 1069 if peek() == '*':
1070 1070 i += 1
1071 1071 if peek() == '/':
1072 1072 i += 1
1073 1073 res += '(?:.*/)?'
1074 1074 else:
1075 1075 res += '.*'
1076 1076 else:
1077 1077 res += '[^/]*'
1078 1078 elif c == '?':
1079 1079 res += '.'
1080 1080 elif c == '[':
1081 1081 j = i
1082 1082 if j < n and pat[j:j + 1] in '!]':
1083 1083 j += 1
1084 1084 while j < n and pat[j:j + 1] != ']':
1085 1085 j += 1
1086 1086 if j >= n:
1087 1087 res += '\\['
1088 1088 else:
1089 1089 stuff = pat[i:j].replace('\\','\\\\')
1090 1090 i = j + 1
1091 1091 if stuff[0:1] == '!':
1092 1092 stuff = '^' + stuff[1:]
1093 1093 elif stuff[0:1] == '^':
1094 1094 stuff = '\\' + stuff
1095 1095 res = '%s[%s]' % (res, stuff)
1096 1096 elif c == '{':
1097 1097 group += 1
1098 1098 res += '(?:'
1099 1099 elif c == '}' and group:
1100 1100 res += ')'
1101 1101 group -= 1
1102 1102 elif c == ',' and group:
1103 1103 res += '|'
1104 1104 elif c == '\\':
1105 1105 p = peek()
1106 1106 if p:
1107 1107 i += 1
1108 1108 res += escape(p, p)
1109 1109 else:
1110 1110 res += escape(c, c)
1111 1111 else:
1112 1112 res += escape(c, c)
1113 1113 return res
1114 1114
1115 1115 def _regex(kind, pat, globsuffix):
1116 1116 '''Convert a (normalized) pattern of any kind into a regular expression.
1117 1117 globsuffix is appended to the regexp of globs.'''
1118 1118 if not pat:
1119 1119 return ''
1120 1120 if kind == 're':
1121 1121 return pat
1122 1122 if kind in ('path', 'relpath'):
1123 1123 if pat == '.':
1124 1124 return ''
1125 1125 return util.stringutil.reescape(pat) + '(?:/|$)'
1126 1126 if kind == 'rootfilesin':
1127 1127 if pat == '.':
1128 1128 escaped = ''
1129 1129 else:
1130 1130 # Pattern is a directory name.
1131 1131 escaped = util.stringutil.reescape(pat) + '/'
1132 1132 # Anything after the pattern must be a non-directory.
1133 1133 return escaped + '[^/]+$'
1134 1134 if kind == 'relglob':
1135 1135 return '(?:|.*/)' + _globre(pat) + globsuffix
1136 1136 if kind == 'relre':
1137 1137 if pat.startswith('^'):
1138 1138 return pat
1139 1139 return '.*' + pat
1140 1140 if kind == 'glob':
1141 1141 return _globre(pat) + globsuffix
1142 1142 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
1143 1143
1144 1144 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
1145 1145 '''Return regexp string and a matcher function for kindpats.
1146 1146 globsuffix is appended to the regexp of globs.'''
1147 1147 matchfuncs = []
1148 1148
1149 1149 subincludes, kindpats = _expandsubinclude(kindpats, root)
1150 1150 if subincludes:
1151 1151 submatchers = {}
1152 1152 def matchsubinclude(f):
1153 1153 for prefix, matcherargs in subincludes:
1154 1154 if f.startswith(prefix):
1155 1155 mf = submatchers.get(prefix)
1156 1156 if mf is None:
1157 1157 mf = match(*matcherargs)
1158 1158 submatchers[prefix] = mf
1159 1159
1160 1160 if mf(f[len(prefix):]):
1161 1161 return True
1162 1162 return False
1163 1163 matchfuncs.append(matchsubinclude)
1164 1164
1165 1165 regex = ''
1166 1166 if kindpats:
1167 1167 if all(k == 'rootfilesin' for k, p, s in kindpats):
1168 1168 dirs = {p for k, p, s in kindpats}
1169 1169 def mf(f):
1170 1170 i = f.rfind('/')
1171 1171 if i >= 0:
1172 1172 dir = f[:i]
1173 1173 else:
1174 1174 dir = '.'
1175 1175 return dir in dirs
1176 1176 regex = b'rootfilesin: %s' % stringutil.pprint(list(sorted(dirs)))
1177 1177 matchfuncs.append(mf)
1178 1178 else:
1179 1179 regex, mf = _buildregexmatch(kindpats, globsuffix)
1180 1180 matchfuncs.append(mf)
1181 1181
1182 1182 if len(matchfuncs) == 1:
1183 1183 return regex, matchfuncs[0]
1184 1184 else:
1185 1185 return regex, lambda f: any(mf(f) for mf in matchfuncs)
1186 1186
1187 1187 MAX_RE_SIZE = 20000
1188 _BASE_SIZE = len('(?:)')
1189 1188
1190 1189 def _joinregexes(regexps):
1191 1190 """gather multiple regular expressions into a single one"""
1192 return '(?:%s)' % '|'.join(regexps)
1191 return '|'.join(regexps)
1193 1192
1194 1193 def _buildregexmatch(kindpats, globsuffix):
1195 1194 """Build a match function from a list of kinds and kindpats,
1196 1195 return regexp string and a matcher function.
1197 1196
1198 1197 Test too large input
1199 1198 >>> _buildregexmatch([
1200 1199 ... ('relglob', '?' * MAX_RE_SIZE, '')
1201 1200 ... ], '$')
1202 1201 Traceback (most recent call last):
1203 1202 ...
1204 1203 Abort: matcher pattern is too long (20009 bytes)
1205 1204 """
1206 1205 try:
1207 1206 allgroups = []
1208 1207 regexps = [_regex(k, p, globsuffix) for (k, p, s) in kindpats]
1209 1208 fullregexp = _joinregexes(regexps)
1210 1209
1211 1210 startidx = 0
1212 groupsize = _BASE_SIZE
1211 groupsize = 0
1213 1212 for idx, r in enumerate(regexps):
1214 1213 piecesize = len(r)
1215 if (piecesize + _BASE_SIZE) > MAX_RE_SIZE:
1214 if piecesize > MAX_RE_SIZE:
1216 1215 msg = _("matcher pattern is too long (%d bytes)") % piecesize
1217 1216 raise error.Abort(msg)
1218 1217 elif (groupsize + piecesize) > MAX_RE_SIZE:
1219 1218 group = regexps[startidx:idx]
1220 1219 allgroups.append(_joinregexes(group))
1221 1220 startidx = idx
1222 groupsize = _BASE_SIZE
1221 groupsize = 0
1223 1222 groupsize += piecesize + 1
1224 1223
1225 1224 if startidx == 0:
1226 1225 func = _rematcher(fullregexp)
1227 1226 else:
1228 1227 group = regexps[startidx:]
1229 1228 allgroups.append(_joinregexes(group))
1230 1229 allmatchers = [_rematcher(g) for g in allgroups]
1231 1230 func = lambda s: any(m(s) for m in allmatchers)
1232 1231 return fullregexp, func
1233 1232 except re.error:
1234 1233 for k, p, s in kindpats:
1235 1234 try:
1236 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
1235 _rematcher(_regex(k, p, globsuffix))
1237 1236 except re.error:
1238 1237 if s:
1239 1238 raise error.Abort(_("%s: invalid pattern (%s): %s") %
1240 1239 (s, k, p))
1241 1240 else:
1242 1241 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
1243 1242 raise error.Abort(_("invalid pattern"))
1244 1243
1245 1244 def _patternrootsanddirs(kindpats):
1246 1245 '''Returns roots and directories corresponding to each pattern.
1247 1246
1248 1247 This calculates the roots and directories exactly matching the patterns and
1249 1248 returns a tuple of (roots, dirs) for each. It does not return other
1250 1249 directories which may also need to be considered, like the parent
1251 1250 directories.
1252 1251 '''
1253 1252 r = []
1254 1253 d = []
1255 1254 for kind, pat, source in kindpats:
1256 1255 if kind == 'glob': # find the non-glob prefix
1257 1256 root = []
1258 1257 for p in pat.split('/'):
1259 1258 if '[' in p or '{' in p or '*' in p or '?' in p:
1260 1259 break
1261 1260 root.append(p)
1262 1261 r.append('/'.join(root) or '.')
1263 1262 elif kind in ('relpath', 'path'):
1264 1263 r.append(pat or '.')
1265 1264 elif kind in ('rootfilesin',):
1266 1265 d.append(pat or '.')
1267 1266 else: # relglob, re, relre
1268 1267 r.append('.')
1269 1268 return r, d
1270 1269
1271 1270 def _roots(kindpats):
1272 1271 '''Returns root directories to match recursively from the given patterns.'''
1273 1272 roots, dirs = _patternrootsanddirs(kindpats)
1274 1273 return roots
1275 1274
1276 1275 def _rootsdirsandparents(kindpats):
1277 1276 '''Returns roots and exact directories from patterns.
1278 1277
1279 1278 `roots` are directories to match recursively, `dirs` should
1280 1279 be matched non-recursively, and `parents` are the implicitly required
1281 1280 directories to walk to items in either roots or dirs.
1282 1281
1283 1282 Returns a tuple of (roots, dirs, parents).
1284 1283
1285 1284 >>> _rootsdirsandparents(
1286 1285 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1287 1286 ... (b'glob', b'g*', b'')])
1288 1287 (['g/h', 'g/h', '.'], [], ['g', '.'])
1289 1288 >>> _rootsdirsandparents(
1290 1289 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1291 1290 ([], ['g/h', '.'], ['g', '.'])
1292 1291 >>> _rootsdirsandparents(
1293 1292 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1294 1293 ... (b'path', b'', b'')])
1295 1294 (['r', 'p/p', '.'], [], ['p', '.'])
1296 1295 >>> _rootsdirsandparents(
1297 1296 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1298 1297 ... (b'relre', b'rr', b'')])
1299 1298 (['.', '.', '.'], [], ['.'])
1300 1299 '''
1301 1300 r, d = _patternrootsanddirs(kindpats)
1302 1301
1303 1302 p = []
1304 1303 # Append the parents as non-recursive/exact directories, since they must be
1305 1304 # scanned to get to either the roots or the other exact directories.
1306 1305 p.extend(util.dirs(d))
1307 1306 p.extend(util.dirs(r))
1308 1307 # util.dirs() does not include the root directory, so add it manually
1309 1308 p.append('.')
1310 1309
1311 1310 # FIXME: all uses of this function convert these to sets, do so before
1312 1311 # returning.
1313 1312 # FIXME: all uses of this function do not need anything in 'roots' and
1314 1313 # 'dirs' to also be in 'parents', consider removing them before returning.
1315 1314 return r, d, p
1316 1315
1317 1316 def _explicitfiles(kindpats):
1318 1317 '''Returns the potential explicit filenames from the patterns.
1319 1318
1320 1319 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1321 1320 ['foo/bar']
1322 1321 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1323 1322 []
1324 1323 '''
1325 1324 # Keep only the pattern kinds where one can specify filenames (vs only
1326 1325 # directory names).
1327 1326 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1328 1327 return _roots(filable)
1329 1328
1330 1329 def _prefix(kindpats):
1331 1330 '''Whether all the patterns match a prefix (i.e. recursively)'''
1332 1331 for kind, pat, source in kindpats:
1333 1332 if kind not in ('path', 'relpath'):
1334 1333 return False
1335 1334 return True
1336 1335
1337 1336 _commentre = None
1338 1337
1339 1338 def readpatternfile(filepath, warn, sourceinfo=False):
1340 1339 '''parse a pattern file, returning a list of
1341 1340 patterns. These patterns should be given to compile()
1342 1341 to be validated and converted into a match function.
1343 1342
1344 1343 trailing white space is dropped.
1345 1344 the escape character is backslash.
1346 1345 comments start with #.
1347 1346 empty lines are skipped.
1348 1347
1349 1348 lines can be of the following formats:
1350 1349
1351 1350 syntax: regexp # defaults following lines to non-rooted regexps
1352 1351 syntax: glob # defaults following lines to non-rooted globs
1353 1352 re:pattern # non-rooted regular expression
1354 1353 glob:pattern # non-rooted glob
1355 1354 pattern # pattern of the current default type
1356 1355
1357 1356 if sourceinfo is set, returns a list of tuples:
1358 1357 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1359 1358 '''
1360 1359
1361 1360 syntaxes = {
1362 1361 're': 'relre:',
1363 1362 'regexp': 'relre:',
1364 1363 'glob': 'relglob:',
1365 1364 'include': 'include',
1366 1365 'subinclude': 'subinclude',
1367 1366 }
1368 1367 syntax = 'relre:'
1369 1368 patterns = []
1370 1369
1371 1370 fp = open(filepath, 'rb')
1372 1371 for lineno, line in enumerate(util.iterfile(fp), start=1):
1373 1372 if "#" in line:
1374 1373 global _commentre
1375 1374 if not _commentre:
1376 1375 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1377 1376 # remove comments prefixed by an even number of escapes
1378 1377 m = _commentre.search(line)
1379 1378 if m:
1380 1379 line = line[:m.end(1)]
1381 1380 # fixup properly escaped comments that survived the above
1382 1381 line = line.replace("\\#", "#")
1383 1382 line = line.rstrip()
1384 1383 if not line:
1385 1384 continue
1386 1385
1387 1386 if line.startswith('syntax:'):
1388 1387 s = line[7:].strip()
1389 1388 try:
1390 1389 syntax = syntaxes[s]
1391 1390 except KeyError:
1392 1391 if warn:
1393 1392 warn(_("%s: ignoring invalid syntax '%s'\n") %
1394 1393 (filepath, s))
1395 1394 continue
1396 1395
1397 1396 linesyntax = syntax
1398 1397 for s, rels in syntaxes.iteritems():
1399 1398 if line.startswith(rels):
1400 1399 linesyntax = rels
1401 1400 line = line[len(rels):]
1402 1401 break
1403 1402 elif line.startswith(s+':'):
1404 1403 linesyntax = rels
1405 1404 line = line[len(s) + 1:]
1406 1405 break
1407 1406 if sourceinfo:
1408 1407 patterns.append((linesyntax + line, lineno, line))
1409 1408 else:
1410 1409 patterns.append(linesyntax + line)
1411 1410 fp.close()
1412 1411 return patterns
@@ -1,1037 +1,1037 b''
1 1 $ fileset() {
2 2 > hg debugfileset --all-files "$@"
3 3 > }
4 4
5 5 $ hg init repo
6 6 $ cd repo
7 7 $ echo a > a1
8 8 $ echo a > a2
9 9 $ echo b > b1
10 10 $ echo b > b2
11 11 $ hg ci -Am addfiles
12 12 adding a1
13 13 adding a2
14 14 adding b1
15 15 adding b2
16 16
17 17 Test operators and basic patterns
18 18
19 19 $ fileset -v a1
20 20 (symbol 'a1')
21 21 * matcher:
22 <patternmatcher patterns='(?:a1$)'>
22 <patternmatcher patterns='a1$'>
23 23 a1
24 24 $ fileset -v 'a*'
25 25 (symbol 'a*')
26 26 * matcher:
27 <patternmatcher patterns='(?:a[^/]*$)'>
27 <patternmatcher patterns='a[^/]*$'>
28 28 a1
29 29 a2
30 30 $ fileset -v '"re:a\d"'
31 31 (string 're:a\\d')
32 32 * matcher:
33 <patternmatcher patterns='(?:a\\d)'>
33 <patternmatcher patterns='a\\d'>
34 34 a1
35 35 a2
36 36 $ fileset -v '!re:"a\d"'
37 37 (not
38 38 (kindpat
39 39 (symbol 're')
40 40 (string 'a\\d')))
41 41 * matcher:
42 42 <predicatenmatcher
43 43 pred=<not
44 <patternmatcher patterns='(?:a\\d)'>>>
44 <patternmatcher patterns='a\\d'>>>
45 45 b1
46 46 b2
47 47 $ fileset -v 'path:a1 or glob:b?'
48 48 (or
49 49 (kindpat
50 50 (symbol 'path')
51 51 (symbol 'a1'))
52 52 (kindpat
53 53 (symbol 'glob')
54 54 (symbol 'b?')))
55 55 * matcher:
56 <patternmatcher patterns='(?:a1(?:/|$)|b.$)'>
56 <patternmatcher patterns='a1(?:/|$)|b.$'>
57 57 a1
58 58 b1
59 59 b2
60 60 $ fileset -v --no-show-matcher 'a1 or a2'
61 61 (or
62 62 (symbol 'a1')
63 63 (symbol 'a2'))
64 64 a1
65 65 a2
66 66 $ fileset 'a1 | a2'
67 67 a1
68 68 a2
69 69 $ fileset 'a* and "*1"'
70 70 a1
71 71 $ fileset 'a* & "*1"'
72 72 a1
73 73 $ fileset 'not (r"a*")'
74 74 b1
75 75 b2
76 76 $ fileset '! ("a*")'
77 77 b1
78 78 b2
79 79 $ fileset 'a* - a1'
80 80 a2
81 81 $ fileset 'a_b'
82 82 $ fileset '"\xy"'
83 83 hg: parse error: invalid \x escape* (glob)
84 84 [255]
85 85
86 86 Test invalid syntax
87 87
88 88 $ fileset -v '"added"()'
89 89 (func
90 90 (string 'added')
91 91 None)
92 92 hg: parse error: not a symbol
93 93 [255]
94 94 $ fileset -v '()()'
95 95 (func
96 96 (group
97 97 None)
98 98 None)
99 99 hg: parse error: not a symbol
100 100 [255]
101 101 $ fileset -v -- '-x'
102 102 (negate
103 103 (symbol 'x'))
104 104 hg: parse error: can't use negate operator in this context
105 105 [255]
106 106 $ fileset -v -- '-()'
107 107 (negate
108 108 (group
109 109 None))
110 110 hg: parse error: can't use negate operator in this context
111 111 [255]
112 112 $ fileset -p parsed 'a, b, c'
113 113 * parsed:
114 114 (list
115 115 (symbol 'a')
116 116 (symbol 'b')
117 117 (symbol 'c'))
118 118 hg: parse error: can't use a list in this context
119 119 (see 'hg help "filesets.x or y"')
120 120 [255]
121 121
122 122 $ fileset '"path":.'
123 123 hg: parse error: not a symbol
124 124 [255]
125 125 $ fileset 'path:foo bar'
126 126 hg: parse error at 9: invalid token
127 127 [255]
128 128 $ fileset 'foo:bar:baz'
129 129 hg: parse error: not a symbol
130 130 [255]
131 131 $ fileset 'foo:bar()'
132 132 hg: parse error: pattern must be a string
133 133 [255]
134 134 $ fileset 'foo:bar'
135 135 hg: parse error: invalid pattern kind: foo
136 136 [255]
137 137
138 138 Show parsed tree at stages:
139 139
140 140 $ fileset -p unknown a
141 141 abort: invalid stage name: unknown
142 142 [255]
143 143
144 144 $ fileset -p parsed 'path:a1 or glob:b?'
145 145 * parsed:
146 146 (or
147 147 (kindpat
148 148 (symbol 'path')
149 149 (symbol 'a1'))
150 150 (kindpat
151 151 (symbol 'glob')
152 152 (symbol 'b?')))
153 153 a1
154 154 b1
155 155 b2
156 156
157 157 $ fileset -p all -s 'a1 or a2 or (grep("b") & clean())'
158 158 * parsed:
159 159 (or
160 160 (symbol 'a1')
161 161 (symbol 'a2')
162 162 (group
163 163 (and
164 164 (func
165 165 (symbol 'grep')
166 166 (string 'b'))
167 167 (func
168 168 (symbol 'clean')
169 169 None))))
170 170 * analyzed:
171 171 (or
172 172 (symbol 'a1')
173 173 (symbol 'a2')
174 174 (and
175 175 (func
176 176 (symbol 'grep')
177 177 (string 'b'))
178 178 (withstatus
179 179 (func
180 180 (symbol 'clean')
181 181 None)
182 182 (string 'clean'))))
183 183 * optimized:
184 184 (or
185 185 (patterns
186 186 (symbol 'a1')
187 187 (symbol 'a2'))
188 188 (and
189 189 (withstatus
190 190 (func
191 191 (symbol 'clean')
192 192 None)
193 193 (string 'clean'))
194 194 (func
195 195 (symbol 'grep')
196 196 (string 'b'))))
197 197 * matcher:
198 198 <unionmatcher matchers=[
199 <patternmatcher patterns='(?:a1$|a2$)'>,
199 <patternmatcher patterns='a1$|a2$'>,
200 200 <intersectionmatcher
201 201 m1=<predicatenmatcher pred=clean>,
202 202 m2=<predicatenmatcher pred=grep('b')>>]>
203 203 a1
204 204 a2
205 205 b1
206 206 b2
207 207
208 208 Union of basic patterns:
209 209
210 210 $ fileset -p optimized -s -r. 'a1 or a2 or path:b1'
211 211 * optimized:
212 212 (patterns
213 213 (symbol 'a1')
214 214 (symbol 'a2')
215 215 (kindpat
216 216 (symbol 'path')
217 217 (symbol 'b1')))
218 218 * matcher:
219 <patternmatcher patterns='(?:a1$|a2$|b1(?:/|$))'>
219 <patternmatcher patterns='a1$|a2$|b1(?:/|$)'>
220 220 a1
221 221 a2
222 222 b1
223 223
224 224 OR expression should be reordered by weight:
225 225
226 226 $ fileset -p optimized -s -r. 'grep("a") or a1 or grep("b") or b2'
227 227 * optimized:
228 228 (or
229 229 (patterns
230 230 (symbol 'a1')
231 231 (symbol 'b2'))
232 232 (func
233 233 (symbol 'grep')
234 234 (string 'a'))
235 235 (func
236 236 (symbol 'grep')
237 237 (string 'b')))
238 238 * matcher:
239 239 <unionmatcher matchers=[
240 <patternmatcher patterns='(?:a1$|b2$)'>,
240 <patternmatcher patterns='a1$|b2$'>,
241 241 <predicatenmatcher pred=grep('a')>,
242 242 <predicatenmatcher pred=grep('b')>]>
243 243 a1
244 244 a2
245 245 b1
246 246 b2
247 247
248 248 Use differencematcher for 'x and not y':
249 249
250 250 $ fileset -p optimized -s 'a* and not a1'
251 251 * optimized:
252 252 (minus
253 253 (symbol 'a*')
254 254 (symbol 'a1'))
255 255 * matcher:
256 256 <differencematcher
257 m1=<patternmatcher patterns='(?:a[^/]*$)'>,
258 m2=<patternmatcher patterns='(?:a1$)'>>
257 m1=<patternmatcher patterns='a[^/]*$'>,
258 m2=<patternmatcher patterns='a1$'>>
259 259 a2
260 260
261 261 $ fileset -p optimized -s '!binary() and a*'
262 262 * optimized:
263 263 (minus
264 264 (symbol 'a*')
265 265 (func
266 266 (symbol 'binary')
267 267 None))
268 268 * matcher:
269 269 <differencematcher
270 m1=<patternmatcher patterns='(?:a[^/]*$)'>,
270 m1=<patternmatcher patterns='a[^/]*$'>,
271 271 m2=<predicatenmatcher pred=binary>>
272 272 a1
273 273 a2
274 274
275 275 'x - y' is rewritten to 'x and not y' first so the operands can be reordered:
276 276
277 277 $ fileset -p analyzed -p optimized -s 'a* - a1'
278 278 * analyzed:
279 279 (and
280 280 (symbol 'a*')
281 281 (not
282 282 (symbol 'a1')))
283 283 * optimized:
284 284 (minus
285 285 (symbol 'a*')
286 286 (symbol 'a1'))
287 287 * matcher:
288 288 <differencematcher
289 m1=<patternmatcher patterns='(?:a[^/]*$)'>,
290 m2=<patternmatcher patterns='(?:a1$)'>>
289 m1=<patternmatcher patterns='a[^/]*$'>,
290 m2=<patternmatcher patterns='a1$'>>
291 291 a2
292 292
293 293 $ fileset -p analyzed -p optimized -s 'binary() - a*'
294 294 * analyzed:
295 295 (and
296 296 (func
297 297 (symbol 'binary')
298 298 None)
299 299 (not
300 300 (symbol 'a*')))
301 301 * optimized:
302 302 (and
303 303 (not
304 304 (symbol 'a*'))
305 305 (func
306 306 (symbol 'binary')
307 307 None))
308 308 * matcher:
309 309 <intersectionmatcher
310 310 m1=<predicatenmatcher
311 311 pred=<not
312 <patternmatcher patterns='(?:a[^/]*$)'>>>,
312 <patternmatcher patterns='a[^/]*$'>>>,
313 313 m2=<predicatenmatcher pred=binary>>
314 314
315 315 Test files status
316 316
317 317 $ rm a1
318 318 $ hg rm a2
319 319 $ echo b >> b2
320 320 $ hg cp b1 c1
321 321 $ echo c > c2
322 322 $ echo c > c3
323 323 $ cat > .hgignore <<EOF
324 324 > \.hgignore
325 325 > 2$
326 326 > EOF
327 327 $ fileset 'modified()'
328 328 b2
329 329 $ fileset 'added()'
330 330 c1
331 331 $ fileset 'removed()'
332 332 a2
333 333 $ fileset 'deleted()'
334 334 a1
335 335 $ fileset 'missing()'
336 336 a1
337 337 $ fileset 'unknown()'
338 338 c3
339 339 $ fileset 'ignored()'
340 340 .hgignore
341 341 c2
342 342 $ fileset 'hgignore()'
343 343 .hgignore
344 344 a2
345 345 b2
346 346 c2
347 347 $ fileset 'clean()'
348 348 b1
349 349 $ fileset 'copied()'
350 350 c1
351 351
352 352 Test files status in different revisions
353 353
354 354 $ hg status -m
355 355 M b2
356 356 $ fileset -r0 'revs("wdir()", modified())' --traceback
357 357 b2
358 358 $ hg status -a
359 359 A c1
360 360 $ fileset -r0 'revs("wdir()", added())'
361 361 c1
362 362 $ hg status --change 0 -a
363 363 A a1
364 364 A a2
365 365 A b1
366 366 A b2
367 367 $ hg status -mru
368 368 M b2
369 369 R a2
370 370 ? c3
371 371 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
372 372 a2
373 373 b2
374 374 $ fileset -r0 'added() or revs("wdir()", added())'
375 375 a1
376 376 a2
377 377 b1
378 378 b2
379 379 c1
380 380
381 381 Test insertion of status hints
382 382
383 383 $ fileset -p optimized 'added()'
384 384 * optimized:
385 385 (withstatus
386 386 (func
387 387 (symbol 'added')
388 388 None)
389 389 (string 'added'))
390 390 c1
391 391
392 392 $ fileset -p optimized 'a* & removed()'
393 393 * optimized:
394 394 (and
395 395 (symbol 'a*')
396 396 (withstatus
397 397 (func
398 398 (symbol 'removed')
399 399 None)
400 400 (string 'removed')))
401 401 a2
402 402
403 403 $ fileset -p optimized 'a* - removed()'
404 404 * optimized:
405 405 (minus
406 406 (symbol 'a*')
407 407 (withstatus
408 408 (func
409 409 (symbol 'removed')
410 410 None)
411 411 (string 'removed')))
412 412 a1
413 413
414 414 $ fileset -p analyzed -p optimized '(added() + removed()) - a*'
415 415 * analyzed:
416 416 (and
417 417 (withstatus
418 418 (or
419 419 (func
420 420 (symbol 'added')
421 421 None)
422 422 (func
423 423 (symbol 'removed')
424 424 None))
425 425 (string 'added removed'))
426 426 (not
427 427 (symbol 'a*')))
428 428 * optimized:
429 429 (and
430 430 (not
431 431 (symbol 'a*'))
432 432 (withstatus
433 433 (or
434 434 (func
435 435 (symbol 'added')
436 436 None)
437 437 (func
438 438 (symbol 'removed')
439 439 None))
440 440 (string 'added removed')))
441 441 c1
442 442
443 443 $ fileset -p optimized 'a* + b* + added() + unknown()'
444 444 * optimized:
445 445 (withstatus
446 446 (or
447 447 (patterns
448 448 (symbol 'a*')
449 449 (symbol 'b*'))
450 450 (func
451 451 (symbol 'added')
452 452 None)
453 453 (func
454 454 (symbol 'unknown')
455 455 None))
456 456 (string 'added unknown'))
457 457 a1
458 458 a2
459 459 b1
460 460 b2
461 461 c1
462 462 c3
463 463
464 464 $ fileset -p analyzed -p optimized 'removed() & missing() & a*'
465 465 * analyzed:
466 466 (and
467 467 (withstatus
468 468 (and
469 469 (func
470 470 (symbol 'removed')
471 471 None)
472 472 (func
473 473 (symbol 'missing')
474 474 None))
475 475 (string 'removed missing'))
476 476 (symbol 'a*'))
477 477 * optimized:
478 478 (and
479 479 (symbol 'a*')
480 480 (withstatus
481 481 (and
482 482 (func
483 483 (symbol 'removed')
484 484 None)
485 485 (func
486 486 (symbol 'missing')
487 487 None))
488 488 (string 'removed missing')))
489 489
490 490 $ fileset -p optimized 'clean() & revs(0, added())'
491 491 * optimized:
492 492 (and
493 493 (withstatus
494 494 (func
495 495 (symbol 'clean')
496 496 None)
497 497 (string 'clean'))
498 498 (func
499 499 (symbol 'revs')
500 500 (list
501 501 (symbol '0')
502 502 (withstatus
503 503 (func
504 504 (symbol 'added')
505 505 None)
506 506 (string 'added')))))
507 507 b1
508 508
509 509 $ fileset -p optimized 'clean() & status(null, 0, b* & added())'
510 510 * optimized:
511 511 (and
512 512 (withstatus
513 513 (func
514 514 (symbol 'clean')
515 515 None)
516 516 (string 'clean'))
517 517 (func
518 518 (symbol 'status')
519 519 (list
520 520 (symbol 'null')
521 521 (symbol '0')
522 522 (and
523 523 (symbol 'b*')
524 524 (withstatus
525 525 (func
526 526 (symbol 'added')
527 527 None)
528 528 (string 'added'))))))
529 529 b1
530 530
531 531 Test files properties
532 532
533 533 >>> open('bin', 'wb').write(b'\0a') and None
534 534 $ fileset 'binary()'
535 535 bin
536 536 $ fileset 'binary() and unknown()'
537 537 bin
538 538 $ echo '^bin$' >> .hgignore
539 539 $ fileset 'binary() and ignored()'
540 540 bin
541 541 $ hg add bin
542 542 $ fileset 'binary()'
543 543 bin
544 544
545 545 $ fileset -p optimized -s 'binary() and b*'
546 546 * optimized:
547 547 (and
548 548 (symbol 'b*')
549 549 (func
550 550 (symbol 'binary')
551 551 None))
552 552 * matcher:
553 553 <intersectionmatcher
554 m1=<patternmatcher patterns='(?:b[^/]*$)'>,
554 m1=<patternmatcher patterns='b[^/]*$'>,
555 555 m2=<predicatenmatcher pred=binary>>
556 556 bin
557 557
558 558 $ fileset 'grep("b{1}")'
559 559 .hgignore
560 560 b1
561 561 b2
562 562 c1
563 563 $ fileset 'grep("missingparens(")'
564 564 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \)).* (re)
565 565 [255]
566 566
567 567 #if execbit
568 568 $ chmod +x b2
569 569 $ fileset 'exec()'
570 570 b2
571 571 #endif
572 572
573 573 #if symlink
574 574 $ ln -s b2 b2link
575 575 $ fileset 'symlink() and unknown()'
576 576 b2link
577 577 $ hg add b2link
578 578 #endif
579 579
580 580 #if no-windows
581 581 $ echo foo > con.xml
582 582 $ fileset 'not portable()'
583 583 con.xml
584 584 $ hg --config ui.portablefilenames=ignore add con.xml
585 585 #endif
586 586
587 587 >>> open('1k', 'wb').write(b' '*1024) and None
588 588 >>> open('2k', 'wb').write(b' '*2048) and None
589 589 $ hg add 1k 2k
590 590 $ fileset 'size("bar")'
591 591 hg: parse error: couldn't parse size: bar
592 592 [255]
593 593 $ fileset '(1k, 2k)'
594 594 hg: parse error: can't use a list in this context
595 595 (see 'hg help "filesets.x or y"')
596 596 [255]
597 597 $ fileset 'size(1k)'
598 598 1k
599 599 $ fileset '(1k or 2k) and size("< 2k")'
600 600 1k
601 601 $ fileset '(1k or 2k) and size("<=2k")'
602 602 1k
603 603 2k
604 604 $ fileset '(1k or 2k) and size("> 1k")'
605 605 2k
606 606 $ fileset '(1k or 2k) and size(">=1K")'
607 607 1k
608 608 2k
609 609 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
610 610 1k
611 611 $ fileset 'size("1M")'
612 612 $ fileset 'size("1 GB")'
613 613
614 614 Test merge states
615 615
616 616 $ hg ci -m manychanges
617 617 $ hg file -r . 'set:copied() & modified()'
618 618 [1]
619 619 $ hg up -C 0
620 620 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
621 621 $ echo c >> b2
622 622 $ hg ci -m diverging b2
623 623 created new head
624 624 $ fileset 'resolved()'
625 625 $ fileset 'unresolved()'
626 626 $ hg merge
627 627 merging b2
628 628 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
629 629 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
630 630 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
631 631 [1]
632 632 $ fileset 'resolved()'
633 633 $ fileset 'unresolved()'
634 634 b2
635 635 $ echo e > b2
636 636 $ hg resolve -m b2
637 637 (no more unresolved files)
638 638 $ fileset 'resolved()'
639 639 b2
640 640 $ fileset 'unresolved()'
641 641 $ hg ci -m merge
642 642
643 643 Test subrepo predicate
644 644
645 645 $ hg init sub
646 646 $ echo a > sub/suba
647 647 $ hg -R sub add sub/suba
648 648 $ hg -R sub ci -m sub
649 649 $ echo 'sub = sub' > .hgsub
650 650 $ hg init sub2
651 651 $ echo b > sub2/b
652 652 $ hg -R sub2 ci -Am sub2
653 653 adding b
654 654 $ echo 'sub2 = sub2' >> .hgsub
655 655 $ fileset 'subrepo()'
656 656 $ hg add .hgsub
657 657 $ fileset 'subrepo()'
658 658 sub
659 659 sub2
660 660 $ fileset 'subrepo("sub")'
661 661 sub
662 662 $ fileset 'subrepo("glob:*")'
663 663 sub
664 664 sub2
665 665 $ hg ci -m subrepo
666 666
667 667 Test that .hgsubstate is updated as appropriate during a conversion. The
668 668 saverev property is enough to alter the hashes of the subrepo.
669 669
670 670 $ hg init ../converted
671 671 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
672 672 > sub ../converted/sub
673 673 initializing destination ../converted/sub repository
674 674 scanning source...
675 675 sorting...
676 676 converting...
677 677 0 sub
678 678 $ hg clone -U sub2 ../converted/sub2
679 679 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
680 680 > . ../converted
681 681 scanning source...
682 682 sorting...
683 683 converting...
684 684 4 addfiles
685 685 3 manychanges
686 686 2 diverging
687 687 1 merge
688 688 0 subrepo
689 689 no ".hgsubstate" updates will be made for "sub2"
690 690 $ hg up -q -R ../converted -r tip
691 691 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
692 692 a
693 693 b
694 694 $ oldnode=`hg log -r tip -T "{node}\n"`
695 695 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
696 696 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
697 697
698 698 Test with a revision
699 699
700 700 $ hg log -G --template '{rev} {desc}\n'
701 701 @ 4 subrepo
702 702 |
703 703 o 3 merge
704 704 |\
705 705 | o 2 diverging
706 706 | |
707 707 o | 1 manychanges
708 708 |/
709 709 o 0 addfiles
710 710
711 711 $ echo unknown > unknown
712 712 $ fileset -r1 'modified()'
713 713 b2
714 714 $ fileset -r1 'added() and c1'
715 715 c1
716 716 $ fileset -r1 'removed()'
717 717 a2
718 718 $ fileset -r1 'deleted()'
719 719 $ fileset -r1 'unknown()'
720 720 $ fileset -r1 'ignored()'
721 721 $ fileset -r1 'hgignore()'
722 722 .hgignore
723 723 a2
724 724 b2
725 725 bin
726 726 c2
727 727 sub2
728 728 $ fileset -r1 'binary()'
729 729 bin
730 730 $ fileset -r1 'size(1k)'
731 731 1k
732 732 $ fileset -r3 'resolved()'
733 733 $ fileset -r3 'unresolved()'
734 734
735 735 #if execbit
736 736 $ fileset -r1 'exec()'
737 737 b2
738 738 #endif
739 739
740 740 #if symlink
741 741 $ fileset -r1 'symlink()'
742 742 b2link
743 743 #endif
744 744
745 745 #if no-windows
746 746 $ fileset -r1 'not portable()'
747 747 con.xml
748 748 $ hg forget 'con.xml'
749 749 #endif
750 750
751 751 $ fileset -r4 'subrepo("re:su.*")'
752 752 sub
753 753 sub2
754 754 $ fileset -r4 'subrepo(re:su.*)'
755 755 sub
756 756 sub2
757 757 $ fileset -r4 'subrepo("sub")'
758 758 sub
759 759 $ fileset -r4 'b2 or c1'
760 760 b2
761 761 c1
762 762
763 763 >>> open('dos', 'wb').write(b"dos\r\n") and None
764 764 >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
765 765 >>> open('mac', 'wb').write(b"mac\r") and None
766 766 $ hg add dos mixed mac
767 767
768 768 (remove a1, to examine safety of 'eol' on removed files)
769 769 $ rm a1
770 770
771 771 $ fileset 'eol(dos)'
772 772 dos
773 773 mixed
774 774 $ fileset 'eol(unix)'
775 775 .hgignore
776 776 .hgsub
777 777 .hgsubstate
778 778 b1
779 779 b2
780 780 b2.orig
781 781 c1
782 782 c2
783 783 c3
784 784 con.xml (no-windows !)
785 785 mixed
786 786 unknown
787 787 $ fileset 'eol(mac)'
788 788 mac
789 789
790 790 Test safety of 'encoding' on removed files
791 791
792 792 $ fileset 'encoding("ascii")'
793 793 .hgignore
794 794 .hgsub
795 795 .hgsubstate
796 796 1k
797 797 2k
798 798 b1
799 799 b2
800 800 b2.orig
801 801 b2link (symlink !)
802 802 bin
803 803 c1
804 804 c2
805 805 c3
806 806 con.xml (no-windows !)
807 807 dos
808 808 mac
809 809 mixed
810 810 unknown
811 811
812 812 Test 'revs(...)'
813 813 ================
814 814
815 815 small reminder of the repository state
816 816
817 817 $ hg log -G
818 818 @ changeset: 4:* (glob)
819 819 | tag: tip
820 820 | user: test
821 821 | date: Thu Jan 01 00:00:00 1970 +0000
822 822 | summary: subrepo
823 823 |
824 824 o changeset: 3:* (glob)
825 825 |\ parent: 2:55b05bdebf36
826 826 | | parent: 1:* (glob)
827 827 | | user: test
828 828 | | date: Thu Jan 01 00:00:00 1970 +0000
829 829 | | summary: merge
830 830 | |
831 831 | o changeset: 2:55b05bdebf36
832 832 | | parent: 0:8a9576c51c1f
833 833 | | user: test
834 834 | | date: Thu Jan 01 00:00:00 1970 +0000
835 835 | | summary: diverging
836 836 | |
837 837 o | changeset: 1:* (glob)
838 838 |/ user: test
839 839 | date: Thu Jan 01 00:00:00 1970 +0000
840 840 | summary: manychanges
841 841 |
842 842 o changeset: 0:8a9576c51c1f
843 843 user: test
844 844 date: Thu Jan 01 00:00:00 1970 +0000
845 845 summary: addfiles
846 846
847 847 $ hg status --change 0
848 848 A a1
849 849 A a2
850 850 A b1
851 851 A b2
852 852 $ hg status --change 1
853 853 M b2
854 854 A 1k
855 855 A 2k
856 856 A b2link (no-windows !)
857 857 A bin
858 858 A c1
859 859 A con.xml (no-windows !)
860 860 R a2
861 861 $ hg status --change 2
862 862 M b2
863 863 $ hg status --change 3
864 864 M b2
865 865 A 1k
866 866 A 2k
867 867 A b2link (no-windows !)
868 868 A bin
869 869 A c1
870 870 A con.xml (no-windows !)
871 871 R a2
872 872 $ hg status --change 4
873 873 A .hgsub
874 874 A .hgsubstate
875 875 $ hg status
876 876 A dos
877 877 A mac
878 878 A mixed
879 879 R con.xml (no-windows !)
880 880 ! a1
881 881 ? b2.orig
882 882 ? c3
883 883 ? unknown
884 884
885 885 Test files at -r0 should be filtered by files at wdir
886 886 -----------------------------------------------------
887 887
888 888 $ fileset -r0 'tracked() and revs("wdir()", tracked())'
889 889 a1
890 890 b1
891 891 b2
892 892
893 893 Test that "revs()" work at all
894 894 ------------------------------
895 895
896 896 $ fileset "revs('2', modified())"
897 897 b2
898 898
899 899 Test that "revs()" work for file missing in the working copy/current context
900 900 ----------------------------------------------------------------------------
901 901
902 902 (a2 not in working copy)
903 903
904 904 $ fileset "revs('0', added())"
905 905 a1
906 906 a2
907 907 b1
908 908 b2
909 909
910 910 (none of the file exist in "0")
911 911
912 912 $ fileset -r 0 "revs('4', added())"
913 913 .hgsub
914 914 .hgsubstate
915 915
916 916 Call with empty revset
917 917 --------------------------
918 918
919 919 $ fileset "revs('2-2', modified())"
920 920
921 921 Call with revset matching multiple revs
922 922 ---------------------------------------
923 923
924 924 $ fileset "revs('0+4', added())"
925 925 .hgsub
926 926 .hgsubstate
927 927 a1
928 928 a2
929 929 b1
930 930 b2
931 931
932 932 overlapping set
933 933
934 934 $ fileset "revs('1+2', modified())"
935 935 b2
936 936
937 937 test 'status(...)'
938 938 =================
939 939
940 940 Simple case
941 941 -----------
942 942
943 943 $ fileset "status(3, 4, added())"
944 944 .hgsub
945 945 .hgsubstate
946 946
947 947 use rev to restrict matched file
948 948 -----------------------------------------
949 949
950 950 $ hg status --removed --rev 0 --rev 1
951 951 R a2
952 952 $ fileset "status(0, 1, removed())"
953 953 a2
954 954 $ fileset "tracked() and status(0, 1, removed())"
955 955 $ fileset -r 4 "status(0, 1, removed())"
956 956 a2
957 957 $ fileset -r 4 "tracked() and status(0, 1, removed())"
958 958 $ fileset "revs('4', tracked() and status(0, 1, removed()))"
959 959 $ fileset "revs('0', tracked() and status(0, 1, removed()))"
960 960 a2
961 961
962 962 check wdir()
963 963 ------------
964 964
965 965 $ hg status --removed --rev 4
966 966 R con.xml (no-windows !)
967 967 $ fileset "status(4, 'wdir()', removed())"
968 968 con.xml (no-windows !)
969 969
970 970 $ hg status --removed --rev 2
971 971 R a2
972 972 $ fileset "status('2', 'wdir()', removed())"
973 973 a2
974 974
975 975 test backward status
976 976 --------------------
977 977
978 978 $ hg status --removed --rev 0 --rev 4
979 979 R a2
980 980 $ hg status --added --rev 4 --rev 0
981 981 A a2
982 982 $ fileset "status(4, 0, added())"
983 983 a2
984 984
985 985 test cross branch status
986 986 ------------------------
987 987
988 988 $ hg status --added --rev 1 --rev 2
989 989 A a2
990 990 $ fileset "status(1, 2, added())"
991 991 a2
992 992
993 993 test with multi revs revset
994 994 ---------------------------
995 995 $ hg status --added --rev 0:1 --rev 3:4
996 996 A .hgsub
997 997 A .hgsubstate
998 998 A 1k
999 999 A 2k
1000 1000 A b2link (no-windows !)
1001 1001 A bin
1002 1002 A c1
1003 1003 A con.xml (no-windows !)
1004 1004 $ fileset "status('0:1', '3:4', added())"
1005 1005 .hgsub
1006 1006 .hgsubstate
1007 1007 1k
1008 1008 2k
1009 1009 b2link (no-windows !)
1010 1010 bin
1011 1011 c1
1012 1012 con.xml (no-windows !)
1013 1013
1014 1014 tests with empty value
1015 1015 ----------------------
1016 1016
1017 1017 Fully empty revset
1018 1018
1019 1019 $ fileset "status('', '4', added())"
1020 1020 hg: parse error: first argument to status must be a revision
1021 1021 [255]
1022 1022 $ fileset "status('2', '', added())"
1023 1023 hg: parse error: second argument to status must be a revision
1024 1024 [255]
1025 1025
1026 1026 Empty revset will error at the revset layer
1027 1027
1028 1028 $ fileset "status(' ', '4', added())"
1029 1029 hg: parse error at 1: not a prefix: end
1030 1030 (
1031 1031 ^ here)
1032 1032 [255]
1033 1033 $ fileset "status('2', ' ', added())"
1034 1034 hg: parse error at 1: not a prefix: end
1035 1035 (
1036 1036 ^ here)
1037 1037 [255]
@@ -1,339 +1,339 b''
1 1 $ hg init ignorerepo
2 2 $ cd ignorerepo
3 3
4 4 debugignore with no hgignore should be deterministic:
5 5 $ hg debugignore
6 6 <nevermatcher>
7 7
8 8 Issue562: .hgignore requires newline at end:
9 9
10 10 $ touch foo
11 11 $ touch bar
12 12 $ touch baz
13 13 $ cat > makeignore.py <<EOF
14 14 > f = open(".hgignore", "w")
15 15 > f.write("ignore\n")
16 16 > f.write("foo\n")
17 17 > # No EOL here
18 18 > f.write("bar")
19 19 > f.close()
20 20 > EOF
21 21
22 22 $ "$PYTHON" makeignore.py
23 23
24 24 Should display baz only:
25 25
26 26 $ hg status
27 27 ? baz
28 28
29 29 $ rm foo bar baz .hgignore makeignore.py
30 30
31 31 $ touch a.o
32 32 $ touch a.c
33 33 $ touch syntax
34 34 $ mkdir dir
35 35 $ touch dir/a.o
36 36 $ touch dir/b.o
37 37 $ touch dir/c.o
38 38
39 39 $ hg add dir/a.o
40 40 $ hg commit -m 0
41 41 $ hg add dir/b.o
42 42
43 43 $ hg status
44 44 A dir/b.o
45 45 ? a.c
46 46 ? a.o
47 47 ? dir/c.o
48 48 ? syntax
49 49
50 50 $ echo "*.o" > .hgignore
51 51 $ hg status
52 52 abort: $TESTTMP/ignorerepo/.hgignore: invalid pattern (relre): *.o (glob)
53 53 [255]
54 54
55 55 Ensure given files are relative to cwd
56 56
57 57 $ echo "dir/.*\.o" > .hgignore
58 58 $ hg status -i
59 59 I dir/c.o
60 60
61 61 $ hg debugignore dir/c.o dir/missing.o
62 62 dir/c.o is ignored
63 63 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
64 64 dir/missing.o is ignored
65 65 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
66 66 $ cd dir
67 67 $ hg debugignore c.o missing.o
68 68 c.o is ignored
69 69 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
70 70 missing.o is ignored
71 71 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
72 72
73 73 For icasefs, inexact matches also work, except for missing files
74 74
75 75 #if icasefs
76 76 $ hg debugignore c.O missing.O
77 77 c.o is ignored
78 78 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
79 79 missing.O is not ignored
80 80 #endif
81 81
82 82 $ cd ..
83 83
84 84 $ echo ".*\.o" > .hgignore
85 85 $ hg status
86 86 A dir/b.o
87 87 ? .hgignore
88 88 ? a.c
89 89 ? syntax
90 90
91 91 Ensure that comments work:
92 92
93 93 $ touch 'foo#bar' 'quux#'
94 94 #if no-windows
95 95 $ touch 'baz\#wat'
96 96 #endif
97 97 $ cat <<'EOF' >> .hgignore
98 98 > # full-line comment
99 99 > # whitespace-only comment line
100 100 > syntax# pattern, no whitespace, then comment
101 101 > a.c # pattern, then whitespace, then comment
102 102 > baz\\# # escaped comment character
103 103 > foo\#b # escaped comment character
104 104 > quux\## escaped comment character at end of name
105 105 > EOF
106 106 $ hg status
107 107 A dir/b.o
108 108 ? .hgignore
109 109 $ rm 'foo#bar' 'quux#'
110 110 #if no-windows
111 111 $ rm 'baz\#wat'
112 112 #endif
113 113
114 114 Check that '^\.' does not ignore the root directory:
115 115
116 116 $ echo "^\." > .hgignore
117 117 $ hg status
118 118 A dir/b.o
119 119 ? a.c
120 120 ? a.o
121 121 ? dir/c.o
122 122 ? syntax
123 123
124 124 Test that patterns from ui.ignore options are read:
125 125
126 126 $ echo > .hgignore
127 127 $ cat >> $HGRCPATH << EOF
128 128 > [ui]
129 129 > ignore.other = $TESTTMP/ignorerepo/.hg/testhgignore
130 130 > EOF
131 131 $ echo "glob:**.o" > .hg/testhgignore
132 132 $ hg status
133 133 A dir/b.o
134 134 ? .hgignore
135 135 ? a.c
136 136 ? syntax
137 137
138 138 empty out testhgignore
139 139 $ echo > .hg/testhgignore
140 140
141 141 Test relative ignore path (issue4473):
142 142
143 143 $ cat >> $HGRCPATH << EOF
144 144 > [ui]
145 145 > ignore.relative = .hg/testhgignorerel
146 146 > EOF
147 147 $ echo "glob:*.o" > .hg/testhgignorerel
148 148 $ cd dir
149 149 $ hg status
150 150 A dir/b.o
151 151 ? .hgignore
152 152 ? a.c
153 153 ? syntax
154 154
155 155 $ cd ..
156 156 $ echo > .hg/testhgignorerel
157 157 $ echo "syntax: glob" > .hgignore
158 158 $ echo "re:.*\.o" >> .hgignore
159 159 $ hg status
160 160 A dir/b.o
161 161 ? .hgignore
162 162 ? a.c
163 163 ? syntax
164 164
165 165 $ echo "syntax: invalid" > .hgignore
166 166 $ hg status
167 167 $TESTTMP/ignorerepo/.hgignore: ignoring invalid syntax 'invalid'
168 168 A dir/b.o
169 169 ? .hgignore
170 170 ? a.c
171 171 ? a.o
172 172 ? dir/c.o
173 173 ? syntax
174 174
175 175 $ echo "syntax: glob" > .hgignore
176 176 $ echo "*.o" >> .hgignore
177 177 $ hg status
178 178 A dir/b.o
179 179 ? .hgignore
180 180 ? a.c
181 181 ? syntax
182 182
183 183 $ echo "relglob:syntax*" > .hgignore
184 184 $ hg status
185 185 A dir/b.o
186 186 ? .hgignore
187 187 ? a.c
188 188 ? a.o
189 189 ? dir/c.o
190 190
191 191 $ echo "relglob:*" > .hgignore
192 192 $ hg status
193 193 A dir/b.o
194 194
195 195 $ cd dir
196 196 $ hg status .
197 197 A b.o
198 198
199 199 $ hg debugignore
200 <includematcher includes='(?:(?:|.*/)[^/]*(?:/|$))'>
200 <includematcher includes='(?:|.*/)[^/]*(?:/|$)'>
201 201
202 202 $ hg debugignore b.o
203 203 b.o is ignored
204 204 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: '*') (glob)
205 205
206 206 $ cd ..
207 207
208 208 Check patterns that match only the directory
209 209
210 210 "(fsmonitor !)" below assumes that fsmonitor is enabled with
211 211 "walk_on_invalidate = false" (default), which doesn't involve
212 212 re-walking whole repository at detection of .hgignore change.
213 213
214 214 $ echo "^dir\$" > .hgignore
215 215 $ hg status
216 216 A dir/b.o
217 217 ? .hgignore
218 218 ? a.c
219 219 ? a.o
220 220 ? dir/c.o (fsmonitor !)
221 221 ? syntax
222 222
223 223 Check recursive glob pattern matches no directories (dir/**/c.o matches dir/c.o)
224 224
225 225 $ echo "syntax: glob" > .hgignore
226 226 $ echo "dir/**/c.o" >> .hgignore
227 227 $ touch dir/c.o
228 228 $ mkdir dir/subdir
229 229 $ touch dir/subdir/c.o
230 230 $ hg status
231 231 A dir/b.o
232 232 ? .hgignore
233 233 ? a.c
234 234 ? a.o
235 235 ? syntax
236 236 $ hg debugignore a.c
237 237 a.c is not ignored
238 238 $ hg debugignore dir/c.o
239 239 dir/c.o is ignored
240 240 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 2: 'dir/**/c.o') (glob)
241 241
242 242 Check using 'include:' in ignore file
243 243
244 244 $ hg purge --all --config extensions.purge=
245 245 $ touch foo.included
246 246
247 247 $ echo ".*.included" > otherignore
248 248 $ hg status -I "include:otherignore"
249 249 ? foo.included
250 250
251 251 $ echo "include:otherignore" >> .hgignore
252 252 $ hg status
253 253 A dir/b.o
254 254 ? .hgignore
255 255 ? otherignore
256 256
257 257 Check recursive uses of 'include:'
258 258
259 259 $ echo "include:nested/ignore" >> otherignore
260 260 $ mkdir nested
261 261 $ echo "glob:*ignore" > nested/ignore
262 262 $ hg status
263 263 A dir/b.o
264 264
265 265 $ cp otherignore goodignore
266 266 $ echo "include:badignore" >> otherignore
267 267 $ hg status
268 268 skipping unreadable pattern file 'badignore': $ENOENT$
269 269 A dir/b.o
270 270
271 271 $ mv goodignore otherignore
272 272
273 273 Check using 'include:' while in a non-root directory
274 274
275 275 $ cd ..
276 276 $ hg -R ignorerepo status
277 277 A dir/b.o
278 278 $ cd ignorerepo
279 279
280 280 Check including subincludes
281 281
282 282 $ hg revert -q --all
283 283 $ hg purge --all --config extensions.purge=
284 284 $ echo ".hgignore" > .hgignore
285 285 $ mkdir dir1 dir2
286 286 $ touch dir1/file1 dir1/file2 dir2/file1 dir2/file2
287 287 $ echo "subinclude:dir2/.hgignore" >> .hgignore
288 288 $ echo "glob:file*2" > dir2/.hgignore
289 289 $ hg status
290 290 ? dir1/file1
291 291 ? dir1/file2
292 292 ? dir2/file1
293 293
294 294 Check including subincludes with regexs
295 295
296 296 $ echo "subinclude:dir1/.hgignore" >> .hgignore
297 297 $ echo "regexp:f.le1" > dir1/.hgignore
298 298
299 299 $ hg status
300 300 ? dir1/file2
301 301 ? dir2/file1
302 302
303 303 Check multiple levels of sub-ignores
304 304
305 305 $ mkdir dir1/subdir
306 306 $ touch dir1/subdir/subfile1 dir1/subdir/subfile3 dir1/subdir/subfile4
307 307 $ echo "subinclude:subdir/.hgignore" >> dir1/.hgignore
308 308 $ echo "glob:subfil*3" >> dir1/subdir/.hgignore
309 309
310 310 $ hg status
311 311 ? dir1/file2
312 312 ? dir1/subdir/subfile4
313 313 ? dir2/file1
314 314
315 315 Check include subignore at the same level
316 316
317 317 $ mv dir1/subdir/.hgignore dir1/.hgignoretwo
318 318 $ echo "regexp:f.le1" > dir1/.hgignore
319 319 $ echo "subinclude:.hgignoretwo" >> dir1/.hgignore
320 320 $ echo "glob:file*2" > dir1/.hgignoretwo
321 321
322 322 $ hg status | grep file2
323 323 [1]
324 324 $ hg debugignore dir1/file2
325 325 dir1/file2 is ignored
326 326 (ignore rule in dir2/.hgignore, line 1: 'file*2')
327 327
328 328 #if windows
329 329
330 330 Windows paths are accepted on input
331 331
332 332 $ rm dir1/.hgignore
333 333 $ echo "dir1/file*" >> .hgignore
334 334 $ hg debugignore "dir1\file2"
335 335 dir1\file2 is ignored
336 336 (ignore rule in $TESTTMP\ignorerepo\.hgignore, line 4: 'dir1/file*')
337 337 $ hg up -qC .
338 338
339 339 #endif
@@ -1,106 +1,106 b''
1 1 #testcases flat tree
2 2
3 3 $ . "$TESTDIR/narrow-library.sh"
4 4
5 5 #if tree
6 6 $ cat << EOF >> $HGRCPATH
7 7 > [experimental]
8 8 > treemanifest = 1
9 9 > EOF
10 10 #endif
11 11
12 12 create full repo
13 13
14 14 $ hg init master
15 15 $ cd master
16 16
17 17 $ mkdir inside
18 18 $ echo inside > inside/f1
19 19 $ mkdir outside
20 20 $ echo outside > outside/f1
21 21 $ hg ci -Aqm 'initial'
22 22
23 23 $ echo modified > inside/f1
24 24 $ hg ci -qm 'modify inside'
25 25
26 26 $ echo modified > outside/f1
27 27 $ hg ci -qm 'modify outside'
28 28
29 29 $ cd ..
30 30
31 31 (The lfs extension does nothing here, but this test ensures that its hook that
32 32 determines whether to add the lfs requirement, respects the narrow boundaries.)
33 33
34 34 $ hg --config extensions.lfs= clone --narrow ssh://user@dummy/master narrow \
35 35 > --include inside
36 36 requesting all changes
37 37 adding changesets
38 38 adding manifests
39 39 adding file changes
40 40 added 3 changesets with 2 changes to 1 files
41 41 new changesets *:* (glob)
42 42 updating to branch default
43 43 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 44 $ cd narrow
45 45
46 46 $ hg update -q 0
47 47
48 48 Can not modify dirstate outside
49 49
50 50 $ mkdir outside
51 51 $ touch outside/f1
52 52 $ hg debugwalk -v -I 'relglob:f1'
53 53 * matcher:
54 <includematcher includes='(?:(?:|.*/)f1(?:/|$))'>
54 <includematcher includes='(?:|.*/)f1(?:/|$)'>
55 55 f inside/f1 inside/f1
56 56 $ hg add .
57 57 $ hg add outside/f1
58 58 abort: cannot track 'outside/f1' - it is outside the narrow clone
59 59 [255]
60 60 $ touch outside/f3
61 61 $ hg add outside/f3
62 62 abort: cannot track 'outside/f3' - it is outside the narrow clone
63 63 [255]
64 64
65 65 But adding a truly excluded file shouldn't count
66 66
67 67 $ hg add outside/f3 -X outside/f3
68 68
69 69 $ rm -r outside
70 70
71 71 Can modify dirstate inside
72 72
73 73 $ echo modified > inside/f1
74 74 $ touch inside/f3
75 75 $ hg add inside/f3
76 76 $ hg status
77 77 M inside/f1
78 78 A inside/f3
79 79 $ hg revert -qC .
80 80 $ rm inside/f3
81 81
82 82 Can commit changes inside. Leaves outside unchanged.
83 83
84 84 $ hg update -q 'desc("initial")'
85 85 $ echo modified2 > inside/f1
86 86 $ hg manifest --debug
87 87 4d6a634d5ba06331a60c29ee0db8412490a54fcd 644 inside/f1
88 88 7fb3bb6356d28d4dc352c5ba52d7350a81b6bd46 644 outside/f1 (flat !)
89 89 d0f2f706468ab0e8bec7af87446835fb1b13511b 755 d outside/ (tree !)
90 90 $ hg commit -m 'modify inside/f1'
91 91 created new head
92 92 $ hg files -r .
93 93 inside/f1
94 94 $ hg manifest --debug
95 95 3f4197b4a11b9016e77ebc47fe566944885fd11b 644 inside/f1
96 96 7fb3bb6356d28d4dc352c5ba52d7350a81b6bd46 644 outside/f1 (flat !)
97 97 d0f2f706468ab0e8bec7af87446835fb1b13511b 755 d outside/ (tree !)
98 98 Some filesystems (notably FAT/exFAT only store timestamps with 2
99 99 seconds of precision, so by sleeping for 3 seconds, we can ensure that
100 100 the timestamps of files stored by dirstate will appear older than the
101 101 dirstate file, and therefore we'll be able to get stable output from
102 102 debugdirstate. If we don't do this, the test can be slightly flaky.
103 103 $ sleep 3
104 104 $ hg status
105 105 $ hg debugdirstate --no-dates
106 106 n 644 10 set inside/f1
@@ -1,652 +1,652 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 -v
32 32 * matcher:
33 33 <alwaysmatcher>
34 34 f beans/black beans/black
35 35 f beans/borlotti beans/borlotti
36 36 f beans/kidney beans/kidney
37 37 f beans/navy beans/navy
38 38 f beans/pinto beans/pinto
39 39 f beans/turtle beans/turtle
40 40 f fennel fennel
41 41 f fenugreek fenugreek
42 42 f fiddlehead fiddlehead
43 43 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
44 44 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
45 45 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
46 46 f mammals/skunk mammals/skunk
47 47 $ hg debugwalk -v -I.
48 48 * matcher:
49 <includematcher includes='(?:)'>
49 <includematcher includes=''>
50 50 f beans/black beans/black
51 51 f beans/borlotti beans/borlotti
52 52 f beans/kidney beans/kidney
53 53 f beans/navy beans/navy
54 54 f beans/pinto beans/pinto
55 55 f beans/turtle beans/turtle
56 56 f fennel fennel
57 57 f fenugreek fenugreek
58 58 f fiddlehead fiddlehead
59 59 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
60 60 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
61 61 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
62 62 f mammals/skunk mammals/skunk
63 63
64 64 $ cd mammals
65 65 $ hg debugwalk -v
66 66 * matcher:
67 67 <alwaysmatcher>
68 68 f beans/black ../beans/black
69 69 f beans/borlotti ../beans/borlotti
70 70 f beans/kidney ../beans/kidney
71 71 f beans/navy ../beans/navy
72 72 f beans/pinto ../beans/pinto
73 73 f beans/turtle ../beans/turtle
74 74 f fennel ../fennel
75 75 f fenugreek ../fenugreek
76 76 f fiddlehead ../fiddlehead
77 77 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
78 78 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
79 79 f mammals/Procyonidae/raccoon Procyonidae/raccoon
80 80 f mammals/skunk skunk
81 81 $ hg debugwalk -v -X ../beans
82 82 * matcher:
83 83 <differencematcher
84 84 m1=<alwaysmatcher>,
85 m2=<includematcher includes='(?:beans(?:/|$))'>>
85 m2=<includematcher includes='beans(?:/|$)'>>
86 86 f fennel ../fennel
87 87 f fenugreek ../fenugreek
88 88 f fiddlehead ../fiddlehead
89 89 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
90 90 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
91 91 f mammals/Procyonidae/raccoon Procyonidae/raccoon
92 92 f mammals/skunk skunk
93 93 $ hg debugwalk -v -I '*k'
94 94 * matcher:
95 <includematcher includes='(?:mammals/[^/]*k(?:/|$))'>
95 <includematcher includes='mammals/[^/]*k(?:/|$)'>
96 96 f mammals/skunk skunk
97 97 $ hg debugwalk -v -I 'glob:*k'
98 98 * matcher:
99 <includematcher includes='(?:mammals/[^/]*k(?:/|$))'>
99 <includematcher includes='mammals/[^/]*k(?:/|$)'>
100 100 f mammals/skunk skunk
101 101 $ hg debugwalk -v -I 'relglob:*k'
102 102 * matcher:
103 <includematcher includes='(?:(?:|.*/)[^/]*k(?:/|$))'>
103 <includematcher includes='(?:|.*/)[^/]*k(?:/|$)'>
104 104 f beans/black ../beans/black
105 105 f fenugreek ../fenugreek
106 106 f mammals/skunk skunk
107 107 $ hg debugwalk -v -I 'relglob:*k' .
108 108 * matcher:
109 109 <intersectionmatcher
110 m1=<patternmatcher patterns='(?:mammals(?:/|$))'>,
111 m2=<includematcher includes='(?:(?:|.*/)[^/]*k(?:/|$))'>>
110 m1=<patternmatcher patterns='mammals(?:/|$)'>,
111 m2=<includematcher includes='(?:|.*/)[^/]*k(?:/|$)'>>
112 112 f mammals/skunk skunk
113 113 $ hg debugwalk -v -I 're:.*k$'
114 114 * matcher:
115 <includematcher includes='(?:.*k$)'>
115 <includematcher includes='.*k$'>
116 116 f beans/black ../beans/black
117 117 f fenugreek ../fenugreek
118 118 f mammals/skunk skunk
119 119 $ hg debugwalk -v -I 'relre:.*k$'
120 120 * matcher:
121 <includematcher includes='(?:.*.*k$)'>
121 <includematcher includes='.*.*k$'>
122 122 f beans/black ../beans/black
123 123 f fenugreek ../fenugreek
124 124 f mammals/skunk skunk
125 125 $ hg debugwalk -v -I 'path:beans'
126 126 * matcher:
127 <includematcher includes='(?:beans(?:/|$))'>
127 <includematcher includes='beans(?:/|$)'>
128 128 f beans/black ../beans/black
129 129 f beans/borlotti ../beans/borlotti
130 130 f beans/kidney ../beans/kidney
131 131 f beans/navy ../beans/navy
132 132 f beans/pinto ../beans/pinto
133 133 f beans/turtle ../beans/turtle
134 134 $ hg debugwalk -v -I 'relpath:detour/../../beans'
135 135 * matcher:
136 <includematcher includes='(?:beans(?:/|$))'>
136 <includematcher includes='beans(?:/|$)'>
137 137 f beans/black ../beans/black
138 138 f beans/borlotti ../beans/borlotti
139 139 f beans/kidney ../beans/kidney
140 140 f beans/navy ../beans/navy
141 141 f beans/pinto ../beans/pinto
142 142 f beans/turtle ../beans/turtle
143 143
144 144 $ hg debugwalk -v 'rootfilesin:'
145 145 * matcher:
146 146 <patternmatcher patterns="rootfilesin: ['.']">
147 147 f fennel ../fennel
148 148 f fenugreek ../fenugreek
149 149 f fiddlehead ../fiddlehead
150 150 $ hg debugwalk -v -I 'rootfilesin:'
151 151 * matcher:
152 152 <includematcher includes="rootfilesin: ['.']">
153 153 f fennel ../fennel
154 154 f fenugreek ../fenugreek
155 155 f fiddlehead ../fiddlehead
156 156 $ hg debugwalk -v 'rootfilesin:.'
157 157 * matcher:
158 158 <patternmatcher patterns="rootfilesin: ['.']">
159 159 f fennel ../fennel
160 160 f fenugreek ../fenugreek
161 161 f fiddlehead ../fiddlehead
162 162 $ hg debugwalk -v -I 'rootfilesin:.'
163 163 * matcher:
164 164 <includematcher includes="rootfilesin: ['.']">
165 165 f fennel ../fennel
166 166 f fenugreek ../fenugreek
167 167 f fiddlehead ../fiddlehead
168 168 $ hg debugwalk -v -X 'rootfilesin:'
169 169 * matcher:
170 170 <differencematcher
171 171 m1=<alwaysmatcher>,
172 172 m2=<includematcher includes="rootfilesin: ['.']">>
173 173 f beans/black ../beans/black
174 174 f beans/borlotti ../beans/borlotti
175 175 f beans/kidney ../beans/kidney
176 176 f beans/navy ../beans/navy
177 177 f beans/pinto ../beans/pinto
178 178 f beans/turtle ../beans/turtle
179 179 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
180 180 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
181 181 f mammals/Procyonidae/raccoon Procyonidae/raccoon
182 182 f mammals/skunk skunk
183 183 $ hg debugwalk -v 'rootfilesin:fennel'
184 184 * matcher:
185 185 <patternmatcher patterns="rootfilesin: ['fennel']">
186 186 $ hg debugwalk -v -I 'rootfilesin:fennel'
187 187 * matcher:
188 188 <includematcher includes="rootfilesin: ['fennel']">
189 189 $ hg debugwalk -v 'rootfilesin:skunk'
190 190 * matcher:
191 191 <patternmatcher patterns="rootfilesin: ['skunk']">
192 192 $ hg debugwalk -v -I 'rootfilesin:skunk'
193 193 * matcher:
194 194 <includematcher includes="rootfilesin: ['skunk']">
195 195 $ hg debugwalk -v 'rootfilesin:beans'
196 196 * matcher:
197 197 <patternmatcher patterns="rootfilesin: ['beans']">
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 $ hg debugwalk -v -I 'rootfilesin:beans'
205 205 * matcher:
206 206 <includematcher includes="rootfilesin: ['beans']">
207 207 f beans/black ../beans/black
208 208 f beans/borlotti ../beans/borlotti
209 209 f beans/kidney ../beans/kidney
210 210 f beans/navy ../beans/navy
211 211 f beans/pinto ../beans/pinto
212 212 f beans/turtle ../beans/turtle
213 213 $ hg debugwalk -v 'rootfilesin:mammals'
214 214 * matcher:
215 215 <patternmatcher patterns="rootfilesin: ['mammals']">
216 216 f mammals/skunk skunk
217 217 $ hg debugwalk -v -I 'rootfilesin:mammals'
218 218 * matcher:
219 219 <includematcher includes="rootfilesin: ['mammals']">
220 220 f mammals/skunk skunk
221 221 $ hg debugwalk -v 'rootfilesin:mammals/'
222 222 * matcher:
223 223 <patternmatcher patterns="rootfilesin: ['mammals']">
224 224 f mammals/skunk skunk
225 225 $ hg debugwalk -v -I 'rootfilesin:mammals/'
226 226 * matcher:
227 227 <includematcher includes="rootfilesin: ['mammals']">
228 228 f mammals/skunk skunk
229 229 $ hg debugwalk -v -X 'rootfilesin:mammals'
230 230 * matcher:
231 231 <differencematcher
232 232 m1=<alwaysmatcher>,
233 233 m2=<includematcher includes="rootfilesin: ['mammals']">>
234 234 f beans/black ../beans/black
235 235 f beans/borlotti ../beans/borlotti
236 236 f beans/kidney ../beans/kidney
237 237 f beans/navy ../beans/navy
238 238 f beans/pinto ../beans/pinto
239 239 f beans/turtle ../beans/turtle
240 240 f fennel ../fennel
241 241 f fenugreek ../fenugreek
242 242 f fiddlehead ../fiddlehead
243 243 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
244 244 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
245 245 f mammals/Procyonidae/raccoon Procyonidae/raccoon
246 246
247 247 $ hg debugwalk -v .
248 248 * matcher:
249 <patternmatcher patterns='(?:mammals(?:/|$))'>
249 <patternmatcher patterns='mammals(?:/|$)'>
250 250 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
251 251 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
252 252 f mammals/Procyonidae/raccoon Procyonidae/raccoon
253 253 f mammals/skunk skunk
254 254 $ hg debugwalk -v -I.
255 255 * matcher:
256 <includematcher includes='(?:mammals(?:/|$))'>
256 <includematcher includes='mammals(?:/|$)'>
257 257 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
258 258 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
259 259 f mammals/Procyonidae/raccoon Procyonidae/raccoon
260 260 f mammals/skunk skunk
261 261 $ hg debugwalk -v Procyonidae
262 262 * matcher:
263 <patternmatcher patterns='(?:mammals/Procyonidae(?:/|$))'>
263 <patternmatcher patterns='mammals/Procyonidae(?:/|$)'>
264 264 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
265 265 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
266 266 f mammals/Procyonidae/raccoon Procyonidae/raccoon
267 267
268 268 $ cd Procyonidae
269 269 $ hg debugwalk -v .
270 270 * matcher:
271 <patternmatcher patterns='(?:mammals/Procyonidae(?:/|$))'>
271 <patternmatcher patterns='mammals/Procyonidae(?:/|$)'>
272 272 f mammals/Procyonidae/cacomistle cacomistle
273 273 f mammals/Procyonidae/coatimundi coatimundi
274 274 f mammals/Procyonidae/raccoon raccoon
275 275 $ hg debugwalk -v ..
276 276 * matcher:
277 <patternmatcher patterns='(?:mammals(?:/|$))'>
277 <patternmatcher patterns='mammals(?:/|$)'>
278 278 f mammals/Procyonidae/cacomistle cacomistle
279 279 f mammals/Procyonidae/coatimundi coatimundi
280 280 f mammals/Procyonidae/raccoon raccoon
281 281 f mammals/skunk ../skunk
282 282 $ cd ..
283 283
284 284 $ hg debugwalk -v ../beans
285 285 * matcher:
286 <patternmatcher patterns='(?:beans(?:/|$))'>
286 <patternmatcher patterns='beans(?:/|$)'>
287 287 f beans/black ../beans/black
288 288 f beans/borlotti ../beans/borlotti
289 289 f beans/kidney ../beans/kidney
290 290 f beans/navy ../beans/navy
291 291 f beans/pinto ../beans/pinto
292 292 f beans/turtle ../beans/turtle
293 293 $ hg debugwalk -v .
294 294 * matcher:
295 <patternmatcher patterns='(?:mammals(?:/|$))'>
295 <patternmatcher patterns='mammals(?:/|$)'>
296 296 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
297 297 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
298 298 f mammals/Procyonidae/raccoon Procyonidae/raccoon
299 299 f mammals/skunk skunk
300 300 $ hg debugwalk -v .hg
301 301 abort: path 'mammals/.hg' is inside nested repo 'mammals'
302 302 [255]
303 303 $ hg debugwalk -v ../.hg
304 304 abort: path contains illegal component: .hg
305 305 [255]
306 306 $ cd ..
307 307
308 308 $ hg debugwalk -v -Ibeans
309 309 * matcher:
310 <includematcher includes='(?:beans(?:/|$))'>
310 <includematcher includes='beans(?:/|$)'>
311 311 f beans/black beans/black
312 312 f beans/borlotti beans/borlotti
313 313 f beans/kidney beans/kidney
314 314 f beans/navy beans/navy
315 315 f beans/pinto beans/pinto
316 316 f beans/turtle beans/turtle
317 317 $ hg debugwalk -v -I '{*,{b,m}*/*}k'
318 318 * matcher:
319 <includematcher includes='(?:(?:[^/]*|(?:b|m)[^/]*/[^/]*)k(?:/|$))'>
319 <includematcher includes='(?:[^/]*|(?:b|m)[^/]*/[^/]*)k(?:/|$)'>
320 320 f beans/black beans/black
321 321 f fenugreek fenugreek
322 322 f mammals/skunk mammals/skunk
323 323 $ hg debugwalk -v -Ibeans mammals
324 324 * matcher:
325 325 <intersectionmatcher
326 m1=<patternmatcher patterns='(?:mammals(?:/|$))'>,
327 m2=<includematcher includes='(?:beans(?:/|$))'>>
326 m1=<patternmatcher patterns='mammals(?:/|$)'>,
327 m2=<includematcher includes='beans(?:/|$)'>>
328 328 $ hg debugwalk -v -Inon-existent
329 329 * matcher:
330 <includematcher includes='(?:non\\-existent(?:/|$))'>
330 <includematcher includes='non\\-existent(?:/|$)'>
331 331 $ hg debugwalk -v -Inon-existent -Ibeans/black
332 332 * matcher:
333 <includematcher includes='(?:non\\-existent(?:/|$)|beans/black(?:/|$))'>
333 <includematcher includes='non\\-existent(?:/|$)|beans/black(?:/|$)'>
334 334 f beans/black beans/black
335 335 $ hg debugwalk -v -Ibeans beans/black
336 336 * matcher:
337 337 <intersectionmatcher
338 m1=<patternmatcher patterns='(?:beans/black(?:/|$))'>,
339 m2=<includematcher includes='(?:beans(?:/|$))'>>
338 m1=<patternmatcher patterns='beans/black(?:/|$)'>,
339 m2=<includematcher includes='beans(?:/|$)'>>
340 340 f beans/black beans/black exact
341 341 $ hg debugwalk -v -Ibeans/black beans
342 342 * matcher:
343 343 <intersectionmatcher
344 m1=<patternmatcher patterns='(?:beans(?:/|$))'>,
345 m2=<includematcher includes='(?:beans/black(?:/|$))'>>
344 m1=<patternmatcher patterns='beans(?:/|$)'>,
345 m2=<includematcher includes='beans/black(?:/|$)'>>
346 346 f beans/black beans/black
347 347 $ hg debugwalk -v -Xbeans/black beans
348 348 * matcher:
349 349 <differencematcher
350 m1=<patternmatcher patterns='(?:beans(?:/|$))'>,
351 m2=<includematcher includes='(?:beans/black(?:/|$))'>>
350 m1=<patternmatcher patterns='beans(?:/|$)'>,
351 m2=<includematcher includes='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 -v -Xbeans/black -Ibeans
358 358 * matcher:
359 359 <differencematcher
360 m1=<includematcher includes='(?:beans(?:/|$))'>,
361 m2=<includematcher includes='(?:beans/black(?:/|$))'>>
360 m1=<includematcher includes='beans(?:/|$)'>,
361 m2=<includematcher includes='beans/black(?:/|$)'>>
362 362 f beans/borlotti beans/borlotti
363 363 f beans/kidney beans/kidney
364 364 f beans/navy beans/navy
365 365 f beans/pinto beans/pinto
366 366 f beans/turtle beans/turtle
367 367 $ hg debugwalk -v -Xbeans/black beans/black
368 368 * matcher:
369 369 <differencematcher
370 m1=<patternmatcher patterns='(?:beans/black(?:/|$))'>,
371 m2=<includematcher includes='(?:beans/black(?:/|$))'>>
370 m1=<patternmatcher patterns='beans/black(?:/|$)'>,
371 m2=<includematcher includes='beans/black(?:/|$)'>>
372 372 $ hg debugwalk -v -Xbeans/black -Ibeans/black
373 373 * matcher:
374 374 <differencematcher
375 m1=<includematcher includes='(?:beans/black(?:/|$))'>,
376 m2=<includematcher includes='(?:beans/black(?:/|$))'>>
375 m1=<includematcher includes='beans/black(?:/|$)'>,
376 m2=<includematcher includes='beans/black(?:/|$)'>>
377 377 $ hg debugwalk -v -Xbeans beans/black
378 378 * matcher:
379 379 <differencematcher
380 m1=<patternmatcher patterns='(?:beans/black(?:/|$))'>,
381 m2=<includematcher includes='(?:beans(?:/|$))'>>
380 m1=<patternmatcher patterns='beans/black(?:/|$)'>,
381 m2=<includematcher includes='beans(?:/|$)'>>
382 382 $ hg debugwalk -v -Xbeans -Ibeans/black
383 383 * matcher:
384 384 <differencematcher
385 m1=<includematcher includes='(?:beans/black(?:/|$))'>,
386 m2=<includematcher includes='(?:beans(?:/|$))'>>
385 m1=<includematcher includes='beans/black(?:/|$)'>,
386 m2=<includematcher includes='beans(?:/|$)'>>
387 387 $ hg debugwalk -v 'glob:mammals/../beans/b*'
388 388 * matcher:
389 <patternmatcher patterns='(?:beans/b[^/]*$)'>
389 <patternmatcher patterns='beans/b[^/]*$'>
390 390 f beans/black beans/black
391 391 f beans/borlotti beans/borlotti
392 392 $ hg debugwalk -v '-X*/Procyonidae' mammals
393 393 * matcher:
394 394 <differencematcher
395 m1=<patternmatcher patterns='(?:mammals(?:/|$))'>,
396 m2=<includematcher includes='(?:[^/]*/Procyonidae(?:/|$))'>>
395 m1=<patternmatcher patterns='mammals(?:/|$)'>,
396 m2=<includematcher includes='[^/]*/Procyonidae(?:/|$)'>>
397 397 f mammals/skunk mammals/skunk
398 398 $ hg debugwalk -v path:mammals
399 399 * matcher:
400 <patternmatcher patterns='(?:mammals(?:/|$))'>
400 <patternmatcher patterns='mammals(?:/|$)'>
401 401 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
402 402 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
403 403 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
404 404 f mammals/skunk mammals/skunk
405 405 $ hg debugwalk -v ..
406 406 abort: .. not under root '$TESTTMP/t'
407 407 [255]
408 408 $ hg debugwalk -v beans/../..
409 409 abort: beans/../.. not under root '$TESTTMP/t'
410 410 [255]
411 411 $ hg debugwalk -v .hg
412 412 abort: path contains illegal component: .hg
413 413 [255]
414 414 $ hg debugwalk -v beans/../.hg
415 415 abort: path contains illegal component: .hg
416 416 [255]
417 417 $ hg debugwalk -v beans/../.hg/data
418 418 abort: path contains illegal component: .hg/data
419 419 [255]
420 420 $ hg debugwalk -v beans/.hg
421 421 abort: path 'beans/.hg' is inside nested repo 'beans'
422 422 [255]
423 423
424 424 Test explicit paths and excludes:
425 425
426 426 $ hg debugwalk -v fennel -X fennel
427 427 * matcher:
428 428 <differencematcher
429 m1=<patternmatcher patterns='(?:fennel(?:/|$))'>,
430 m2=<includematcher includes='(?:fennel(?:/|$))'>>
429 m1=<patternmatcher patterns='fennel(?:/|$)'>,
430 m2=<includematcher includes='fennel(?:/|$)'>>
431 431 $ hg debugwalk -v fennel -X 'f*'
432 432 * matcher:
433 433 <differencematcher
434 m1=<patternmatcher patterns='(?:fennel(?:/|$))'>,
435 m2=<includematcher includes='(?:f[^/]*(?:/|$))'>>
434 m1=<patternmatcher patterns='fennel(?:/|$)'>,
435 m2=<includematcher includes='f[^/]*(?:/|$)'>>
436 436 $ hg debugwalk -v beans/black -X 'path:beans'
437 437 * matcher:
438 438 <differencematcher
439 m1=<patternmatcher patterns='(?:beans/black(?:/|$))'>,
440 m2=<includematcher includes='(?:beans(?:/|$))'>>
439 m1=<patternmatcher patterns='beans/black(?:/|$)'>,
440 m2=<includematcher includes='beans(?:/|$)'>>
441 441 $ hg debugwalk -v -I 'path:beans/black' -X 'path:beans'
442 442 * matcher:
443 443 <differencematcher
444 m1=<includematcher includes='(?:beans/black(?:/|$))'>,
445 m2=<includematcher includes='(?:beans(?:/|$))'>>
444 m1=<includematcher includes='beans/black(?:/|$)'>,
445 m2=<includematcher includes='beans(?:/|$)'>>
446 446
447 447 Test absolute paths:
448 448
449 449 $ hg debugwalk -v `pwd`/beans
450 450 * matcher:
451 <patternmatcher patterns='(?:beans(?:/|$))'>
451 <patternmatcher patterns='beans(?:/|$)'>
452 452 f beans/black beans/black
453 453 f beans/borlotti beans/borlotti
454 454 f beans/kidney beans/kidney
455 455 f beans/navy beans/navy
456 456 f beans/pinto beans/pinto
457 457 f beans/turtle beans/turtle
458 458 $ hg debugwalk -v `pwd`/..
459 459 abort: $TESTTMP/t/.. not under root '$TESTTMP/t'
460 460 [255]
461 461
462 462 Test patterns:
463 463
464 464 $ hg debugwalk -v glob:\*
465 465 * matcher:
466 <patternmatcher patterns='(?:[^/]*$)'>
466 <patternmatcher patterns='[^/]*$'>
467 467 f fennel fennel
468 468 f fenugreek fenugreek
469 469 f fiddlehead fiddlehead
470 470 #if eol-in-paths
471 471 $ echo glob:glob > glob:glob
472 472 $ hg addremove
473 473 adding glob:glob
474 474 warning: filename contains ':', which is reserved on Windows: 'glob:glob'
475 475 $ hg debugwalk -v glob:\*
476 476 * matcher:
477 <patternmatcher patterns='(?:[^/]*$)'>
477 <patternmatcher patterns='[^/]*$'>
478 478 f fennel fennel
479 479 f fenugreek fenugreek
480 480 f fiddlehead fiddlehead
481 481 f glob:glob glob:glob
482 482 $ hg debugwalk -v glob:glob
483 483 * matcher:
484 <patternmatcher patterns='(?:glob$)'>
484 <patternmatcher patterns='glob$'>
485 485 glob: $ENOENT$
486 486 $ hg debugwalk -v glob:glob:glob
487 487 * matcher:
488 <patternmatcher patterns='(?:glob:glob$)'>
488 <patternmatcher patterns='glob:glob$'>
489 489 f glob:glob glob:glob exact
490 490 $ hg debugwalk -v path:glob:glob
491 491 * matcher:
492 <patternmatcher patterns='(?:glob:glob(?:/|$))'>
492 <patternmatcher patterns='glob:glob(?:/|$)'>
493 493 f glob:glob glob:glob exact
494 494 $ rm glob:glob
495 495 $ hg addremove
496 496 removing glob:glob
497 497 #endif
498 498
499 499 $ hg debugwalk -v 'glob:**e'
500 500 * matcher:
501 <patternmatcher patterns='(?:.*e$)'>
501 <patternmatcher patterns='.*e$'>
502 502 f beans/turtle beans/turtle
503 503 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
504 504
505 505 $ hg debugwalk -v 're:.*[kb]$'
506 506 * matcher:
507 <patternmatcher patterns='(?:.*[kb]$)'>
507 <patternmatcher patterns='.*[kb]$'>
508 508 f beans/black beans/black
509 509 f fenugreek fenugreek
510 510 f mammals/skunk mammals/skunk
511 511
512 512 $ hg debugwalk -v path:beans/black
513 513 * matcher:
514 <patternmatcher patterns='(?:beans/black(?:/|$))'>
514 <patternmatcher patterns='beans/black(?:/|$)'>
515 515 f beans/black beans/black exact
516 516 $ hg debugwalk -v path:beans//black
517 517 * matcher:
518 <patternmatcher patterns='(?:beans/black(?:/|$))'>
518 <patternmatcher patterns='beans/black(?:/|$)'>
519 519 f beans/black beans/black exact
520 520
521 521 $ hg debugwalk -v relglob:Procyonidae
522 522 * matcher:
523 <patternmatcher patterns='(?:(?:|.*/)Procyonidae$)'>
523 <patternmatcher patterns='(?:|.*/)Procyonidae$'>
524 524 $ hg debugwalk -v 'relglob:Procyonidae/**'
525 525 * matcher:
526 <patternmatcher patterns='(?:(?:|.*/)Procyonidae/.*$)'>
526 <patternmatcher patterns='(?:|.*/)Procyonidae/.*$'>
527 527 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
528 528 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
529 529 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
530 530 $ hg debugwalk -v 'relglob:Procyonidae/**' fennel
531 531 * matcher:
532 <patternmatcher patterns='(?:(?:|.*/)Procyonidae/.*$|fennel(?:/|$))'>
532 <patternmatcher patterns='(?:|.*/)Procyonidae/.*$|fennel(?:/|$)'>
533 533 f fennel fennel exact
534 534 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
535 535 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
536 536 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
537 537 $ hg debugwalk -v beans 'glob:beans/*'
538 538 * matcher:
539 <patternmatcher patterns='(?:beans(?:/|$)|beans/[^/]*$)'>
539 <patternmatcher patterns='beans(?:/|$)|beans/[^/]*$'>
540 540 f beans/black beans/black
541 541 f beans/borlotti beans/borlotti
542 542 f beans/kidney beans/kidney
543 543 f beans/navy beans/navy
544 544 f beans/pinto beans/pinto
545 545 f beans/turtle beans/turtle
546 546 $ hg debugwalk -v 'glob:mamm**'
547 547 * matcher:
548 <patternmatcher patterns='(?:mamm.*$)'>
548 <patternmatcher patterns='mamm.*$'>
549 549 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
550 550 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
551 551 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
552 552 f mammals/skunk mammals/skunk
553 553 $ hg debugwalk -v 'glob:mamm**' fennel
554 554 * matcher:
555 <patternmatcher patterns='(?:mamm.*$|fennel(?:/|$))'>
555 <patternmatcher patterns='mamm.*$|fennel(?:/|$)'>
556 556 f fennel fennel exact
557 557 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
558 558 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
559 559 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
560 560 f mammals/skunk mammals/skunk
561 561 $ hg debugwalk -v 'glob:j*'
562 562 * matcher:
563 <patternmatcher patterns='(?:j[^/]*$)'>
563 <patternmatcher patterns='j[^/]*$'>
564 564 $ hg debugwalk -v NOEXIST
565 565 * matcher:
566 <patternmatcher patterns='(?:NOEXIST(?:/|$))'>
566 <patternmatcher patterns='NOEXIST(?:/|$)'>
567 567 NOEXIST: * (glob)
568 568
569 569 #if fifo
570 570 $ mkfifo fifo
571 571 $ hg debugwalk -v fifo
572 572 * matcher:
573 <patternmatcher patterns='(?:fifo(?:/|$))'>
573 <patternmatcher patterns='fifo(?:/|$)'>
574 574 fifo: unsupported file type (type is fifo)
575 575 #endif
576 576
577 577 $ rm fenugreek
578 578 $ hg debugwalk -v fenugreek
579 579 * matcher:
580 <patternmatcher patterns='(?:fenugreek(?:/|$))'>
580 <patternmatcher patterns='fenugreek(?:/|$)'>
581 581 f fenugreek fenugreek exact
582 582 $ hg rm fenugreek
583 583 $ hg debugwalk -v fenugreek
584 584 * matcher:
585 <patternmatcher patterns='(?:fenugreek(?:/|$))'>
585 <patternmatcher patterns='fenugreek(?:/|$)'>
586 586 f fenugreek fenugreek exact
587 587 $ touch new
588 588 $ hg debugwalk -v new
589 589 * matcher:
590 <patternmatcher patterns='(?:new(?:/|$))'>
590 <patternmatcher patterns='new(?:/|$)'>
591 591 f new new exact
592 592
593 593 $ mkdir ignored
594 594 $ touch ignored/file
595 595 $ echo '^ignored$' > .hgignore
596 596 $ hg debugwalk -v ignored
597 597 * matcher:
598 <patternmatcher patterns='(?:ignored(?:/|$))'>
598 <patternmatcher patterns='ignored(?:/|$)'>
599 599 $ hg debugwalk -v ignored/file
600 600 * matcher:
601 <patternmatcher patterns='(?:ignored/file(?:/|$))'>
601 <patternmatcher patterns='ignored/file(?:/|$)'>
602 602 f ignored/file ignored/file exact
603 603
604 604 Test listfile and listfile0
605 605
606 606 $ "$PYTHON" -c "open('listfile0', 'wb').write(b'fenugreek\0new\0')"
607 607 $ hg debugwalk -v -I 'listfile0:listfile0'
608 608 * matcher:
609 <includematcher includes='(?:fenugreek(?:/|$)|new(?:/|$))'>
609 <includematcher includes='fenugreek(?:/|$)|new(?:/|$)'>
610 610 f fenugreek fenugreek
611 611 f new new
612 612 $ "$PYTHON" -c "open('listfile', 'wb').write(b'fenugreek\nnew\r\nmammals/skunk\n')"
613 613 $ hg debugwalk -v -I 'listfile:listfile'
614 614 * matcher:
615 <includematcher includes='(?:fenugreek(?:/|$)|new(?:/|$)|mammals/skunk(?:/|$))'>
615 <includematcher includes='fenugreek(?:/|$)|new(?:/|$)|mammals/skunk(?:/|$)'>
616 616 f fenugreek fenugreek
617 617 f mammals/skunk mammals/skunk
618 618 f new new
619 619
620 620 $ cd ..
621 621 $ hg debugwalk -v -R t t/mammals/skunk
622 622 * matcher:
623 <patternmatcher patterns='(?:mammals/skunk(?:/|$))'>
623 <patternmatcher patterns='mammals/skunk(?:/|$)'>
624 624 f mammals/skunk t/mammals/skunk exact
625 625 $ mkdir t2
626 626 $ cd t2
627 627 $ hg debugwalk -v -R ../t ../t/mammals/skunk
628 628 * matcher:
629 <patternmatcher patterns='(?:mammals/skunk(?:/|$))'>
629 <patternmatcher patterns='mammals/skunk(?:/|$)'>
630 630 f mammals/skunk ../t/mammals/skunk exact
631 631 $ hg debugwalk -v --cwd ../t mammals/skunk
632 632 * matcher:
633 <patternmatcher patterns='(?:mammals/skunk(?:/|$))'>
633 <patternmatcher patterns='mammals/skunk(?:/|$)'>
634 634 f mammals/skunk mammals/skunk exact
635 635
636 636 $ cd ..
637 637
638 638 Test split patterns on overflow
639 639
640 640 $ cd t
641 641 $ echo fennel > overflow.list
642 642 $ cat >> printnum.py <<EOF
643 643 > from __future__ import print_function
644 644 > for i in range(20000 // 100):
645 645 > print('x' * 100)
646 646 > EOF
647 647 $ "$PYTHON" printnum.py >> overflow.list
648 648 $ echo fenugreek >> overflow.list
649 649 $ hg debugwalk 'listfile:overflow.list' 2>&1 | egrep -v '^xxx'
650 650 f fennel fennel exact
651 651 f fenugreek fenugreek exact
652 652 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now