comparison mercurial/util.py @ 4195:b5d1eaade333

Merge a bunch of matcher and locate fixes.
author Alexis S. L. Carvalho <alexis@cecm.usp.br>
date Sat, 10 Mar 2007 23:21:33 -0300
parents 9dc64c8414ca ec932167c3a7
children 0d51eb296fb9
comparison
equal deleted inserted replaced
4177:ba51a8225a60 4195:b5d1eaade333
387 break 387 break
388 name = dirname 388 name = dirname
389 389
390 raise Abort('%s not under root' % myname) 390 raise Abort('%s not under root' % myname)
391 391
392 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None): 392 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
393 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src) 393 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
394 394
395 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', 395 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
396 src=None, globbed=False): 396 globbed=False, default=None):
397 if not globbed: 397 default = default or 'relpath'
398 if default == 'relpath' and not globbed:
398 names = expand_glob(names) 399 names = expand_glob(names)
399 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src) 400 return _matcher(canonroot, cwd, names, inc, exc, default, src)
400 401
401 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src): 402 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
402 """build a function to match a set of file patterns 403 """build a function to match a set of file patterns
403 404
404 arguments: 405 arguments:
405 canonroot - the canonical root of the tree you're matching against 406 canonroot - the canonical root of the tree you're matching against
406 cwd - the current working directory, if relevant 407 cwd - the current working directory, if relevant
407 names - patterns to find 408 names - patterns to find
408 inc - patterns to include 409 inc - patterns to include
409 exc - patterns to exclude 410 exc - patterns to exclude
410 head - a regex to prepend to patterns to control whether a match is rooted 411 dflt_pat - if a pattern in names has no explicit type, assume this one
412 src - where these patterns came from (e.g. .hgignore)
411 413
412 a pattern is one of: 414 a pattern is one of:
413 'glob:<rooted glob>' 415 'glob:<glob>' - a glob relative to cwd
414 're:<rooted regexp>' 416 're:<regexp>' - a regular expression
415 'path:<rooted path>' 417 'path:<path>' - a path relative to canonroot
416 'relglob:<relative glob>' 418 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
417 'relpath:<relative path>' 419 'relpath:<path>' - a path relative to cwd
418 'relre:<relative regexp>' 420 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
419 '<rooted path or regexp>' 421 '<something>' - one of the cases above, selected by the dflt_pat argument
420 422
421 returns: 423 returns:
422 a 3-tuple containing 424 a 3-tuple containing
423 - list of explicit non-pattern names passed in 425 - list of roots (places where one should start a recursive walk of the fs);
426 this often matches the explicit non-pattern names passed in, but also
427 includes the initial part of glob: patterns that has no glob characters
424 - a bool match(filename) function 428 - a bool match(filename) function
425 - a bool indicating if any patterns were passed in 429 - a bool indicating if any patterns were passed in
426
427 todo:
428 make head regex a rooted bool
429 """ 430 """
431
432 # a common case: no patterns at all
433 if not names and not inc and not exc:
434 return [], always, False
430 435
431 def contains_glob(name): 436 def contains_glob(name):
432 for c in name: 437 for c in name:
433 if c in _globchars: return True 438 if c in _globchars: return True
434 return False 439 return False
435 440
436 def regex(kind, name, tail): 441 def regex(kind, name, tail):
437 '''convert a pattern into a regular expression''' 442 '''convert a pattern into a regular expression'''
443 if not name:
444 return ''
438 if kind == 're': 445 if kind == 're':
439 return name 446 return name
440 elif kind == 'path': 447 elif kind == 'path':
441 return '^' + re.escape(name) + '(?:/|$)' 448 return '^' + re.escape(name) + '(?:/|$)'
442 elif kind == 'relglob': 449 elif kind == 'relglob':
443 return head + globre(name, '(?:|.*/)', tail) 450 return globre(name, '(?:|.*/)', '(?:/|$)')
444 elif kind == 'relpath': 451 elif kind == 'relpath':
445 return head + re.escape(name) + tail 452 return re.escape(name) + '(?:/|$)'
446 elif kind == 'relre': 453 elif kind == 'relre':
447 if name.startswith('^'): 454 if name.startswith('^'):
448 return name 455 return name
449 return '.*' + name 456 return '.*' + name
450 return head + globre(name, '', tail) 457 return globre(name, '', tail)
451 458
452 def matchfn(pats, tail): 459 def matchfn(pats, tail):
453 """build a matching function from a set of patterns""" 460 """build a matching function from a set of patterns"""
454 if not pats: 461 if not pats:
455 return 462 return
471 return buildfn 478 return buildfn
472 479
473 def globprefix(pat): 480 def globprefix(pat):
474 '''return the non-glob prefix of a path, e.g. foo/* -> foo''' 481 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
475 root = [] 482 root = []
476 for p in pat.split(os.sep): 483 for p in pat.split('/'):
477 if contains_glob(p): break 484 if contains_glob(p): break
478 root.append(p) 485 root.append(p)
479 return '/'.join(root) 486 return '/'.join(root) or '.'
480 487
481 pats = [] 488 def normalizepats(names, default):
482 files = [] 489 pats = []
483 roots = [] 490 files = []
484 for kind, name in [patkind(p, dflt_pat) for p in names]: 491 roots = []
485 if kind in ('glob', 'relpath'): 492 anypats = False
486 name = canonpath(canonroot, cwd, name) 493 for kind, name in [patkind(p, default) for p in names]:
487 if name == '': 494 if kind in ('glob', 'relpath'):
488 kind, name = 'glob', '**' 495 name = canonpath(canonroot, cwd, name)
489 if kind in ('glob', 'path', 're'): 496 elif kind in ('relglob', 'path'):
490 pats.append((kind, name)) 497 name = normpath(name)
491 if kind == 'glob': 498 if kind in ('glob', 're', 'relglob', 'relre'):
492 root = globprefix(name) 499 pats.append((kind, name))
493 if root: roots.append(root) 500 anypats = True
494 elif kind == 'relpath': 501 if kind == 'glob':
495 files.append((kind, name)) 502 root = globprefix(name)
496 roots.append(name) 503 roots.append(root)
504 elif kind in ('relpath', 'path'):
505 files.append((kind, name))
506 roots.append(name)
507 elif kind == 'relglob':
508 roots.append('.')
509 return roots, pats + files, anypats
510
511 roots, pats, anypats = normalizepats(names, dflt_pat)
497 512
498 patmatch = matchfn(pats, '$') or always 513 patmatch = matchfn(pats, '$') or always
499 filematch = matchfn(files, '(?:/|$)') or always
500 incmatch = always 514 incmatch = always
501 if inc: 515 if inc:
502 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc] 516 dummy, inckinds, dummy = normalizepats(inc, 'glob')
503 incmatch = matchfn(inckinds, '(?:/|$)') 517 incmatch = matchfn(inckinds, '(?:/|$)')
504 excmatch = lambda fn: False 518 excmatch = lambda fn: False
505 if exc: 519 if exc:
506 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc] 520 dummy, exckinds, dummy = normalizepats(exc, 'glob')
507 excmatch = matchfn(exckinds, '(?:/|$)') 521 excmatch = matchfn(exckinds, '(?:/|$)')
508 522
509 return (roots, 523 if not names and inc and not exc:
510 lambda fn: (incmatch(fn) and not excmatch(fn) and 524 # common case: hgignore patterns
511 (fn.endswith('/') or 525 match = incmatch
512 (not pats and not files) or 526 else:
513 (pats and patmatch(fn)) or 527 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
514 (files and filematch(fn)))), 528
515 (inc or exc or (pats and pats != [('glob', '**')])) and True) 529 return (roots, match, (inc or exc or anypats) and True)
516 530
517 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None): 531 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
518 '''enhanced shell command execution. 532 '''enhanced shell command execution.
519 run with environment maybe modified, maybe in different dir. 533 run with environment maybe modified, maybe in different dir.
520 534