--- a/mercurial/context.py Thu Nov 18 15:00:13 2021 +0100
+++ b/mercurial/context.py Thu Nov 18 13:12:40 2021 +0100
@@ -1796,13 +1796,14 @@
sane.append(f)
return sane
- def _checklookup(self, files):
+ def _checklookup(self, files, mtime_boundary):
# check for any possibly clean files
if not files:
- return [], [], []
+ return [], [], [], []
modified = []
deleted = []
+ clean = []
fixup = []
pctx = self._parents[0]
# do a full compare of any files that might have changed
@@ -1816,22 +1817,35 @@
or pctx[f].cmp(self[f])
):
modified.append(f)
+ elif mtime_boundary is None:
+ clean.append(f)
else:
- # XXX note that we have a race windows here since we gather
- # the stats after we compared so the file might have
- # changed.
- #
- # However this have always been the case and the
- # refactoring moving the code here is improving the
- # situation by narrowing the race and moving the two steps
- # (comparison + stat) in the same location.
- #
- # Making this code "correct" is now possible.
s = self[f].lstat()
mode = s.st_mode
size = s.st_size
- mtime = timestamp.mtime_of(s)
- fixup.append((f, (mode, size, mtime)))
+ file_mtime = timestamp.mtime_of(s)
+ cache_info = (mode, size, file_mtime)
+
+ file_second = file_mtime[0]
+ boundary_second = mtime_boundary[0]
+ # If the mtime of the ambiguous file is younger (or equal)
+ # to the starting point of the `status` walk, we cannot
+ # garantee that another, racy, write will not happen right
+ # after with the same mtime and we cannot cache the
+ # information.
+ #
+ # However is the mtime is far away in the future, this is
+ # likely some mismatch between the current clock and
+ # previous file system operation. So mtime more than one days
+ # in the future are considered fine.
+ if (
+ boundary_second
+ <= file_second
+ < (3600 * 24 + boundary_second)
+ ):
+ clean.append(f)
+ else:
+ fixup.append((f, cache_info))
except (IOError, OSError):
# A file become inaccessible in between? Mark it as deleted,
# matching dirstate behavior (issue5584).
@@ -1841,7 +1855,7 @@
# it's in the dirstate.
deleted.append(f)
- return modified, deleted, fixup
+ return modified, deleted, clean, fixup
def _poststatusfixup(self, status, fixup):
"""update dirstate for files that are actually clean"""
@@ -1895,17 +1909,21 @@
subrepos = []
if b'.hgsub' in self:
subrepos = sorted(self.substate)
- cmp, s = self._repo.dirstate.status(
+ cmp, s, mtime_boundary = self._repo.dirstate.status(
match, subrepos, ignored=ignored, clean=clean, unknown=unknown
)
# check for any possibly clean files
fixup = []
if cmp:
- modified2, deleted2, fixup = self._checklookup(cmp)
+ modified2, deleted2, clean_set, fixup = self._checklookup(
+ cmp, mtime_boundary
+ )
s.modified.extend(modified2)
s.deleted.extend(deleted2)
+ if clean_set and clean:
+ s.clean.extend(clean_set)
if fixup and clean:
s.clean.extend((f for f, _ in fixup))