mercurial/dirstate.py
changeset 43076 2372284d9457
parent 42927 d459cd8ea42d
child 43077 687b865b95ad
--- a/mercurial/dirstate.py	Sat Oct 05 10:29:34 2019 -0400
+++ b/mercurial/dirstate.py	Sun Oct 06 09:45:02 2019 -0400
@@ -37,20 +37,25 @@
 
 propertycache = util.propertycache
 filecache = scmutil.filecache
-_rangemask = 0x7fffffff
+_rangemask = 0x7FFFFFFF
 
 dirstatetuple = parsers.dirstatetuple
 
+
 class repocache(filecache):
     """filecache for files in .hg/"""
+
     def join(self, obj, fname):
         return obj._opener.join(fname)
 
+
 class rootcache(filecache):
     """filecache for files in the repository root"""
+
     def join(self, obj, fname):
         return obj._join(fname)
 
+
 def _getfsnow(vfs):
     '''Get "now" timestamp on filesystem'''
     tmpfd, tmpname = vfs.mkstemp()
@@ -60,9 +65,9 @@
         os.close(tmpfd)
         vfs.unlink(tmpname)
 
+
 @interfaceutil.implementer(intdirstate.idirstate)
 class dirstate(object):
-
     def __init__(self, opener, ui, root, validate, sparsematchfn):
         '''Create a new dirstate object.
 
@@ -183,6 +188,7 @@
 
     def flagfunc(self, buildfallback):
         if self._checklink and self._checkexec:
+
             def f(x):
                 try:
                     st = os.lstat(self._join(x))
@@ -193,24 +199,29 @@
                 except OSError:
                     pass
                 return ''
+
             return f
 
         fallback = buildfallback()
         if self._checklink:
+
             def f(x):
                 if os.path.islink(self._join(x)):
                     return 'l'
                 if 'x' in fallback(x):
                     return 'x'
                 return ''
+
             return f
         if self._checkexec:
+
             def f(x):
                 if 'l' in fallback(x):
                     return 'l'
                 if util.isexec(self._join(x)):
                     return 'x'
                 return ''
+
             return f
         else:
             return fallback
@@ -238,7 +249,7 @@
         if not util.endswithsep(rootsep):
             rootsep += pycompat.ossep
         if cwd.startswith(rootsep):
-            return cwd[len(rootsep):]
+            return cwd[len(rootsep) :]
         else:
             # we're outside the repo. return an absolute path.
             return cwd
@@ -296,8 +307,10 @@
         See localrepo.setparents()
         """
         if self._parentwriters == 0:
-            raise ValueError("cannot set dirstate parent outside of "
-                             "dirstate.parentchange context manager")
+            raise ValueError(
+                "cannot set dirstate parent outside of "
+                "dirstate.parentchange context manager"
+            )
 
         self._dirty = True
         oldp2 = self._pl[1]
@@ -307,7 +320,8 @@
         copies = {}
         if oldp2 != nullid and p2 == nullid:
             candidatefiles = self._map.nonnormalset.union(
-                                self._map.otherparentset)
+                self._map.otherparentset
+            )
             for f in candidatefiles:
                 s = self._map.get(f)
                 if s is None:
@@ -339,7 +353,7 @@
             ce = self._filecache['_branch']
             if ce:
                 ce.refresh()
-        except: # re-raises
+        except:  # re-raises
             f.discard()
             raise
 
@@ -382,8 +396,9 @@
         if state == 'a' or oldstate == 'r':
             scmutil.checkfilename(f)
             if self._map.hastrackeddir(f):
-                raise error.Abort(_('directory %r already in dirstate') %
-                                  pycompat.bytestr(f))
+                raise error.Abort(
+                    _('directory %r already in dirstate') % pycompat.bytestr(f)
+                )
             # shadows
             for d in util.finddirs(f):
                 if self._map.hastrackeddir(d):
@@ -391,8 +406,9 @@
                 entry = self._map.get(d)
                 if entry is not None and entry[0] != 'r':
                     raise error.Abort(
-                        _('file %r in dirstate clashes with %r') %
-                        (pycompat.bytestr(d), pycompat.bytestr(f)))
+                        _('file %r in dirstate clashes with %r')
+                        % (pycompat.bytestr(d), pycompat.bytestr(f))
+                    )
         self._dirty = True
         self._updatedfiles.add(f)
         self._map.addfile(f, oldstate, state, mode, size, mtime)
@@ -449,8 +465,9 @@
     def otherparent(self, f):
         '''Mark as coming from the other parent, always dirty.'''
         if self._pl[1] == nullid:
-            raise error.Abort(_("setting %r to other parent "
-                               "only allowed in merges") % f)
+            raise error.Abort(
+                _("setting %r to other parent " "only allowed in merges") % f
+            )
         if f in self and self[f] == 'n':
             # merge-like
             self._addpath(f, 'm', 0, -2, -1)
@@ -473,9 +490,9 @@
             entry = self._map.get(f)
             if entry is not None:
                 # backup the previous state
-                if entry[0] == 'm': # merge
+                if entry[0] == 'm':  # merge
                     size = -1
-                elif entry[0] == 'n' and entry[2] == -2: # other parent
+                elif entry[0] == 'n' and entry[2] == -2:  # other parent
                     size = -2
                     self._map.otherparentset.add(f)
         self._updatedfiles.add(f)
@@ -530,8 +547,9 @@
             if isknown:
                 folded = path
             else:
-                folded = self._discoverpath(path, normed, ignoremissing, exists,
-                                            self._map.filefoldmap)
+                folded = self._discoverpath(
+                    path, normed, ignoremissing, exists, self._map.filefoldmap
+                )
         return folded
 
     def _normalize(self, path, isknown, ignoremissing=False, exists=None):
@@ -545,8 +563,9 @@
             else:
                 # store discovered result in dirfoldmap so that future
                 # normalizefile calls don't start matching directories
-                folded = self._discoverpath(path, normed, ignoremissing, exists,
-                                            self._map.dirfoldmap)
+                folded = self._discoverpath(
+                    path, normed, ignoremissing, exists, self._map.dirfoldmap
+                )
         return folded
 
     def normalize(self, path, isknown=False, ignoremissing=False):
@@ -625,8 +644,12 @@
             self._updatedfiles.clear()
 
             # delay writing in-memory changes out
-            tr.addfilegenerator('dirstate', (self._filename,),
-                                self._writedirstate, location='plain')
+            tr.addfilegenerator(
+                'dirstate',
+                (self._filename,),
+                self._writedirstate,
+                location='plain',
+            )
             return
 
         st = self._opener(filename, "w", atomictemp=True, checkambig=True)
@@ -661,14 +684,15 @@
             items = self._map.iteritems()
             for f, e in items:
                 if e[0] == 'n' and e[3] == now:
-                    import time # to avoid useless import
+                    import time  # to avoid useless import
+
                     # rather than sleep n seconds, sleep until the next
                     # multiple of n seconds
                     clock = time.time()
                     start = int(clock) - (int(clock) % delaywrite)
                     end = start + delaywrite
                     time.sleep(end - clock)
-                    now = end # trust our estimate that the end is near now
+                    now = end  # trust our estimate that the end is near now
                     break
             # since the iterator is potentially not deleted,
             # delete the iterator to release the reference for the Rust
@@ -705,16 +729,18 @@
         visited = set()
         while files:
             i = files.popleft()
-            patterns = matchmod.readpatternfile(i, self._ui.warn,
-                                                sourceinfo=True)
+            patterns = matchmod.readpatternfile(
+                i, self._ui.warn, sourceinfo=True
+            )
             for pattern, lineno, line in patterns:
                 kind, p = matchmod._patsplit(pattern, 'glob')
                 if kind == "subinclude":
                     if p not in visited:
                         files.append(p)
                     continue
-                m = matchmod.match(self._root, '', [], [pattern],
-                                   warn=self._ui.warn)
+                m = matchmod.match(
+                    self._root, '', [], [pattern], warn=self._ui.warn
+                )
                 if m(f):
                     return (i, lineno, line)
             visited.add(i)
@@ -807,10 +833,10 @@
                     badfn(ff, badtype(kind))
                     if nf in dmap:
                         results[nf] = None
-            except OSError as inst: # nf not found on disk - it is dirstate only
-                if nf in dmap: # does it exactly match a missing file?
+            except OSError as inst:  # nf not found on disk - it is dirstate only
+                if nf in dmap:  # does it exactly match a missing file?
                     results[nf] = None
-                else: # does it match a missing directory?
+                else:  # does it match a missing directory?
                     if self._map.hasdir(nf):
                         if matchedir:
                             matchedir(nf)
@@ -852,8 +878,9 @@
             for norm, paths in normed.iteritems():
                 if len(paths) > 1:
                     for path in paths:
-                        folded = self._discoverpath(path, norm, True, None,
-                                                    self._map.dirfoldmap)
+                        folded = self._discoverpath(
+                            path, norm, True, None, self._map.dirfoldmap
+                        )
                         if path != folded:
                             results[path] = None
 
@@ -897,10 +924,10 @@
         join = self._join
 
         exact = skipstep3 = False
-        if match.isexact(): # match.exact
+        if match.isexact():  # match.exact
             exact = True
-            dirignore = util.always # skip step 2
-        elif match.prefix(): # match.match, no patterns
+            dirignore = util.always  # skip step 2
+        elif match.prefix():  # match.match, no patterns
             skipstep3 = True
 
         if not exact and self._checkcase:
@@ -934,8 +961,9 @@
                     entries = listdir(join(nd), stat=True, skip=skip)
                 except OSError as inst:
                     if inst.errno in (errno.EACCES, errno.ENOENT):
-                        match.bad(self.pathto(nd),
-                                  encoding.strtolocal(inst.strerror))
+                        match.bad(
+                            self.pathto(nd), encoding.strtolocal(inst.strerror)
+                        )
                         continue
                     raise
                 for f, kind, st in entries:
@@ -953,8 +981,9 @@
                         # even though f might be a directory, we're only
                         # interested in comparing it to files currently in the
                         # dmap -- therefore normalizefile is enough
-                        nf = normalizefile(nd and (nd + "/" + f) or f, True,
-                                           True)
+                        nf = normalizefile(
+                            nd and (nd + "/" + f) or f, True, True
+                        )
                     else:
                         nf = nd and (nd + "/" + f) or f
                     if nf not in results:
@@ -969,8 +998,9 @@
                             if nf in dmap:
                                 if matchalways or matchfn(nf):
                                     results[nf] = st
-                            elif ((matchalways or matchfn(nf))
-                                  and not ignore(nf)):
+                            elif (matchalways or matchfn(nf)) and not ignore(
+                                nf
+                            ):
                                 # unknown file -- normalize if necessary
                                 if not alreadynormed:
                                     nf = normalize(nf, False, True)
@@ -1011,8 +1041,10 @@
                     # different case, don't add one for this, since that would
                     # make it appear as if the file exists under both names
                     # on disk.
-                    if (normalizefile and
-                        normalizefile(nf, True, True) in results):
+                    if (
+                        normalizefile
+                        and normalizefile(nf, True, True) in results
+                    ):
                         results[nf] = None
                     # Report ignored items in the dmap as long as they are not
                     # under a symlink directory.
@@ -1059,7 +1091,7 @@
         dmap.preload()
         dcontains = dmap.__contains__
         dget = dmap.__getitem__
-        ladd = lookup.append            # aka "unsure"
+        ladd = lookup.append  # aka "unsure"
         madd = modified.append
         aadd = added.append
         uadd = unknown.append
@@ -1078,8 +1110,9 @@
         # - match.traversedir does something, because match.traversedir should
         #   be called for every dir in the working dir
         full = listclean or match.traversedir is not None
-        for fn, st in self.walk(match, subrepos, listunknown, listignored,
-                                full=full).iteritems():
+        for fn, st in self.walk(
+            match, subrepos, listunknown, listignored, full=full
+        ).iteritems():
             if not dcontains(fn):
                 if (listignored or mexact(fn)) and dirignore(fn):
                     if listignored:
@@ -1104,14 +1137,20 @@
             if not st and state in "nma":
                 dadd(fn)
             elif state == 'n':
-                if (size >= 0 and
-                    ((size != st.st_size and size != st.st_size & _rangemask)
-                     or ((mode ^ st.st_mode) & 0o100 and checkexec))
-                    or size == -2 # other parent
-                    or fn in copymap):
+                if (
+                    size >= 0
+                    and (
+                        (size != st.st_size and size != st.st_size & _rangemask)
+                        or ((mode ^ st.st_mode) & 0o100 and checkexec)
+                    )
+                    or size == -2  # other parent
+                    or fn in copymap
+                ):
                     madd(fn)
-                elif (time != st[stat.ST_MTIME]
-                      and time != st[stat.ST_MTIME] & _rangemask):
+                elif (
+                    time != st[stat.ST_MTIME]
+                    and time != st[stat.ST_MTIME] & _rangemask
+                ):
                     ladd(fn)
                 elif st[stat.ST_MTIME] == lastnormaltime:
                     # fn may have just been marked as normal and it may have
@@ -1128,8 +1167,12 @@
             elif state == 'r':
                 radd(fn)
 
-        return (lookup, scmutil.status(modified, added, removed, deleted,
-                                       unknown, ignored, clean))
+        return (
+            lookup,
+            scmutil.status(
+                modified, added, removed, deleted, unknown, ignored, clean
+            ),
+        )
 
     def matches(self, match):
         '''
@@ -1164,15 +1207,20 @@
         # because the latter omits writing out if transaction is running.
         # output file will be used to create backup of dirstate at this point.
         if self._dirty or not self._opener.exists(filename):
-            self._writedirstate(self._opener(filename, "w", atomictemp=True,
-                                             checkambig=True))
+            self._writedirstate(
+                self._opener(filename, "w", atomictemp=True, checkambig=True)
+            )
 
         if tr:
             # ensure that subsequent tr.writepending returns True for
             # changes written out above, even if dirstate is never
             # changed after this
-            tr.addfilegenerator('dirstate', (self._filename,),
-                                self._writedirstate, location='plain')
+            tr.addfilegenerator(
+                'dirstate',
+                (self._filename,),
+                self._writedirstate,
+                location='plain',
+            )
 
             # ensure that pending file written above is unlinked at
             # failure, even if tr.writepending isn't invoked until the
@@ -1182,8 +1230,11 @@
         self._opener.tryunlink(backupname)
         # hardlink backup is okay because _writedirstate is always called
         # with an "atomictemp=True" file.
-        util.copyfile(self._opener.join(filename),
-                      self._opener.join(backupname), hardlink=True)
+        util.copyfile(
+            self._opener.join(filename),
+            self._opener.join(backupname),
+            hardlink=True,
+        )
 
     def restorebackup(self, tr, backupname):
         '''Restore dirstate by backup file'''
@@ -1201,6 +1252,7 @@
         '''Clear backup file'''
         self._opener.unlink(backupname)
 
+
 class dirstatemap(object):
     """Map encapsulating the dirstate's contents.
 
@@ -1376,15 +1428,16 @@
         except AttributeError:
             pass
         else:
-            return makefilefoldmap(self._map, util.normcasespec,
-                                   util.normcasefallback)
+            return makefilefoldmap(
+                self._map, util.normcasespec, util.normcasefallback
+            )
 
         f = {}
         normcase = util.normcase
         for name, s in self._map.iteritems():
             if s[0] != 'r':
                 f[normcase(name)] = name
-        f['.'] = '.' # prevents useless util.fspath() invocation
+        f['.'] = '.'  # prevents useless util.fspath() invocation
         return f
 
     def hastrackeddir(self, d):
@@ -1413,8 +1466,9 @@
         fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
         if self._pendingmode is not None and self._pendingmode != mode:
             fp.close()
-            raise error.Abort(_('working directory state may be '
-                                'changed parallelly'))
+            raise error.Abort(
+                _('working directory state may be ' 'changed parallelly')
+            )
         self._pendingmode = mode
         return fp
 
@@ -1436,8 +1490,9 @@
             elif l == 0:
                 self._parents = (nullid, nullid)
             else:
-                raise error.Abort(_('working directory state appears '
-                                    'damaged!'))
+                raise error.Abort(
+                    _('working directory state appears ' 'damaged!')
+                )
 
         return self._parents
 
@@ -1448,7 +1503,8 @@
     def read(self):
         # ignore HG_PENDING because identity is used only for writing
         self.identity = util.filestat.frompath(
-            self._opener.join(self._filename))
+            self._opener.join(self._filename)
+        )
 
         try:
             fp = self._opendirstatefile()
@@ -1499,8 +1555,9 @@
         self.get = self._map.get
 
     def write(self, st, now):
-        st.write(parsers.pack_dirstate(self._map, self.copymap,
-                                       self.parents(), now))
+        st.write(
+            parsers.pack_dirstate(self._map, self.copymap, self.parents(), now)
+        )
         st.close()
         self._dirtyparents = False
         self.nonnormalset, self.otherparentset = self.nonnormalentries()
@@ -1532,6 +1589,7 @@
 
 
 if rustmod is not None:
+
     class dirstatemap(object):
         def __init__(self, ui, opener, root):
             self._ui = ui
@@ -1604,12 +1662,14 @@
         iteritems = items
 
         def _opendirstatefile(self):
-            fp, mode = txnutil.trypending(self._root, self._opener,
-                                          self._filename)
+            fp, mode = txnutil.trypending(
+                self._root, self._opener, self._filename
+            )
             if self._pendingmode is not None and self._pendingmode != mode:
                 fp.close()
-                raise error.Abort(_('working directory state may be '
-                                    'changed parallelly'))
+                raise error.Abort(
+                    _('working directory state may be ' 'changed parallelly')
+                )
             self._pendingmode = mode
             return fp
 
@@ -1633,15 +1693,17 @@
                 try:
                     self._parents = self._rustmap.parents(st)
                 except ValueError:
-                    raise error.Abort(_('working directory state appears '
-                                        'damaged!'))
+                    raise error.Abort(
+                        _('working directory state appears ' 'damaged!')
+                    )
 
             return self._parents
 
         def read(self):
             # ignore HG_PENDING because identity is used only for writing
             self.identity = util.filestat.frompath(
-                self._opener.join(self._filename))
+                self._opener.join(self._filename)
+            )
 
             try:
                 fp = self._opendirstatefile()
@@ -1675,11 +1737,11 @@
             return self._rustmap.filefoldmapasdict()
 
         def hastrackeddir(self, d):
-            self._dirs # Trigger Python's propertycache
+            self._dirs  # Trigger Python's propertycache
             return self._rustmap.hastrackeddir(d)
 
         def hasdir(self, d):
-            self._dirs # Trigger Python's propertycache
+            self._dirs  # Trigger Python's propertycache
             return self._rustmap.hasdir(d)
 
         @propertycache