mercurial/dirstate.py
changeset 4610 b43f17691ae6
parent 4609 220211b88656
child 4611 274c99fc629f
equal deleted inserted replaced
4609:220211b88656 4610:b43f17691ae6
     7 of the GNU General Public License, incorporated herein by reference.
     7 of the GNU General Public License, incorporated herein by reference.
     8 """
     8 """
     9 
     9 
    10 from node import *
    10 from node import *
    11 from i18n import _
    11 from i18n import _
    12 import struct, os, time, bisect, stat, strutil, util, re, errno
    12 import struct, os, time, bisect, stat, strutil, util, re, errno, ignore
    13 import cStringIO
    13 import cStringIO
    14 
    14 
    15 class dirstate(object):
    15 class dirstate(object):
    16     format = ">cllll"
    16     format = ">cllll"
    17 
    17 
    18     def __init__(self, opener, ui, root):
    18     def __init__(self, opener, ui, root):
    19         self.opener = opener
    19         self.opener = opener
    20         self.root = root
    20         self.root = root
    21         self.dirty = 0
    21         self.dirty = 0
    22         self.ui = ui
    22         self.ui = ui
    23         self.ignorefunc = None
       
    24         self._slash = None
    23         self._slash = None
    25 
    24 
    26     def __getattr__(self, name):
    25     def __getattr__(self, name):
    27         if name == 'map':
    26         if name == 'map':
    28             self.read()
    27             self.read()
    49         elif name == 'dirs':
    48         elif name == 'dirs':
    50             self.dirs = {}
    49             self.dirs = {}
    51             for f in self.map:
    50             for f in self.map:
    52                 self.updatedirs(f, 1)
    51                 self.updatedirs(f, 1)
    53             return self.dirs
    52             return self.dirs
       
    53         elif name == '_ignore':
       
    54             files = [self.wjoin('.hgignore')] + self.ui.hgignorefiles()
       
    55             self._ignore = ignore.ignore(self.root, files, self.ui.warn)
       
    56             return self._ignore
    54         else:
    57         else:
    55             raise AttributeError, name
    58             raise AttributeError, name
    56 
    59 
    57     def wjoin(self, f):
    60     def wjoin(self, f):
    58         return os.path.join(self.root, f)
    61         return os.path.join(self.root, f)
    78             self._slash = self.ui.configbool('ui', 'slash') and os.sep != '/'
    81             self._slash = self.ui.configbool('ui', 'slash') and os.sep != '/'
    79         if self._slash:
    82         if self._slash:
    80             path = path.replace(os.sep, '/')
    83             path = path.replace(os.sep, '/')
    81         return path
    84         return path
    82 
    85 
    83     def hgignore(self):
       
    84         '''return the contents of .hgignore files as a list of patterns.
       
    85 
       
    86         the files parsed for patterns include:
       
    87         .hgignore in the repository root
       
    88         any additional files specified in the [ui] section of ~/.hgrc
       
    89 
       
    90         trailing white space is dropped.
       
    91         the escape character is backslash.
       
    92         comments start with #.
       
    93         empty lines are skipped.
       
    94 
       
    95         lines can be of the following formats:
       
    96 
       
    97         syntax: regexp # defaults following lines to non-rooted regexps
       
    98         syntax: glob   # defaults following lines to non-rooted globs
       
    99         re:pattern     # non-rooted regular expression
       
   100         glob:pattern   # non-rooted glob
       
   101         pattern        # pattern of the current default type'''
       
   102         syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
       
   103         def parselines(fp):
       
   104             for line in fp:
       
   105                 if not line.endswith('\n'):
       
   106                     line += '\n'
       
   107                 escape = False
       
   108                 for i in xrange(len(line)):
       
   109                     if escape: escape = False
       
   110                     elif line[i] == '\\': escape = True
       
   111                     elif line[i] == '#': break
       
   112                 line = line[:i].rstrip()
       
   113                 if line: yield line
       
   114         repoignore = self.wjoin('.hgignore')
       
   115         files = [repoignore]
       
   116         files.extend(self.ui.hgignorefiles())
       
   117         pats = {}
       
   118         for f in files:
       
   119             try:
       
   120                 pats[f] = []
       
   121                 fp = open(f)
       
   122                 syntax = 'relre:'
       
   123                 for line in parselines(fp):
       
   124                     if line.startswith('syntax:'):
       
   125                         s = line[7:].strip()
       
   126                         try:
       
   127                             syntax = syntaxes[s]
       
   128                         except KeyError:
       
   129                             self.ui.warn(_("%s: ignoring invalid "
       
   130                                            "syntax '%s'\n") % (f, s))
       
   131                         continue
       
   132                     pat = syntax + line
       
   133                     for s in syntaxes.values():
       
   134                         if line.startswith(s):
       
   135                             pat = line
       
   136                             break
       
   137                     pats[f].append(pat)
       
   138             except IOError, inst:
       
   139                 if f != repoignore:
       
   140                     self.ui.warn(_("skipping unreadable ignore file"
       
   141                                    " '%s': %s\n") % (f, inst.strerror))
       
   142         return pats
       
   143 
       
   144     def ignore(self, fn):
       
   145         '''default match function used by dirstate and
       
   146         localrepository.  this honours the repository .hgignore file
       
   147         and any other files specified in the [ui] section of .hgrc.'''
       
   148         if not self.ignorefunc:
       
   149             ignore = self.hgignore()
       
   150             allpats = []
       
   151             [allpats.extend(patlist) for patlist in ignore.values()]
       
   152             if allpats:
       
   153                 try:
       
   154                     files, self.ignorefunc, anypats = (
       
   155                         util.matcher(self.root, inc=allpats, src='.hgignore'))
       
   156                 except util.Abort:
       
   157                     # Re-raise an exception where the src is the right file
       
   158                     for f, patlist in ignore.items():
       
   159                         files, self.ignorefunc, anypats = (
       
   160                             util.matcher(self.root, inc=patlist, src=f))
       
   161             else:
       
   162                 self.ignorefunc = util.never
       
   163         return self.ignorefunc(fn)
       
   164 
       
   165     def __del__(self):
    86     def __del__(self):
   166         if self.dirty:
    87         if self.dirty:
   167             self.write()
    88             self.write()
   168 
    89 
   169     def __getitem__(self, key):
    90     def __getitem__(self, key):
   239                 copymap[f] = c
   160                 copymap[f] = c
   240             dmap[f] = e[:4]
   161             dmap[f] = e[:4]
   241             pos = newpos
   162             pos = newpos
   242 
   163 
   243     def reload(self):
   164     def reload(self):
   244         for a in "map copymap _branch pl dirs".split():
   165         for a in "map copymap _branch pl dirs _ignore".split():
   245             if hasattr(self, a):
   166             if hasattr(self, a):
   246                 self.__delattr__(a)
   167                 self.__delattr__(a)
   247         self.ignorefunc = None
       
   248 
   168 
   249     def copy(self, source, dest):
   169     def copy(self, source, dest):
   250         self.markdirty()
   170         self.markdirty()
   251         self.copymap[dest] = source
   171         self.copymap[dest] = source
   252 
   172 
   418         else:
   338         else:
   419             files = util.unique(files)
   339             files = util.unique(files)
   420             dc = self.filterfiles(files)
   340             dc = self.filterfiles(files)
   421 
   341 
   422         def imatch(file_):
   342         def imatch(file_):
   423             if file_ not in dc and self.ignore(file_):
   343             if file_ not in dc and self._ignore(file_):
   424                 return False
   344                 return False
   425             return match(file_)
   345             return match(file_)
   426 
   346 
   427         ignore = self.ignore
   347         ignore = self._ignore
   428         if ignored:
   348         if ignored:
   429             imatch = match
   349             imatch = match
   430             ignore = util.never
   350             ignore = util.never
   431 
   351 
   432         # self.root may end with a path separator when self.root == '/'
   352         # self.root may end with a path separator when self.root == '/'
   527 
   447 
   528         for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
   448         for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
   529             try:
   449             try:
   530                 type_, mode, size, time = self[fn]
   450                 type_, mode, size, time = self[fn]
   531             except KeyError:
   451             except KeyError:
   532                 if list_ignored and self.ignore(fn):
   452                 if list_ignored and self._ignore(fn):
   533                     ignored.append(fn)
   453                     ignored.append(fn)
   534                 else:
   454                 else:
   535                     unknown.append(fn)
   455                     unknown.append(fn)
   536                 continue
   456                 continue
   537             if src == 'm':
   457             if src == 'm':