mercurial/dirstate.py
changeset 43076 2372284d9457
parent 42927 d459cd8ea42d
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    35 parsers = policy.importmod(r'parsers')
    35 parsers = policy.importmod(r'parsers')
    36 rustmod = policy.importrust(r'dirstate')
    36 rustmod = policy.importrust(r'dirstate')
    37 
    37 
    38 propertycache = util.propertycache
    38 propertycache = util.propertycache
    39 filecache = scmutil.filecache
    39 filecache = scmutil.filecache
    40 _rangemask = 0x7fffffff
    40 _rangemask = 0x7FFFFFFF
    41 
    41 
    42 dirstatetuple = parsers.dirstatetuple
    42 dirstatetuple = parsers.dirstatetuple
       
    43 
    43 
    44 
    44 class repocache(filecache):
    45 class repocache(filecache):
    45     """filecache for files in .hg/"""
    46     """filecache for files in .hg/"""
       
    47 
    46     def join(self, obj, fname):
    48     def join(self, obj, fname):
    47         return obj._opener.join(fname)
    49         return obj._opener.join(fname)
    48 
    50 
       
    51 
    49 class rootcache(filecache):
    52 class rootcache(filecache):
    50     """filecache for files in the repository root"""
    53     """filecache for files in the repository root"""
       
    54 
    51     def join(self, obj, fname):
    55     def join(self, obj, fname):
    52         return obj._join(fname)
    56         return obj._join(fname)
       
    57 
    53 
    58 
    54 def _getfsnow(vfs):
    59 def _getfsnow(vfs):
    55     '''Get "now" timestamp on filesystem'''
    60     '''Get "now" timestamp on filesystem'''
    56     tmpfd, tmpname = vfs.mkstemp()
    61     tmpfd, tmpname = vfs.mkstemp()
    57     try:
    62     try:
    58         return os.fstat(tmpfd)[stat.ST_MTIME]
    63         return os.fstat(tmpfd)[stat.ST_MTIME]
    59     finally:
    64     finally:
    60         os.close(tmpfd)
    65         os.close(tmpfd)
    61         vfs.unlink(tmpname)
    66         vfs.unlink(tmpname)
    62 
    67 
       
    68 
    63 @interfaceutil.implementer(intdirstate.idirstate)
    69 @interfaceutil.implementer(intdirstate.idirstate)
    64 class dirstate(object):
    70 class dirstate(object):
    65 
       
    66     def __init__(self, opener, ui, root, validate, sparsematchfn):
    71     def __init__(self, opener, ui, root, validate, sparsematchfn):
    67         '''Create a new dirstate object.
    72         '''Create a new dirstate object.
    68 
    73 
    69         opener is an open()-like callable that can be used to open the
    74         opener is an open()-like callable that can be used to open the
    70         dirstate file; root is the root of the directory tracked by
    75         dirstate file; root is the root of the directory tracked by
   181         # it's safe because f is always a relative path
   186         # it's safe because f is always a relative path
   182         return self._rootdir + f
   187         return self._rootdir + f
   183 
   188 
   184     def flagfunc(self, buildfallback):
   189     def flagfunc(self, buildfallback):
   185         if self._checklink and self._checkexec:
   190         if self._checklink and self._checkexec:
       
   191 
   186             def f(x):
   192             def f(x):
   187                 try:
   193                 try:
   188                     st = os.lstat(self._join(x))
   194                     st = os.lstat(self._join(x))
   189                     if util.statislink(st):
   195                     if util.statislink(st):
   190                         return 'l'
   196                         return 'l'
   191                     if util.statisexec(st):
   197                     if util.statisexec(st):
   192                         return 'x'
   198                         return 'x'
   193                 except OSError:
   199                 except OSError:
   194                     pass
   200                     pass
   195                 return ''
   201                 return ''
       
   202 
   196             return f
   203             return f
   197 
   204 
   198         fallback = buildfallback()
   205         fallback = buildfallback()
   199         if self._checklink:
   206         if self._checklink:
       
   207 
   200             def f(x):
   208             def f(x):
   201                 if os.path.islink(self._join(x)):
   209                 if os.path.islink(self._join(x)):
   202                     return 'l'
   210                     return 'l'
   203                 if 'x' in fallback(x):
   211                 if 'x' in fallback(x):
   204                     return 'x'
   212                     return 'x'
   205                 return ''
   213                 return ''
       
   214 
   206             return f
   215             return f
   207         if self._checkexec:
   216         if self._checkexec:
       
   217 
   208             def f(x):
   218             def f(x):
   209                 if 'l' in fallback(x):
   219                 if 'l' in fallback(x):
   210                     return 'l'
   220                     return 'l'
   211                 if util.isexec(self._join(x)):
   221                 if util.isexec(self._join(x)):
   212                     return 'x'
   222                     return 'x'
   213                 return ''
   223                 return ''
       
   224 
   214             return f
   225             return f
   215         else:
   226         else:
   216             return fallback
   227             return fallback
   217 
   228 
   218     @propertycache
   229     @propertycache
   236         # self._root ends with a path separator if self._root is '/' or 'C:\'
   247         # self._root ends with a path separator if self._root is '/' or 'C:\'
   237         rootsep = self._root
   248         rootsep = self._root
   238         if not util.endswithsep(rootsep):
   249         if not util.endswithsep(rootsep):
   239             rootsep += pycompat.ossep
   250             rootsep += pycompat.ossep
   240         if cwd.startswith(rootsep):
   251         if cwd.startswith(rootsep):
   241             return cwd[len(rootsep):]
   252             return cwd[len(rootsep) :]
   242         else:
   253         else:
   243             # we're outside the repo. return an absolute path.
   254             # we're outside the repo. return an absolute path.
   244             return cwd
   255             return cwd
   245 
   256 
   246     def pathto(self, f, cwd=None):
   257     def pathto(self, f, cwd=None):
   294         returned by the call.
   305         returned by the call.
   295 
   306 
   296         See localrepo.setparents()
   307         See localrepo.setparents()
   297         """
   308         """
   298         if self._parentwriters == 0:
   309         if self._parentwriters == 0:
   299             raise ValueError("cannot set dirstate parent outside of "
   310             raise ValueError(
   300                              "dirstate.parentchange context manager")
   311                 "cannot set dirstate parent outside of "
       
   312                 "dirstate.parentchange context manager"
       
   313             )
   301 
   314 
   302         self._dirty = True
   315         self._dirty = True
   303         oldp2 = self._pl[1]
   316         oldp2 = self._pl[1]
   304         if self._origpl is None:
   317         if self._origpl is None:
   305             self._origpl = self._pl
   318             self._origpl = self._pl
   306         self._map.setparents(p1, p2)
   319         self._map.setparents(p1, p2)
   307         copies = {}
   320         copies = {}
   308         if oldp2 != nullid and p2 == nullid:
   321         if oldp2 != nullid and p2 == nullid:
   309             candidatefiles = self._map.nonnormalset.union(
   322             candidatefiles = self._map.nonnormalset.union(
   310                                 self._map.otherparentset)
   323                 self._map.otherparentset
       
   324             )
   311             for f in candidatefiles:
   325             for f in candidatefiles:
   312                 s = self._map.get(f)
   326                 s = self._map.get(f)
   313                 if s is None:
   327                 if s is None:
   314                     continue
   328                     continue
   315 
   329 
   337             # make sure filecache has the correct stat info for _branch after
   351             # make sure filecache has the correct stat info for _branch after
   338             # replacing the underlying file
   352             # replacing the underlying file
   339             ce = self._filecache['_branch']
   353             ce = self._filecache['_branch']
   340             if ce:
   354             if ce:
   341                 ce.refresh()
   355                 ce.refresh()
   342         except: # re-raises
   356         except:  # re-raises
   343             f.discard()
   357             f.discard()
   344             raise
   358             raise
   345 
   359 
   346     def invalidate(self):
   360     def invalidate(self):
   347         '''Causes the next access to reread the dirstate.
   361         '''Causes the next access to reread the dirstate.
   380     def _addpath(self, f, state, mode, size, mtime):
   394     def _addpath(self, f, state, mode, size, mtime):
   381         oldstate = self[f]
   395         oldstate = self[f]
   382         if state == 'a' or oldstate == 'r':
   396         if state == 'a' or oldstate == 'r':
   383             scmutil.checkfilename(f)
   397             scmutil.checkfilename(f)
   384             if self._map.hastrackeddir(f):
   398             if self._map.hastrackeddir(f):
   385                 raise error.Abort(_('directory %r already in dirstate') %
   399                 raise error.Abort(
   386                                   pycompat.bytestr(f))
   400                     _('directory %r already in dirstate') % pycompat.bytestr(f)
       
   401                 )
   387             # shadows
   402             # shadows
   388             for d in util.finddirs(f):
   403             for d in util.finddirs(f):
   389                 if self._map.hastrackeddir(d):
   404                 if self._map.hastrackeddir(d):
   390                     break
   405                     break
   391                 entry = self._map.get(d)
   406                 entry = self._map.get(d)
   392                 if entry is not None and entry[0] != 'r':
   407                 if entry is not None and entry[0] != 'r':
   393                     raise error.Abort(
   408                     raise error.Abort(
   394                         _('file %r in dirstate clashes with %r') %
   409                         _('file %r in dirstate clashes with %r')
   395                         (pycompat.bytestr(d), pycompat.bytestr(f)))
   410                         % (pycompat.bytestr(d), pycompat.bytestr(f))
       
   411                     )
   396         self._dirty = True
   412         self._dirty = True
   397         self._updatedfiles.add(f)
   413         self._updatedfiles.add(f)
   398         self._map.addfile(f, oldstate, state, mode, size, mtime)
   414         self._map.addfile(f, oldstate, state, mode, size, mtime)
   399 
   415 
   400     def normal(self, f, parentfiledata=None):
   416     def normal(self, f, parentfiledata=None):
   447         self._map.copymap.pop(f, None)
   463         self._map.copymap.pop(f, None)
   448 
   464 
   449     def otherparent(self, f):
   465     def otherparent(self, f):
   450         '''Mark as coming from the other parent, always dirty.'''
   466         '''Mark as coming from the other parent, always dirty.'''
   451         if self._pl[1] == nullid:
   467         if self._pl[1] == nullid:
   452             raise error.Abort(_("setting %r to other parent "
   468             raise error.Abort(
   453                                "only allowed in merges") % f)
   469                 _("setting %r to other parent " "only allowed in merges") % f
       
   470             )
   454         if f in self and self[f] == 'n':
   471         if f in self and self[f] == 'n':
   455             # merge-like
   472             # merge-like
   456             self._addpath(f, 'm', 0, -2, -1)
   473             self._addpath(f, 'm', 0, -2, -1)
   457         else:
   474         else:
   458             # add-like
   475             # add-like
   471         size = 0
   488         size = 0
   472         if self._pl[1] != nullid:
   489         if self._pl[1] != nullid:
   473             entry = self._map.get(f)
   490             entry = self._map.get(f)
   474             if entry is not None:
   491             if entry is not None:
   475                 # backup the previous state
   492                 # backup the previous state
   476                 if entry[0] == 'm': # merge
   493                 if entry[0] == 'm':  # merge
   477                     size = -1
   494                     size = -1
   478                 elif entry[0] == 'n' and entry[2] == -2: # other parent
   495                 elif entry[0] == 'n' and entry[2] == -2:  # other parent
   479                     size = -2
   496                     size = -2
   480                     self._map.otherparentset.add(f)
   497                     self._map.otherparentset.add(f)
   481         self._updatedfiles.add(f)
   498         self._updatedfiles.add(f)
   482         self._map.removefile(f, oldstate, size)
   499         self._map.removefile(f, oldstate, size)
   483         if size == 0:
   500         if size == 0:
   528         folded = self._map.filefoldmap.get(normed, None)
   545         folded = self._map.filefoldmap.get(normed, None)
   529         if folded is None:
   546         if folded is None:
   530             if isknown:
   547             if isknown:
   531                 folded = path
   548                 folded = path
   532             else:
   549             else:
   533                 folded = self._discoverpath(path, normed, ignoremissing, exists,
   550                 folded = self._discoverpath(
   534                                             self._map.filefoldmap)
   551                     path, normed, ignoremissing, exists, self._map.filefoldmap
       
   552                 )
   535         return folded
   553         return folded
   536 
   554 
   537     def _normalize(self, path, isknown, ignoremissing=False, exists=None):
   555     def _normalize(self, path, isknown, ignoremissing=False, exists=None):
   538         normed = util.normcase(path)
   556         normed = util.normcase(path)
   539         folded = self._map.filefoldmap.get(normed, None)
   557         folded = self._map.filefoldmap.get(normed, None)
   543             if isknown:
   561             if isknown:
   544                 folded = path
   562                 folded = path
   545             else:
   563             else:
   546                 # store discovered result in dirfoldmap so that future
   564                 # store discovered result in dirfoldmap so that future
   547                 # normalizefile calls don't start matching directories
   565                 # normalizefile calls don't start matching directories
   548                 folded = self._discoverpath(path, normed, ignoremissing, exists,
   566                 folded = self._discoverpath(
   549                                             self._map.dirfoldmap)
   567                     path, normed, ignoremissing, exists, self._map.dirfoldmap
       
   568                 )
   550         return folded
   569         return folded
   551 
   570 
   552     def normalize(self, path, isknown=False, ignoremissing=False):
   571     def normalize(self, path, isknown=False, ignoremissing=False):
   553         '''
   572         '''
   554         normalize the case of a pathname when on a casefolding filesystem
   573         normalize the case of a pathname when on a casefolding filesystem
   623             # emulate that all 'dirstate.normal' results are written out
   642             # emulate that all 'dirstate.normal' results are written out
   624             self._lastnormaltime = 0
   643             self._lastnormaltime = 0
   625             self._updatedfiles.clear()
   644             self._updatedfiles.clear()
   626 
   645 
   627             # delay writing in-memory changes out
   646             # delay writing in-memory changes out
   628             tr.addfilegenerator('dirstate', (self._filename,),
   647             tr.addfilegenerator(
   629                                 self._writedirstate, location='plain')
   648                 'dirstate',
       
   649                 (self._filename,),
       
   650                 self._writedirstate,
       
   651                 location='plain',
       
   652             )
   630             return
   653             return
   631 
   654 
   632         st = self._opener(filename, "w", atomictemp=True, checkambig=True)
   655         st = self._opener(filename, "w", atomictemp=True, checkambig=True)
   633         self._writedirstate(st)
   656         self._writedirstate(st)
   634 
   657 
   659         if delaywrite > 0:
   682         if delaywrite > 0:
   660             # do we have any files to delay for?
   683             # do we have any files to delay for?
   661             items = self._map.iteritems()
   684             items = self._map.iteritems()
   662             for f, e in items:
   685             for f, e in items:
   663                 if e[0] == 'n' and e[3] == now:
   686                 if e[0] == 'n' and e[3] == now:
   664                     import time # to avoid useless import
   687                     import time  # to avoid useless import
       
   688 
   665                     # rather than sleep n seconds, sleep until the next
   689                     # rather than sleep n seconds, sleep until the next
   666                     # multiple of n seconds
   690                     # multiple of n seconds
   667                     clock = time.time()
   691                     clock = time.time()
   668                     start = int(clock) - (int(clock) % delaywrite)
   692                     start = int(clock) - (int(clock) % delaywrite)
   669                     end = start + delaywrite
   693                     end = start + delaywrite
   670                     time.sleep(end - clock)
   694                     time.sleep(end - clock)
   671                     now = end # trust our estimate that the end is near now
   695                     now = end  # trust our estimate that the end is near now
   672                     break
   696                     break
   673             # since the iterator is potentially not deleted,
   697             # since the iterator is potentially not deleted,
   674             # delete the iterator to release the reference for the Rust
   698             # delete the iterator to release the reference for the Rust
   675             # implementation.
   699             # implementation.
   676             # TODO make the Rust implementation behave like Python
   700             # TODO make the Rust implementation behave like Python
   703     def _ignorefileandline(self, f):
   727     def _ignorefileandline(self, f):
   704         files = collections.deque(self._ignorefiles())
   728         files = collections.deque(self._ignorefiles())
   705         visited = set()
   729         visited = set()
   706         while files:
   730         while files:
   707             i = files.popleft()
   731             i = files.popleft()
   708             patterns = matchmod.readpatternfile(i, self._ui.warn,
   732             patterns = matchmod.readpatternfile(
   709                                                 sourceinfo=True)
   733                 i, self._ui.warn, sourceinfo=True
       
   734             )
   710             for pattern, lineno, line in patterns:
   735             for pattern, lineno, line in patterns:
   711                 kind, p = matchmod._patsplit(pattern, 'glob')
   736                 kind, p = matchmod._patsplit(pattern, 'glob')
   712                 if kind == "subinclude":
   737                 if kind == "subinclude":
   713                     if p not in visited:
   738                     if p not in visited:
   714                         files.append(p)
   739                         files.append(p)
   715                     continue
   740                     continue
   716                 m = matchmod.match(self._root, '', [], [pattern],
   741                 m = matchmod.match(
   717                                    warn=self._ui.warn)
   742                     self._root, '', [], [pattern], warn=self._ui.warn
       
   743                 )
   718                 if m(f):
   744                 if m(f):
   719                     return (i, lineno, line)
   745                     return (i, lineno, line)
   720             visited.add(i)
   746             visited.add(i)
   721         return (None, -1, "")
   747         return (None, -1, "")
   722 
   748 
   805                     results[nf] = st
   831                     results[nf] = st
   806                 else:
   832                 else:
   807                     badfn(ff, badtype(kind))
   833                     badfn(ff, badtype(kind))
   808                     if nf in dmap:
   834                     if nf in dmap:
   809                         results[nf] = None
   835                         results[nf] = None
   810             except OSError as inst: # nf not found on disk - it is dirstate only
   836             except OSError as inst:  # nf not found on disk - it is dirstate only
   811                 if nf in dmap: # does it exactly match a missing file?
   837                 if nf in dmap:  # does it exactly match a missing file?
   812                     results[nf] = None
   838                     results[nf] = None
   813                 else: # does it match a missing directory?
   839                 else:  # does it match a missing directory?
   814                     if self._map.hasdir(nf):
   840                     if self._map.hasdir(nf):
   815                         if matchedir:
   841                         if matchedir:
   816                             matchedir(nf)
   842                             matchedir(nf)
   817                         notfoundadd(nf)
   843                         notfoundadd(nf)
   818                     else:
   844                     else:
   850                 paths.add(f)
   876                 paths.add(f)
   851 
   877 
   852             for norm, paths in normed.iteritems():
   878             for norm, paths in normed.iteritems():
   853                 if len(paths) > 1:
   879                 if len(paths) > 1:
   854                     for path in paths:
   880                     for path in paths:
   855                         folded = self._discoverpath(path, norm, True, None,
   881                         folded = self._discoverpath(
   856                                                     self._map.dirfoldmap)
   882                             path, norm, True, None, self._map.dirfoldmap
       
   883                         )
   857                         if path != folded:
   884                         if path != folded:
   858                             results[path] = None
   885                             results[path] = None
   859 
   886 
   860         return results, dirsfound, dirsnotfound
   887         return results, dirsfound, dirsnotfound
   861 
   888 
   895         regkind = stat.S_IFREG
   922         regkind = stat.S_IFREG
   896         lnkkind = stat.S_IFLNK
   923         lnkkind = stat.S_IFLNK
   897         join = self._join
   924         join = self._join
   898 
   925 
   899         exact = skipstep3 = False
   926         exact = skipstep3 = False
   900         if match.isexact(): # match.exact
   927         if match.isexact():  # match.exact
   901             exact = True
   928             exact = True
   902             dirignore = util.always # skip step 2
   929             dirignore = util.always  # skip step 2
   903         elif match.prefix(): # match.match, no patterns
   930         elif match.prefix():  # match.match, no patterns
   904             skipstep3 = True
   931             skipstep3 = True
   905 
   932 
   906         if not exact and self._checkcase:
   933         if not exact and self._checkcase:
   907             normalize = self._normalize
   934             normalize = self._normalize
   908             normalizefile = self._normalizefile
   935             normalizefile = self._normalizefile
   932                     skip = '.hg'
   959                     skip = '.hg'
   933                 try:
   960                 try:
   934                     entries = listdir(join(nd), stat=True, skip=skip)
   961                     entries = listdir(join(nd), stat=True, skip=skip)
   935                 except OSError as inst:
   962                 except OSError as inst:
   936                     if inst.errno in (errno.EACCES, errno.ENOENT):
   963                     if inst.errno in (errno.EACCES, errno.ENOENT):
   937                         match.bad(self.pathto(nd),
   964                         match.bad(
   938                                   encoding.strtolocal(inst.strerror))
   965                             self.pathto(nd), encoding.strtolocal(inst.strerror)
       
   966                         )
   939                         continue
   967                         continue
   940                     raise
   968                     raise
   941                 for f, kind, st in entries:
   969                 for f, kind, st in entries:
   942                     # Some matchers may return files in the visitentries set,
   970                     # Some matchers may return files in the visitentries set,
   943                     # instead of 'this', if the matcher explicitly mentions them
   971                     # instead of 'this', if the matcher explicitly mentions them
   951                         continue
   979                         continue
   952                     if normalizefile:
   980                     if normalizefile:
   953                         # even though f might be a directory, we're only
   981                         # even though f might be a directory, we're only
   954                         # interested in comparing it to files currently in the
   982                         # interested in comparing it to files currently in the
   955                         # dmap -- therefore normalizefile is enough
   983                         # dmap -- therefore normalizefile is enough
   956                         nf = normalizefile(nd and (nd + "/" + f) or f, True,
   984                         nf = normalizefile(
   957                                            True)
   985                             nd and (nd + "/" + f) or f, True, True
       
   986                         )
   958                     else:
   987                     else:
   959                         nf = nd and (nd + "/" + f) or f
   988                         nf = nd and (nd + "/" + f) or f
   960                     if nf not in results:
   989                     if nf not in results:
   961                         if kind == dirkind:
   990                         if kind == dirkind:
   962                             if not ignore(nf):
   991                             if not ignore(nf):
   967                                 results[nf] = None
   996                                 results[nf] = None
   968                         elif kind == regkind or kind == lnkkind:
   997                         elif kind == regkind or kind == lnkkind:
   969                             if nf in dmap:
   998                             if nf in dmap:
   970                                 if matchalways or matchfn(nf):
   999                                 if matchalways or matchfn(nf):
   971                                     results[nf] = st
  1000                                     results[nf] = st
   972                             elif ((matchalways or matchfn(nf))
  1001                             elif (matchalways or matchfn(nf)) and not ignore(
   973                                   and not ignore(nf)):
  1002                                 nf
       
  1003                             ):
   974                                 # unknown file -- normalize if necessary
  1004                                 # unknown file -- normalize if necessary
   975                                 if not alreadynormed:
  1005                                 if not alreadynormed:
   976                                     nf = normalize(nf, False, True)
  1006                                     nf = normalize(nf, False, True)
   977                                 results[nf] = st
  1007                                 results[nf] = st
   978                         elif nf in dmap and (matchalways or matchfn(nf)):
  1008                         elif nf in dmap and (matchalways or matchfn(nf)):
  1009                 for nf in iter(visit):
  1039                 for nf in iter(visit):
  1010                     # If a stat for the same file was already added with a
  1040                     # If a stat for the same file was already added with a
  1011                     # different case, don't add one for this, since that would
  1041                     # different case, don't add one for this, since that would
  1012                     # make it appear as if the file exists under both names
  1042                     # make it appear as if the file exists under both names
  1013                     # on disk.
  1043                     # on disk.
  1014                     if (normalizefile and
  1044                     if (
  1015                         normalizefile(nf, True, True) in results):
  1045                         normalizefile
       
  1046                         and normalizefile(nf, True, True) in results
       
  1047                     ):
  1016                         results[nf] = None
  1048                         results[nf] = None
  1017                     # Report ignored items in the dmap as long as they are not
  1049                     # Report ignored items in the dmap as long as they are not
  1018                     # under a symlink directory.
  1050                     # under a symlink directory.
  1019                     elif audit_path.check(nf):
  1051                     elif audit_path.check(nf):
  1020                         try:
  1052                         try:
  1057 
  1089 
  1058         dmap = self._map
  1090         dmap = self._map
  1059         dmap.preload()
  1091         dmap.preload()
  1060         dcontains = dmap.__contains__
  1092         dcontains = dmap.__contains__
  1061         dget = dmap.__getitem__
  1093         dget = dmap.__getitem__
  1062         ladd = lookup.append            # aka "unsure"
  1094         ladd = lookup.append  # aka "unsure"
  1063         madd = modified.append
  1095         madd = modified.append
  1064         aadd = added.append
  1096         aadd = added.append
  1065         uadd = unknown.append
  1097         uadd = unknown.append
  1066         iadd = ignored.append
  1098         iadd = ignored.append
  1067         radd = removed.append
  1099         radd = removed.append
  1076         # We need to do full walks when either
  1108         # We need to do full walks when either
  1077         # - we're listing all clean files, or
  1109         # - we're listing all clean files, or
  1078         # - match.traversedir does something, because match.traversedir should
  1110         # - match.traversedir does something, because match.traversedir should
  1079         #   be called for every dir in the working dir
  1111         #   be called for every dir in the working dir
  1080         full = listclean or match.traversedir is not None
  1112         full = listclean or match.traversedir is not None
  1081         for fn, st in self.walk(match, subrepos, listunknown, listignored,
  1113         for fn, st in self.walk(
  1082                                 full=full).iteritems():
  1114             match, subrepos, listunknown, listignored, full=full
       
  1115         ).iteritems():
  1083             if not dcontains(fn):
  1116             if not dcontains(fn):
  1084                 if (listignored or mexact(fn)) and dirignore(fn):
  1117                 if (listignored or mexact(fn)) and dirignore(fn):
  1085                     if listignored:
  1118                     if listignored:
  1086                         iadd(fn)
  1119                         iadd(fn)
  1087                 else:
  1120                 else:
  1102             time = t[3]
  1135             time = t[3]
  1103 
  1136 
  1104             if not st and state in "nma":
  1137             if not st and state in "nma":
  1105                 dadd(fn)
  1138                 dadd(fn)
  1106             elif state == 'n':
  1139             elif state == 'n':
  1107                 if (size >= 0 and
  1140                 if (
  1108                     ((size != st.st_size and size != st.st_size & _rangemask)
  1141                     size >= 0
  1109                      or ((mode ^ st.st_mode) & 0o100 and checkexec))
  1142                     and (
  1110                     or size == -2 # other parent
  1143                         (size != st.st_size and size != st.st_size & _rangemask)
  1111                     or fn in copymap):
  1144                         or ((mode ^ st.st_mode) & 0o100 and checkexec)
       
  1145                     )
       
  1146                     or size == -2  # other parent
       
  1147                     or fn in copymap
       
  1148                 ):
  1112                     madd(fn)
  1149                     madd(fn)
  1113                 elif (time != st[stat.ST_MTIME]
  1150                 elif (
  1114                       and time != st[stat.ST_MTIME] & _rangemask):
  1151                     time != st[stat.ST_MTIME]
       
  1152                     and time != st[stat.ST_MTIME] & _rangemask
       
  1153                 ):
  1115                     ladd(fn)
  1154                     ladd(fn)
  1116                 elif st[stat.ST_MTIME] == lastnormaltime:
  1155                 elif st[stat.ST_MTIME] == lastnormaltime:
  1117                     # fn may have just been marked as normal and it may have
  1156                     # fn may have just been marked as normal and it may have
  1118                     # changed in the same second without changing its size.
  1157                     # changed in the same second without changing its size.
  1119                     # This can happen if we quickly do multiple commits.
  1158                     # This can happen if we quickly do multiple commits.
  1126             elif state == 'a':
  1165             elif state == 'a':
  1127                 aadd(fn)
  1166                 aadd(fn)
  1128             elif state == 'r':
  1167             elif state == 'r':
  1129                 radd(fn)
  1168                 radd(fn)
  1130 
  1169 
  1131         return (lookup, scmutil.status(modified, added, removed, deleted,
  1170         return (
  1132                                        unknown, ignored, clean))
  1171             lookup,
       
  1172             scmutil.status(
       
  1173                 modified, added, removed, deleted, unknown, ignored, clean
       
  1174             ),
       
  1175         )
  1133 
  1176 
  1134     def matches(self, match):
  1177     def matches(self, match):
  1135         '''
  1178         '''
  1136         return files in the dirstate (in whatever state) filtered by match
  1179         return files in the dirstate (in whatever state) filtered by match
  1137         '''
  1180         '''
  1162 
  1205 
  1163         # use '_writedirstate' instead of 'write' to write changes certainly,
  1206         # use '_writedirstate' instead of 'write' to write changes certainly,
  1164         # because the latter omits writing out if transaction is running.
  1207         # because the latter omits writing out if transaction is running.
  1165         # output file will be used to create backup of dirstate at this point.
  1208         # output file will be used to create backup of dirstate at this point.
  1166         if self._dirty or not self._opener.exists(filename):
  1209         if self._dirty or not self._opener.exists(filename):
  1167             self._writedirstate(self._opener(filename, "w", atomictemp=True,
  1210             self._writedirstate(
  1168                                              checkambig=True))
  1211                 self._opener(filename, "w", atomictemp=True, checkambig=True)
       
  1212             )
  1169 
  1213 
  1170         if tr:
  1214         if tr:
  1171             # ensure that subsequent tr.writepending returns True for
  1215             # ensure that subsequent tr.writepending returns True for
  1172             # changes written out above, even if dirstate is never
  1216             # changes written out above, even if dirstate is never
  1173             # changed after this
  1217             # changed after this
  1174             tr.addfilegenerator('dirstate', (self._filename,),
  1218             tr.addfilegenerator(
  1175                                 self._writedirstate, location='plain')
  1219                 'dirstate',
       
  1220                 (self._filename,),
       
  1221                 self._writedirstate,
       
  1222                 location='plain',
       
  1223             )
  1176 
  1224 
  1177             # ensure that pending file written above is unlinked at
  1225             # ensure that pending file written above is unlinked at
  1178             # failure, even if tr.writepending isn't invoked until the
  1226             # failure, even if tr.writepending isn't invoked until the
  1179             # end of this transaction
  1227             # end of this transaction
  1180             tr.registertmp(filename, location='plain')
  1228             tr.registertmp(filename, location='plain')
  1181 
  1229 
  1182         self._opener.tryunlink(backupname)
  1230         self._opener.tryunlink(backupname)
  1183         # hardlink backup is okay because _writedirstate is always called
  1231         # hardlink backup is okay because _writedirstate is always called
  1184         # with an "atomictemp=True" file.
  1232         # with an "atomictemp=True" file.
  1185         util.copyfile(self._opener.join(filename),
  1233         util.copyfile(
  1186                       self._opener.join(backupname), hardlink=True)
  1234             self._opener.join(filename),
       
  1235             self._opener.join(backupname),
       
  1236             hardlink=True,
       
  1237         )
  1187 
  1238 
  1188     def restorebackup(self, tr, backupname):
  1239     def restorebackup(self, tr, backupname):
  1189         '''Restore dirstate by backup file'''
  1240         '''Restore dirstate by backup file'''
  1190         # this "invalidate()" prevents "wlock.release()" from writing
  1241         # this "invalidate()" prevents "wlock.release()" from writing
  1191         # changes of dirstate out after restoring from backup file
  1242         # changes of dirstate out after restoring from backup file
  1198             o.rename(backupname, filename, checkambig=True)
  1249             o.rename(backupname, filename, checkambig=True)
  1199 
  1250 
  1200     def clearbackup(self, tr, backupname):
  1251     def clearbackup(self, tr, backupname):
  1201         '''Clear backup file'''
  1252         '''Clear backup file'''
  1202         self._opener.unlink(backupname)
  1253         self._opener.unlink(backupname)
       
  1254 
  1203 
  1255 
  1204 class dirstatemap(object):
  1256 class dirstatemap(object):
  1205     """Map encapsulating the dirstate's contents.
  1257     """Map encapsulating the dirstate's contents.
  1206 
  1258 
  1207     The dirstate contains the following state:
  1259     The dirstate contains the following state:
  1374         try:
  1426         try:
  1375             makefilefoldmap = parsers.make_file_foldmap
  1427             makefilefoldmap = parsers.make_file_foldmap
  1376         except AttributeError:
  1428         except AttributeError:
  1377             pass
  1429             pass
  1378         else:
  1430         else:
  1379             return makefilefoldmap(self._map, util.normcasespec,
  1431             return makefilefoldmap(
  1380                                    util.normcasefallback)
  1432                 self._map, util.normcasespec, util.normcasefallback
       
  1433             )
  1381 
  1434 
  1382         f = {}
  1435         f = {}
  1383         normcase = util.normcase
  1436         normcase = util.normcase
  1384         for name, s in self._map.iteritems():
  1437         for name, s in self._map.iteritems():
  1385             if s[0] != 'r':
  1438             if s[0] != 'r':
  1386                 f[normcase(name)] = name
  1439                 f[normcase(name)] = name
  1387         f['.'] = '.' # prevents useless util.fspath() invocation
  1440         f['.'] = '.'  # prevents useless util.fspath() invocation
  1388         return f
  1441         return f
  1389 
  1442 
  1390     def hastrackeddir(self, d):
  1443     def hastrackeddir(self, d):
  1391         """
  1444         """
  1392         Returns True if the dirstate contains a tracked (not removed) file
  1445         Returns True if the dirstate contains a tracked (not removed) file
  1411 
  1464 
  1412     def _opendirstatefile(self):
  1465     def _opendirstatefile(self):
  1413         fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
  1466         fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
  1414         if self._pendingmode is not None and self._pendingmode != mode:
  1467         if self._pendingmode is not None and self._pendingmode != mode:
  1415             fp.close()
  1468             fp.close()
  1416             raise error.Abort(_('working directory state may be '
  1469             raise error.Abort(
  1417                                 'changed parallelly'))
  1470                 _('working directory state may be ' 'changed parallelly')
       
  1471             )
  1418         self._pendingmode = mode
  1472         self._pendingmode = mode
  1419         return fp
  1473         return fp
  1420 
  1474 
  1421     def parents(self):
  1475     def parents(self):
  1422         if not self._parents:
  1476         if not self._parents:
  1434             if l == 40:
  1488             if l == 40:
  1435                 self._parents = (st[:20], st[20:40])
  1489                 self._parents = (st[:20], st[20:40])
  1436             elif l == 0:
  1490             elif l == 0:
  1437                 self._parents = (nullid, nullid)
  1491                 self._parents = (nullid, nullid)
  1438             else:
  1492             else:
  1439                 raise error.Abort(_('working directory state appears '
  1493                 raise error.Abort(
  1440                                     'damaged!'))
  1494                     _('working directory state appears ' 'damaged!')
       
  1495                 )
  1441 
  1496 
  1442         return self._parents
  1497         return self._parents
  1443 
  1498 
  1444     def setparents(self, p1, p2):
  1499     def setparents(self, p1, p2):
  1445         self._parents = (p1, p2)
  1500         self._parents = (p1, p2)
  1446         self._dirtyparents = True
  1501         self._dirtyparents = True
  1447 
  1502 
  1448     def read(self):
  1503     def read(self):
  1449         # ignore HG_PENDING because identity is used only for writing
  1504         # ignore HG_PENDING because identity is used only for writing
  1450         self.identity = util.filestat.frompath(
  1505         self.identity = util.filestat.frompath(
  1451             self._opener.join(self._filename))
  1506             self._opener.join(self._filename)
       
  1507         )
  1452 
  1508 
  1453         try:
  1509         try:
  1454             fp = self._opendirstatefile()
  1510             fp = self._opendirstatefile()
  1455             try:
  1511             try:
  1456                 st = fp.read()
  1512                 st = fp.read()
  1497         self.__contains__ = self._map.__contains__
  1553         self.__contains__ = self._map.__contains__
  1498         self.__getitem__ = self._map.__getitem__
  1554         self.__getitem__ = self._map.__getitem__
  1499         self.get = self._map.get
  1555         self.get = self._map.get
  1500 
  1556 
  1501     def write(self, st, now):
  1557     def write(self, st, now):
  1502         st.write(parsers.pack_dirstate(self._map, self.copymap,
  1558         st.write(
  1503                                        self.parents(), now))
  1559             parsers.pack_dirstate(self._map, self.copymap, self.parents(), now)
       
  1560         )
  1504         st.close()
  1561         st.close()
  1505         self._dirtyparents = False
  1562         self._dirtyparents = False
  1506         self.nonnormalset, self.otherparentset = self.nonnormalentries()
  1563         self.nonnormalset, self.otherparentset = self.nonnormalentries()
  1507 
  1564 
  1508     @propertycache
  1565     @propertycache
  1530             f[normcase(name)] = name
  1587             f[normcase(name)] = name
  1531         return f
  1588         return f
  1532 
  1589 
  1533 
  1590 
  1534 if rustmod is not None:
  1591 if rustmod is not None:
       
  1592 
  1535     class dirstatemap(object):
  1593     class dirstatemap(object):
  1536         def __init__(self, ui, opener, root):
  1594         def __init__(self, ui, opener, root):
  1537             self._ui = ui
  1595             self._ui = ui
  1538             self._opener = opener
  1596             self._opener = opener
  1539             self._root = root
  1597             self._root = root
  1602 
  1660 
  1603         # forward for python2,3 compat
  1661         # forward for python2,3 compat
  1604         iteritems = items
  1662         iteritems = items
  1605 
  1663 
  1606         def _opendirstatefile(self):
  1664         def _opendirstatefile(self):
  1607             fp, mode = txnutil.trypending(self._root, self._opener,
  1665             fp, mode = txnutil.trypending(
  1608                                           self._filename)
  1666                 self._root, self._opener, self._filename
       
  1667             )
  1609             if self._pendingmode is not None and self._pendingmode != mode:
  1668             if self._pendingmode is not None and self._pendingmode != mode:
  1610                 fp.close()
  1669                 fp.close()
  1611                 raise error.Abort(_('working directory state may be '
  1670                 raise error.Abort(
  1612                                     'changed parallelly'))
  1671                     _('working directory state may be ' 'changed parallelly')
       
  1672                 )
  1613             self._pendingmode = mode
  1673             self._pendingmode = mode
  1614             return fp
  1674             return fp
  1615 
  1675 
  1616         def setparents(self, p1, p2):
  1676         def setparents(self, p1, p2):
  1617             self._rustmap.setparents(p1, p2)
  1677             self._rustmap.setparents(p1, p2)
  1631                     st = ''
  1691                     st = ''
  1632 
  1692 
  1633                 try:
  1693                 try:
  1634                     self._parents = self._rustmap.parents(st)
  1694                     self._parents = self._rustmap.parents(st)
  1635                 except ValueError:
  1695                 except ValueError:
  1636                     raise error.Abort(_('working directory state appears '
  1696                     raise error.Abort(
  1637                                         'damaged!'))
  1697                         _('working directory state appears ' 'damaged!')
       
  1698                     )
  1638 
  1699 
  1639             return self._parents
  1700             return self._parents
  1640 
  1701 
  1641         def read(self):
  1702         def read(self):
  1642             # ignore HG_PENDING because identity is used only for writing
  1703             # ignore HG_PENDING because identity is used only for writing
  1643             self.identity = util.filestat.frompath(
  1704             self.identity = util.filestat.frompath(
  1644                 self._opener.join(self._filename))
  1705                 self._opener.join(self._filename)
       
  1706             )
  1645 
  1707 
  1646             try:
  1708             try:
  1647                 fp = self._opendirstatefile()
  1709                 fp = self._opendirstatefile()
  1648                 try:
  1710                 try:
  1649                     st = fp.read()
  1711                     st = fp.read()
  1673             non-normalized versions.
  1735             non-normalized versions.
  1674             """
  1736             """
  1675             return self._rustmap.filefoldmapasdict()
  1737             return self._rustmap.filefoldmapasdict()
  1676 
  1738 
  1677         def hastrackeddir(self, d):
  1739         def hastrackeddir(self, d):
  1678             self._dirs # Trigger Python's propertycache
  1740             self._dirs  # Trigger Python's propertycache
  1679             return self._rustmap.hastrackeddir(d)
  1741             return self._rustmap.hastrackeddir(d)
  1680 
  1742 
  1681         def hasdir(self, d):
  1743         def hasdir(self, d):
  1682             self._dirs # Trigger Python's propertycache
  1744             self._dirs  # Trigger Python's propertycache
  1683             return self._rustmap.hasdir(d)
  1745             return self._rustmap.hasdir(d)
  1684 
  1746 
  1685         @propertycache
  1747         @propertycache
  1686         def _dirs(self):
  1748         def _dirs(self):
  1687             return self._rustmap.getdirs()
  1749             return self._rustmap.getdirs()