mercurial/dirstate.py
changeset 4615 a8be3c875988
parent 4614 3a645af7fb76
child 4616 9b00b73a5286
equal deleted inserted replaced
4614:3a645af7fb76 4615:a8be3c875988
    16 _format = ">cllll"
    16 _format = ">cllll"
    17 
    17 
    18 class dirstate(object):
    18 class dirstate(object):
    19 
    19 
    20     def __init__(self, opener, ui, root):
    20     def __init__(self, opener, ui, root):
    21         self.opener = opener
    21         self._opener = opener
    22         self.root = root
    22         self._root = root
    23         self._dirty = 0
    23         self._dirty = 0
    24         self.ui = ui
    24         self._ui = ui
    25 
    25 
    26     def __getattr__(self, name):
    26     def __getattr__(self, name):
    27         if name == 'map':
    27         if name == '_map':
    28             self.read()
    28             self.read()
    29             return self.map
    29             return self._map
    30         elif name == 'copymap':
    30         elif name == '_copymap':
    31             self.read()
    31             self.read()
    32             return self.copymap
    32             return self._copymap
    33         elif name == '_branch':
    33         elif name == '_branch':
    34             try:
    34             try:
    35                 self._branch = self.opener("branch").read().strip()\
    35                 self._branch = self._opener("branch").read().strip()\
    36                                or "default"
    36                                or "default"
    37             except IOError:
    37             except IOError:
    38                 self._branch = "default"
    38                 self._branch = "default"
    39             return self._branch
    39             return self._branch
    40         elif name == 'pl':
    40         elif name == '_pl':
    41             self.pl = [nullid, nullid]
    41             self._pl = [nullid, nullid]
    42             try:
    42             try:
    43                 st = self.opener("dirstate").read(40)
    43                 st = self._opener("dirstate").read(40)
    44                 if len(st) == 40:
    44                 if len(st) == 40:
    45                     self.pl = st[:20], st[20:40]
    45                     self._pl = st[:20], st[20:40]
    46             except IOError, err:
    46             except IOError, err:
    47                 if err.errno != errno.ENOENT: raise
    47                 if err.errno != errno.ENOENT: raise
    48             return self.pl
    48             return self._pl
    49         elif name == 'dirs':
    49         elif name == 'dirs':
    50             self.dirs = {}
    50             self.dirs = {}
    51             for f in self.map:
    51             for f in self._map:
    52                 self.updatedirs(f, 1)
    52                 self.updatedirs(f, 1)
    53             return self.dirs
    53             return self.dirs
    54         elif name == '_ignore':
    54         elif name == '_ignore':
    55             files = [self.wjoin('.hgignore')] + self.ui.hgignorefiles()
    55             files = [self.wjoin('.hgignore')] + self._ui.hgignorefiles()
    56             self._ignore = ignore.ignore(self.root, files, self.ui.warn)
    56             self._ignore = ignore.ignore(self._root, files, self._ui.warn)
    57             return self._ignore
    57             return self._ignore
    58         elif name == '_slash':
    58         elif name == '_slash':
    59             self._slash = self.ui.configbool('ui', 'slash') and os.sep != '/'
    59             self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
    60             return self._slash
    60             return self._slash
    61         else:
    61         else:
    62             raise AttributeError, name
    62             raise AttributeError, name
    63 
    63 
    64     def wjoin(self, f):
    64     def wjoin(self, f):
    65         return os.path.join(self.root, f)
    65         return os.path.join(self._root, f)
    66 
    66 
    67     def getcwd(self):
    67     def getcwd(self):
    68         cwd = os.getcwd()
    68         cwd = os.getcwd()
    69         if cwd == self.root: return ''
    69         if cwd == self._root: return ''
    70         # self.root ends with a path separator if self.root is '/' or 'C:\'
    70         # self._root ends with a path separator if self._root is '/' or 'C:\'
    71         rootsep = self.root
    71         rootsep = self._root
    72         if not rootsep.endswith(os.sep):
    72         if not rootsep.endswith(os.sep):
    73             rootsep += os.sep
    73             rootsep += os.sep
    74         if cwd.startswith(rootsep):
    74         if cwd.startswith(rootsep):
    75             return cwd[len(rootsep):]
    75             return cwd[len(rootsep):]
    76         else:
    76         else:
    78             return cwd
    78             return cwd
    79 
    79 
    80     def pathto(self, f, cwd=None):
    80     def pathto(self, f, cwd=None):
    81         if cwd is None:
    81         if cwd is None:
    82             cwd = self.getcwd()
    82             cwd = self.getcwd()
    83         path = util.pathto(self.root, cwd, f)
    83         path = util.pathto(self._root, cwd, f)
    84         if self._slash:
    84         if self._slash:
    85             return path.replace(os.sep, '/')
    85             return path.replace(os.sep, '/')
    86         return path
    86         return path
    87 
    87 
    88     def __del__(self):
    88     def __del__(self):
    89         self.write()
    89         self.write()
    90 
    90 
    91     def __getitem__(self, key):
    91     def __getitem__(self, key):
    92         return self.map[key]
    92         return self._map[key]
    93 
    93 
    94     def __contains__(self, key):
    94     def __contains__(self, key):
    95         return key in self.map
    95         return key in self._map
       
    96 
       
    97     def __iter__(self):
       
    98         a = self._map.keys()
       
    99         a.sort()
       
   100         for x in a:
       
   101             yield x
    96 
   102 
    97     def parents(self):
   103     def parents(self):
    98         return self.pl
   104         return self._pl
    99 
   105 
   100     def branch(self):
   106     def branch(self):
   101         return self._branch
   107         return self._branch
   102 
   108 
   103     def markdirty(self):
   109     def markdirty(self):
   104         self._dirty = 1
   110         self._dirty = 1
   105 
   111 
   106     def setparents(self, p1, p2=nullid):
   112     def setparents(self, p1, p2=nullid):
   107         self.markdirty()
   113         self.markdirty()
   108         self.pl = p1, p2
   114         self._pl = p1, p2
   109 
   115 
   110     def setbranch(self, branch):
   116     def setbranch(self, branch):
   111         self._branch = branch
   117         self._branch = branch
   112         self.opener("branch", "w").write(branch + '\n')
   118         self._opener("branch", "w").write(branch + '\n')
   113 
   119 
   114     def state(self, key):
   120     def state(self, key):
   115         return self.map.get(key, ("?",))[0]
   121         return self._map.get(key, ("?",))[0]
   116 
   122 
   117     def read(self):
   123     def read(self):
   118         self.map = {}
   124         self._map = {}
   119         self.copymap = {}
   125         self._copymap = {}
   120         self.pl = [nullid, nullid]
   126         self._pl = [nullid, nullid]
   121         try:
   127         try:
   122             st = self.opener("dirstate").read()
   128             st = self._opener("dirstate").read()
   123         except IOError, err:
   129         except IOError, err:
   124             if err.errno != errno.ENOENT: raise
   130             if err.errno != errno.ENOENT: raise
   125             return
   131             return
   126         if not st:
   132         if not st:
   127             return
   133             return
   128 
   134 
   129         self.pl = [st[:20], st[20: 40]]
   135         self._pl = [st[:20], st[20: 40]]
   130 
   136 
   131         # deref fields so they will be local in loop
   137         # deref fields so they will be local in loop
   132         dmap = self.map
   138         dmap = self._map
   133         copymap = self.copymap
   139         copymap = self._copymap
   134         unpack = struct.unpack
   140         unpack = struct.unpack
   135 
   141 
   136         pos = 40
   142         pos = 40
   137         e_size = struct.calcsize(_format)
   143         e_size = struct.calcsize(_format)
   138 
   144 
   148                 copymap[f] = c
   154                 copymap[f] = c
   149             dmap[f] = e[:4]
   155             dmap[f] = e[:4]
   150             pos = newpos
   156             pos = newpos
   151 
   157 
   152     def invalidate(self):
   158     def invalidate(self):
   153         for a in "map copymap _branch pl dirs _ignore".split():
   159         for a in "_map _copymap _branch pl dirs _ignore".split():
   154             if hasattr(self, a):
   160             if hasattr(self, a):
   155                 self.__delattr__(a)
   161                 self.__delattr__(a)
   156 
   162 
   157     def copy(self, source, dest):
   163     def copy(self, source, dest):
   158         self.markdirty()
   164         self.markdirty()
   159         self.copymap[dest] = source
   165         self._copymap[dest] = source
   160 
   166 
   161     def copied(self, file):
   167     def copied(self, file):
   162         return self.copymap.get(file, None)
   168         return self._copymap.get(file, None)
   163 
   169 
   164     def copies(self):
   170     def copies(self):
   165         return self.copymap
   171         return self._copymap
   166 
   172 
   167     def updatedirs(self, path, delta):
   173     def updatedirs(self, path, delta):
   168         for c in strutil.findall(path, '/'):
   174         for c in strutil.findall(path, '/'):
   169             pc = path[:c]
   175             pc = path[:c]
   170             self.dirs.setdefault(pc, 0)
   176             self.dirs.setdefault(pc, 0)
   181                 raise util.Abort(_('directory named %r already in dirstate') %
   187                 raise util.Abort(_('directory named %r already in dirstate') %
   182                                  f)
   188                                  f)
   183             for d in prefixes(f):
   189             for d in prefixes(f):
   184                 if d in seendirs:
   190                 if d in seendirs:
   185                     break
   191                     break
   186                 if d in self.map:
   192                 if d in self._map:
   187                     raise util.Abort(_('file named %r already in dirstate') %
   193                     raise util.Abort(_('file named %r already in dirstate') %
   188                                      d)
   194                                      d)
   189                 seendirs[d] = True
   195                 seendirs[d] = True
   190             # disallowed
   196             # disallowed
   191             if '\r' in f or '\n' in f:
   197             if '\r' in f or '\n' in f:
   202         self.markdirty()
   208         self.markdirty()
   203         if state == "a":
   209         if state == "a":
   204             self.checkinterfering(files)
   210             self.checkinterfering(files)
   205         for f in files:
   211         for f in files:
   206             if state == "r":
   212             if state == "r":
   207                 self.map[f] = ('r', 0, 0, 0)
   213                 self._map[f] = ('r', 0, 0, 0)
   208                 self.updatedirs(f, -1)
   214                 self.updatedirs(f, -1)
   209             else:
   215             else:
   210                 if state == "a":
   216                 if state == "a":
   211                     self.updatedirs(f, 1)
   217                     self.updatedirs(f, 1)
   212                 s = os.lstat(self.wjoin(f))
   218                 s = os.lstat(self.wjoin(f))
   213                 st_size = kw.get('st_size', s.st_size)
   219                 st_size = kw.get('st_size', s.st_size)
   214                 st_mtime = kw.get('st_mtime', s.st_mtime)
   220                 st_mtime = kw.get('st_mtime', s.st_mtime)
   215                 self.map[f] = (state, s.st_mode, st_size, st_mtime)
   221                 self._map[f] = (state, s.st_mode, st_size, st_mtime)
   216             if self.copymap.has_key(f):
   222             if self._copymap.has_key(f):
   217                 del self.copymap[f]
   223                 del self._copymap[f]
   218 
   224 
   219     def forget(self, files):
   225     def forget(self, files):
   220         if not files: return
   226         if not files: return
   221         self.markdirty()
   227         self.markdirty()
   222         for f in files:
   228         for f in files:
   223             try:
   229             try:
   224                 del self.map[f]
   230                 del self._map[f]
   225                 self.updatedirs(f, -1)
   231                 self.updatedirs(f, -1)
   226             except KeyError:
   232             except KeyError:
   227                 self.ui.warn(_("not in dirstate: %s!\n") % f)
   233                 self._ui.warn(_("not in dirstate: %s!\n") % f)
   228                 pass
   234                 pass
   229 
   235 
   230     def rebuild(self, parent, files):
   236     def rebuild(self, parent, files):
   231         self.invalidate()
   237         self.invalidate()
   232         for f in files:
   238         for f in files:
   233             if files.execf(f):
   239             if files.execf(f):
   234                 self.map[f] = ('n', 0777, -1, 0)
   240                 self._map[f] = ('n', 0777, -1, 0)
   235             else:
   241             else:
   236                 self.map[f] = ('n', 0666, -1, 0)
   242                 self._map[f] = ('n', 0666, -1, 0)
   237         self.pl = (parent, nullid)
   243         self._pl = (parent, nullid)
   238         self.markdirty()
   244         self.markdirty()
   239 
   245 
   240     def write(self):
   246     def write(self):
   241         if not self._dirty:
   247         if not self._dirty:
   242             return
   248             return
   243         cs = cStringIO.StringIO()
   249         cs = cStringIO.StringIO()
   244         cs.write("".join(self.pl))
   250         cs.write("".join(self._pl))
   245         for f, e in self.map.iteritems():
   251         for f, e in self._map.iteritems():
   246             c = self.copied(f)
   252             c = self.copied(f)
   247             if c:
   253             if c:
   248                 f = f + "\0" + c
   254                 f = f + "\0" + c
   249             e = struct.pack(_format, e[0], e[1], e[2], e[3], len(f))
   255             e = struct.pack(_format, e[0], e[1], e[2], e[3], len(f))
   250             cs.write(e)
   256             cs.write(e)
   251             cs.write(f)
   257             cs.write(f)
   252         st = self.opener("dirstate", "w", atomictemp=True)
   258         st = self._opener("dirstate", "w", atomictemp=True)
   253         st.write(cs.getvalue())
   259         st.write(cs.getvalue())
   254         st.rename()
   260         st.rename()
   255         self._dirty = 0
   261         self._dirty = 0
   256 
   262 
   257     def filterfiles(self, files):
   263     def filterfiles(self, files):
   258         ret = {}
   264         ret = {}
   259         unknown = []
   265         unknown = []
   260 
   266 
   261         for x in files:
   267         for x in files:
   262             if x == '.':
   268             if x == '.':
   263                 return self.map.copy()
   269                 return self._map.copy()
   264             if x not in self.map:
   270             if x not in self._map:
   265                 unknown.append(x)
   271                 unknown.append(x)
   266             else:
   272             else:
   267                 ret[x] = self.map[x]
   273                 ret[x] = self._map[x]
   268 
   274 
   269         if not unknown:
   275         if not unknown:
   270             return ret
   276             return ret
   271 
   277 
   272         b = self.map.keys()
   278         b = self._map.keys()
   273         b.sort()
   279         b.sort()
   274         blen = len(b)
   280         blen = len(b)
   275 
   281 
   276         for x in unknown:
   282         for x in unknown:
   277             bs = bisect.bisect(b, "%s%s" % (x, '/'))
   283             bs = bisect.bisect(b, "%s%s" % (x, '/'))
   278             while bs < blen:
   284             while bs < blen:
   279                 s = b[bs]
   285                 s = b[bs]
   280                 if len(s) > len(x) and s.startswith(x):
   286                 if len(s) > len(x) and s.startswith(x):
   281                     ret[s] = self.map[s]
   287                     ret[s] = self._map[s]
   282                 else:
   288                 else:
   283                     break
   289                     break
   284                 bs += 1
   290                 bs += 1
   285         return ret
   291         return ret
   286 
   292 
   292             if stat.S_ISCHR(st.st_mode): kind = _('character device')
   298             if stat.S_ISCHR(st.st_mode): kind = _('character device')
   293             elif stat.S_ISBLK(st.st_mode): kind = _('block device')
   299             elif stat.S_ISBLK(st.st_mode): kind = _('block device')
   294             elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
   300             elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
   295             elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
   301             elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
   296             elif stat.S_ISDIR(st.st_mode): kind = _('directory')
   302             elif stat.S_ISDIR(st.st_mode): kind = _('directory')
   297             self.ui.warn(_('%s: unsupported file type (type is %s)\n')
   303             self._ui.warn(_('%s: unsupported file type (type is %s)\n')
   298                          % (self.pathto(f), kind))
   304                          % (self.pathto(f), kind))
   299         return False
   305         return False
   300 
   306 
   301     def walk(self, files=None, match=util.always, badmatch=None):
   307     def walk(self, files=None, match=util.always, badmatch=None):
   302         # filter out the stat
   308         # filter out the stat
   320         '''
   326         '''
   321 
   327 
   322         # walk all files by default
   328         # walk all files by default
   323         if not files:
   329         if not files:
   324             files = ['.']
   330             files = ['.']
   325             dc = self.map.copy()
   331             dc = self._map.copy()
   326         else:
   332         else:
   327             files = util.unique(files)
   333             files = util.unique(files)
   328             dc = self.filterfiles(files)
   334             dc = self.filterfiles(files)
   329 
   335 
   330         def imatch(file_):
   336         def imatch(file_):
   335         ignore = self._ignore
   341         ignore = self._ignore
   336         if ignored:
   342         if ignored:
   337             imatch = match
   343             imatch = match
   338             ignore = util.never
   344             ignore = util.never
   339 
   345 
   340         # self.root may end with a path separator when self.root == '/'
   346         # self._root may end with a path separator when self._root == '/'
   341         common_prefix_len = len(self.root)
   347         common_prefix_len = len(self._root)
   342         if not self.root.endswith(os.sep):
   348         if not self._root.endswith(os.sep):
   343             common_prefix_len += 1
   349             common_prefix_len += 1
   344         # recursion free walker, faster than os.walk.
   350         # recursion free walker, faster than os.walk.
   345         def findfiles(s):
   351         def findfiles(s):
   346             work = [s]
   352             work = [s]
   347             if directories:
   353             if directories:
   400                     if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
   406                     if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
   401                         found = True
   407                         found = True
   402                         break
   408                         break
   403                 if not found:
   409                 if not found:
   404                     if inst.errno != errno.ENOENT or not badmatch:
   410                     if inst.errno != errno.ENOENT or not badmatch:
   405                         self.ui.warn('%s: %s\n' % (self.pathto(ff),
   411                         self._ui.warn('%s: %s\n' % (self.pathto(ff),
   406                                                    inst.strerror))
   412                                                    inst.strerror))
   407                     elif badmatch and badmatch(ff) and imatch(nf):
   413                     elif badmatch and badmatch(ff) and imatch(nf):
   408                         yield 'b', ff, None
   414                         yield 'b', ff, None
   409                 continue
   415                 continue
   410             if stat.S_ISDIR(st.st_mode):
   416             if stat.S_ISDIR(st.st_mode):