mercurial/util.py
changeset 4195 b5d1eaade333
parent 4134 9dc64c8414ca
parent 4194 ec932167c3a7
child 4232 0d51eb296fb9
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