comparison mercurial/dirstate.py @ 48260:269ff8978086

dirstate: store mtimes with nanosecond precision in memory Keep integer seconds since the Unix epoch, together with integer nanoseconds in the `0 <= n < 1e9` range. For now, nanoseconds are still always zero. This commit is about data structure changes. Differential Revision: https://phab.mercurial-scm.org/D11684
author Simon Sapin <simon.sapin@octobus.net>
date Mon, 18 Oct 2021 11:23:07 +0200
parents 602c8e8411f5
children 8f54d9c79b12
comparison
equal deleted inserted replaced
48259:84f6b0c41b90 48260:269ff8978086
29 scmutil, 29 scmutil,
30 sparse, 30 sparse,
31 util, 31 util,
32 ) 32 )
33 33
34 from .dirstateutils import (
35 timestamp,
36 )
37
34 from .interfaces import ( 38 from .interfaces import (
35 dirstate as intdirstate, 39 dirstate as intdirstate,
36 util as interfaceutil, 40 util as interfaceutil,
37 ) 41 )
38 42
64 68
65 def _getfsnow(vfs): 69 def _getfsnow(vfs):
66 '''Get "now" timestamp on filesystem''' 70 '''Get "now" timestamp on filesystem'''
67 tmpfd, tmpname = vfs.mkstemp() 71 tmpfd, tmpname = vfs.mkstemp()
68 try: 72 try:
69 return os.fstat(tmpfd)[stat.ST_MTIME] 73 return timestamp.mtime_of(os.fstat(tmpfd))
70 finally: 74 finally:
71 os.close(tmpfd) 75 os.close(tmpfd)
72 vfs.unlink(tmpname) 76 vfs.unlink(tmpname)
73 77
74 78
120 self._sparsematchfn = sparsematchfn 124 self._sparsematchfn = sparsematchfn
121 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is 125 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
122 # UNC path pointing to root share (issue4557) 126 # UNC path pointing to root share (issue4557)
123 self._rootdir = pathutil.normasprefix(root) 127 self._rootdir = pathutil.normasprefix(root)
124 self._dirty = False 128 self._dirty = False
125 self._lastnormaltime = 0 129 self._lastnormaltime = timestamp.zero()
126 self._ui = ui 130 self._ui = ui
127 self._filecache = {} 131 self._filecache = {}
128 self._parentwriters = 0 132 self._parentwriters = 0
129 self._filename = b'dirstate' 133 self._filename = b'dirstate'
130 self._pendingfilename = b'%s.pending' % self._filename 134 self._pendingfilename = b'%s.pending' % self._filename
438 check whether the dirstate has changed before rereading it.""" 442 check whether the dirstate has changed before rereading it."""
439 443
440 for a in ("_map", "_branch", "_ignore"): 444 for a in ("_map", "_branch", "_ignore"):
441 if a in self.__dict__: 445 if a in self.__dict__:
442 delattr(self, a) 446 delattr(self, a)
443 self._lastnormaltime = 0 447 self._lastnormaltime = timestamp.zero()
444 self._dirty = False 448 self._dirty = False
445 self._parentwriters = 0 449 self._parentwriters = 0
446 self._origpl = None 450 self._origpl = None
447 451
448 def copy(self, source, dest): 452 def copy(self, source, dest):
637 def _get_filedata(self, filename): 641 def _get_filedata(self, filename):
638 """returns""" 642 """returns"""
639 s = os.lstat(self._join(filename)) 643 s = os.lstat(self._join(filename))
640 mode = s.st_mode 644 mode = s.st_mode
641 size = s.st_size 645 size = s.st_size
642 mtime = s[stat.ST_MTIME] 646 mtime = timestamp.mtime_of(s)
643 return (mode, size, mtime) 647 return (mode, size, mtime)
644 648
645 def _discoverpath(self, path, normed, ignoremissing, exists, storemap): 649 def _discoverpath(self, path, normed, ignoremissing, exists, storemap):
646 if exists is None: 650 if exists is None:
647 exists = os.path.lexists(os.path.join(self._root, path)) 651 exists = os.path.lexists(os.path.join(self._root, path))
718 return self._normalize(path, isknown, ignoremissing) 722 return self._normalize(path, isknown, ignoremissing)
719 return path 723 return path
720 724
721 def clear(self): 725 def clear(self):
722 self._map.clear() 726 self._map.clear()
723 self._lastnormaltime = 0 727 self._lastnormaltime = timestamp.zero()
724 self._dirty = True 728 self._dirty = True
725 729
726 def rebuild(self, parent, allfiles, changedfiles=None): 730 def rebuild(self, parent, allfiles, changedfiles=None):
727 if changedfiles is None: 731 if changedfiles is None:
728 # Rebuild entire dirstate 732 # Rebuild entire dirstate
821 self._origpl = None 825 self._origpl = None
822 826
823 if now is None: 827 if now is None:
824 # use the modification time of the newly created temporary file as the 828 # use the modification time of the newly created temporary file as the
825 # filesystem's notion of 'now' 829 # filesystem's notion of 'now'
826 now = util.fstat(st)[stat.ST_MTIME] & _rangemask 830 now = timestamp.mtime_of(util.fstat(st))
827 831
828 # enough 'delaywrite' prevents 'pack_dirstate' from dropping 832 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
829 # timestamp of each entries in dirstate, because of 'now > mtime' 833 # timestamp of each entries in dirstate, because of 'now > mtime'
830 delaywrite = self._ui.configint(b'debug', b'dirstate.delaywrite') 834 delaywrite = self._ui.configint(b'debug', b'dirstate.delaywrite')
831 if delaywrite > 0: 835 if delaywrite > 0:
838 # multiple of n seconds 842 # multiple of n seconds
839 clock = time.time() 843 clock = time.time()
840 start = int(clock) - (int(clock) % delaywrite) 844 start = int(clock) - (int(clock) % delaywrite)
841 end = start + delaywrite 845 end = start + delaywrite
842 time.sleep(end - clock) 846 time.sleep(end - clock)
843 now = end # trust our estimate that the end is near now 847 # trust our estimate that the end is near now
848 now = timestamp.timestamp((end, 0))
844 break 849 break
845 850
846 self._map.write(tr, st, now) 851 self._map.write(tr, st, now)
847 self._lastnormaltime = 0 852 self._lastnormaltime = timestamp.zero()
848 self._dirty = False 853 self._dirty = False
849 854
850 def _dirignore(self, f): 855 def _dirignore(self, f):
851 if self._ignore(f): 856 if self._ignore(f):
852 return True 857 return True
1375 iadd(fn) 1380 iadd(fn)
1376 else: 1381 else:
1377 uadd(fn) 1382 uadd(fn)
1378 continue 1383 continue
1379 1384
1380 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
1381 # written like that for performance reasons. dmap[fn] is not a
1382 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
1383 # opcode has fast paths when the value to be unpacked is a tuple or
1384 # a list, but falls back to creating a full-fledged iterator in
1385 # general. That is much slower than simply accessing and storing the
1386 # tuple members one by one.
1387 t = dget(fn) 1385 t = dget(fn)
1388 mode = t.mode 1386 mode = t.mode
1389 size = t.size 1387 size = t.size
1390 time = t.mtime
1391 1388
1392 if not st and t.tracked: 1389 if not st and t.tracked:
1393 dadd(fn) 1390 dadd(fn)
1394 elif t.p2_info: 1391 elif t.p2_info:
1395 madd(fn) 1392 madd(fn)
1410 # issue6456: Size returned may be longer due to 1407 # issue6456: Size returned may be longer due to
1411 # encryption on EXT-4 fscrypt, undecided. 1408 # encryption on EXT-4 fscrypt, undecided.
1412 ladd(fn) 1409 ladd(fn)
1413 else: 1410 else:
1414 madd(fn) 1411 madd(fn)
1415 elif ( 1412 elif not t.mtime_likely_equal_to(timestamp.mtime_of(st)):
1416 time != st[stat.ST_MTIME]
1417 and time != st[stat.ST_MTIME] & _rangemask
1418 ):
1419 ladd(fn) 1413 ladd(fn)
1420 elif st[stat.ST_MTIME] == lastnormaltime: 1414 elif timestamp.mtime_of(st) == lastnormaltime:
1421 # fn may have just been marked as normal and it may have 1415 # fn may have just been marked as normal and it may have
1422 # changed in the same second without changing its size. 1416 # changed in the same second without changing its size.
1423 # This can happen if we quickly do multiple commits. 1417 # This can happen if we quickly do multiple commits.
1424 # Force lookup, so we don't miss such a racy file change. 1418 # Force lookup, so we don't miss such a racy file change.
1425 ladd(fn) 1419 ladd(fn)