##// END OF EJS Templates
status: keep second-ambiguous mtimes during fixup...
marmoute -
r49232:ca42667c default
parent child Browse files
Show More
@@ -1,117 +1,124 b''
1 # Copyright Mercurial Contributors
1 # Copyright Mercurial Contributors
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 from __future__ import absolute_import
6 from __future__ import absolute_import
7
7
8 import functools
8 import functools
9 import os
9 import os
10 import stat
10 import stat
11
11
12 from .. import error
12 from .. import error
13
13
14
14
15 rangemask = 0x7FFFFFFF
15 rangemask = 0x7FFFFFFF
16
16
17
17
18 @functools.total_ordering
18 @functools.total_ordering
19 class timestamp(tuple):
19 class timestamp(tuple):
20 """
20 """
21 A Unix timestamp with optional nanoseconds precision,
21 A Unix timestamp with optional nanoseconds precision,
22 modulo 2**31 seconds.
22 modulo 2**31 seconds.
23
23
24 A 2-tuple containing:
24 A 2-tuple containing:
25
25
26 `truncated_seconds`: seconds since the Unix epoch,
26 `truncated_seconds`: seconds since the Unix epoch,
27 truncated to its lower 31 bits
27 truncated to its lower 31 bits
28
28
29 `subsecond_nanoseconds`: number of nanoseconds since `truncated_seconds`.
29 `subsecond_nanoseconds`: number of nanoseconds since `truncated_seconds`.
30 When this is zero, the sub-second precision is considered unknown.
30 When this is zero, the sub-second precision is considered unknown.
31 """
31 """
32
32
33 def __new__(cls, value):
33 def __new__(cls, value):
34 truncated_seconds, subsec_nanos, second_ambiguous = value
34 truncated_seconds, subsec_nanos, second_ambiguous = value
35 value = (truncated_seconds & rangemask, subsec_nanos, second_ambiguous)
35 value = (truncated_seconds & rangemask, subsec_nanos, second_ambiguous)
36 return super(timestamp, cls).__new__(cls, value)
36 return super(timestamp, cls).__new__(cls, value)
37
37
38 def __eq__(self, other):
38 def __eq__(self, other):
39 raise error.ProgrammingError(
39 raise error.ProgrammingError(
40 'timestamp should never be compared directly'
40 'timestamp should never be compared directly'
41 )
41 )
42
42
43 def __gt__(self, other):
43 def __gt__(self, other):
44 raise error.ProgrammingError(
44 raise error.ProgrammingError(
45 'timestamp should never be compared directly'
45 'timestamp should never be compared directly'
46 )
46 )
47
47
48
48
49 def get_fs_now(vfs):
49 def get_fs_now(vfs):
50 """return a timestamp for "now" in the current vfs
50 """return a timestamp for "now" in the current vfs
51
51
52 This will raise an exception if no temporary files could be created.
52 This will raise an exception if no temporary files could be created.
53 """
53 """
54 tmpfd, tmpname = vfs.mkstemp()
54 tmpfd, tmpname = vfs.mkstemp()
55 try:
55 try:
56 return mtime_of(os.fstat(tmpfd))
56 return mtime_of(os.fstat(tmpfd))
57 finally:
57 finally:
58 os.close(tmpfd)
58 os.close(tmpfd)
59 vfs.unlink(tmpname)
59 vfs.unlink(tmpname)
60
60
61
61
62 def zero():
62 def zero():
63 """
63 """
64 Returns the `timestamp` at the Unix epoch.
64 Returns the `timestamp` at the Unix epoch.
65 """
65 """
66 return tuple.__new__(timestamp, (0, 0))
66 return tuple.__new__(timestamp, (0, 0))
67
67
68
68
69 def mtime_of(stat_result):
69 def mtime_of(stat_result):
70 """
70 """
71 Takes an `os.stat_result`-like object and returns a `timestamp` object
71 Takes an `os.stat_result`-like object and returns a `timestamp` object
72 for its modification time.
72 for its modification time.
73 """
73 """
74 try:
74 try:
75 # TODO: add this attribute to `osutil.stat` objects,
75 # TODO: add this attribute to `osutil.stat` objects,
76 # see `mercurial/cext/osutil.c`.
76 # see `mercurial/cext/osutil.c`.
77 #
77 #
78 # This attribute is also not available on Python 2.
78 # This attribute is also not available on Python 2.
79 nanos = stat_result.st_mtime_ns
79 nanos = stat_result.st_mtime_ns
80 except AttributeError:
80 except AttributeError:
81 # https://docs.python.org/2/library/os.html#os.stat_float_times
81 # https://docs.python.org/2/library/os.html#os.stat_float_times
82 # "For compatibility with older Python versions,
82 # "For compatibility with older Python versions,
83 # accessing stat_result as a tuple always returns integers."
83 # accessing stat_result as a tuple always returns integers."
84 secs = stat_result[stat.ST_MTIME]
84 secs = stat_result[stat.ST_MTIME]
85
85
86 subsec_nanos = 0
86 subsec_nanos = 0
87 else:
87 else:
88 billion = int(1e9)
88 billion = int(1e9)
89 secs = nanos // billion
89 secs = nanos // billion
90 subsec_nanos = nanos % billion
90 subsec_nanos = nanos % billion
91
91
92 return timestamp((secs, subsec_nanos, False))
92 return timestamp((secs, subsec_nanos, False))
93
93
94
94
95 def reliable_mtime_of(stat_result, present_mtime):
95 def reliable_mtime_of(stat_result, present_mtime):
96 """same as `mtime_of`, but return None if the date might be ambiguous
96 """same as `mtime_of`, but return None if the date might be ambiguous
97
97
98 A modification time is reliable if it is older than "present_time" (or
98 A modification time is reliable if it is older than "present_time" (or
99 sufficiently in the futur).
99 sufficiently in the futur).
100
100
101 Otherwise a concurrent modification might happens with the same mtime.
101 Otherwise a concurrent modification might happens with the same mtime.
102 """
102 """
103 file_mtime = mtime_of(stat_result)
103 file_mtime = mtime_of(stat_result)
104 file_second = file_mtime[0]
104 file_second = file_mtime[0]
105 file_ns = file_mtime[1]
105 boundary_second = present_mtime[0]
106 boundary_second = present_mtime[0]
107 boundary_ns = present_mtime[1]
106 # If the mtime of the ambiguous file is younger (or equal) to the starting
108 # If the mtime of the ambiguous file is younger (or equal) to the starting
107 # point of the `status` walk, we cannot garantee that another, racy, write
109 # point of the `status` walk, we cannot garantee that another, racy, write
108 # will not happen right after with the same mtime and we cannot cache the
110 # will not happen right after with the same mtime and we cannot cache the
109 # information.
111 # information.
110 #
112 #
111 # However is the mtime is far away in the future, this is likely some
113 # However if the mtime is far away in the future, this is likely some
112 # mismatch between the current clock and previous file system operation. So
114 # mismatch between the current clock and previous file system operation. So
113 # mtime more than one days in the future are considered fine.
115 # mtime more than one days in the future are considered fine.
114 if boundary_second <= file_second < (3600 * 24 + boundary_second):
116 if boundary_second == file_second:
117 if file_ns and boundary_ns:
118 if file_ns < boundary_ns:
119 return timestamp((file_second, file_ns, True))
120 return None
121 elif boundary_second < file_second < (3600 * 24 + boundary_second):
115 return None
122 return None
116 else:
123 else:
117 return file_mtime
124 return file_mtime
General Comments 0
You need to be logged in to leave comments. Login now