Mercurial > hg
comparison mercurial/pure/parsers.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 | b874e8d81a98 |
children | 9205d9be8b41 |
comparison
equal
deleted
inserted
replaced
48259:84f6b0c41b90 | 48260:269ff8978086 |
---|---|
97 _wc_tracked = attr.ib() | 97 _wc_tracked = attr.ib() |
98 _p1_tracked = attr.ib() | 98 _p1_tracked = attr.ib() |
99 _p2_info = attr.ib() | 99 _p2_info = attr.ib() |
100 _mode = attr.ib() | 100 _mode = attr.ib() |
101 _size = attr.ib() | 101 _size = attr.ib() |
102 _mtime = attr.ib() | 102 _mtime_s = attr.ib() |
103 _mtime_ns = attr.ib() | |
103 _fallback_exec = attr.ib() | 104 _fallback_exec = attr.ib() |
104 _fallback_symlink = attr.ib() | 105 _fallback_symlink = attr.ib() |
105 | 106 |
106 def __init__( | 107 def __init__( |
107 self, | 108 self, |
121 self._fallback_exec = fallback_exec | 122 self._fallback_exec = fallback_exec |
122 self._fallback_symlink = fallback_symlink | 123 self._fallback_symlink = fallback_symlink |
123 | 124 |
124 self._mode = None | 125 self._mode = None |
125 self._size = None | 126 self._size = None |
126 self._mtime = None | 127 self._mtime_s = None |
128 self._mtime_ns = None | |
127 if parentfiledata is None: | 129 if parentfiledata is None: |
128 has_meaningful_mtime = False | 130 has_meaningful_mtime = False |
129 has_meaningful_data = False | 131 has_meaningful_data = False |
130 if has_meaningful_data: | 132 if has_meaningful_data: |
131 self._mode = parentfiledata[0] | 133 self._mode = parentfiledata[0] |
132 self._size = parentfiledata[1] | 134 self._size = parentfiledata[1] |
133 if has_meaningful_mtime: | 135 if has_meaningful_mtime: |
134 self._mtime = parentfiledata[2] | 136 self._mtime_s, self._mtime_ns = parentfiledata[2] |
135 | 137 |
136 @classmethod | 138 @classmethod |
137 def from_v2_data(cls, flags, size, mtime): | 139 def from_v2_data(cls, flags, size, mtime_s, mtime_ns): |
138 """Build a new DirstateItem object from V2 data""" | 140 """Build a new DirstateItem object from V2 data""" |
139 has_mode_size = bool(flags & DIRSTATE_V2_HAS_MODE_AND_SIZE) | 141 has_mode_size = bool(flags & DIRSTATE_V2_HAS_MODE_AND_SIZE) |
140 has_meaningful_mtime = bool(flags & DIRSTATE_V2_HAS_FILE_MTIME) | 142 has_meaningful_mtime = bool(flags & DIRSTATE_V2_HAS_FILE_MTIME) |
141 mode = None | 143 mode = None |
142 | 144 |
168 wc_tracked=bool(flags & DIRSTATE_V2_WDIR_TRACKED), | 170 wc_tracked=bool(flags & DIRSTATE_V2_WDIR_TRACKED), |
169 p1_tracked=bool(flags & DIRSTATE_V2_P1_TRACKED), | 171 p1_tracked=bool(flags & DIRSTATE_V2_P1_TRACKED), |
170 p2_info=bool(flags & DIRSTATE_V2_P2_INFO), | 172 p2_info=bool(flags & DIRSTATE_V2_P2_INFO), |
171 has_meaningful_data=has_mode_size, | 173 has_meaningful_data=has_mode_size, |
172 has_meaningful_mtime=has_meaningful_mtime, | 174 has_meaningful_mtime=has_meaningful_mtime, |
173 parentfiledata=(mode, size, mtime), | 175 parentfiledata=(mode, size, (mtime_s, mtime_ns)), |
174 fallback_exec=fallback_exec, | 176 fallback_exec=fallback_exec, |
175 fallback_symlink=fallback_symlink, | 177 fallback_symlink=fallback_symlink, |
176 ) | 178 ) |
177 | 179 |
178 @classmethod | 180 @classmethod |
205 elif mtime == AMBIGUOUS_TIME: | 207 elif mtime == AMBIGUOUS_TIME: |
206 return cls( | 208 return cls( |
207 wc_tracked=True, | 209 wc_tracked=True, |
208 p1_tracked=True, | 210 p1_tracked=True, |
209 has_meaningful_mtime=False, | 211 has_meaningful_mtime=False, |
210 parentfiledata=(mode, size, 42), | 212 parentfiledata=(mode, size, (42, 0)), |
211 ) | 213 ) |
212 else: | 214 else: |
213 return cls( | 215 return cls( |
214 wc_tracked=True, | 216 wc_tracked=True, |
215 p1_tracked=True, | 217 p1_tracked=True, |
216 parentfiledata=(mode, size, mtime), | 218 parentfiledata=(mode, size, (mtime, 0)), |
217 ) | 219 ) |
218 else: | 220 else: |
219 raise RuntimeError(b'unknown state: %s' % state) | 221 raise RuntimeError(b'unknown state: %s' % state) |
220 | 222 |
221 def set_possibly_dirty(self): | 223 def set_possibly_dirty(self): |
222 """Mark a file as "possibly dirty" | 224 """Mark a file as "possibly dirty" |
223 | 225 |
224 This means the next status call will have to actually check its content | 226 This means the next status call will have to actually check its content |
225 to make sure it is correct. | 227 to make sure it is correct. |
226 """ | 228 """ |
227 self._mtime = None | 229 self._mtime_s = None |
230 self._mtime_ns = None | |
228 | 231 |
229 def set_clean(self, mode, size, mtime): | 232 def set_clean(self, mode, size, mtime): |
230 """mark a file as "clean" cancelling potential "possibly dirty call" | 233 """mark a file as "clean" cancelling potential "possibly dirty call" |
231 | 234 |
232 Note: this function is a descendant of `dirstate.normal` and is | 235 Note: this function is a descendant of `dirstate.normal` and is |
236 """ | 239 """ |
237 self._wc_tracked = True | 240 self._wc_tracked = True |
238 self._p1_tracked = True | 241 self._p1_tracked = True |
239 self._mode = mode | 242 self._mode = mode |
240 self._size = size | 243 self._size = size |
241 self._mtime = mtime | 244 self._mtime_s, self._mtime_ns = mtime |
242 | 245 |
243 def set_tracked(self): | 246 def set_tracked(self): |
244 """mark a file as tracked in the working copy | 247 """mark a file as tracked in the working copy |
245 | 248 |
246 This will ultimately be called by command like `hg add`. | 249 This will ultimately be called by command like `hg add`. |
248 self._wc_tracked = True | 251 self._wc_tracked = True |
249 # `set_tracked` is replacing various `normallookup` call. So we mark | 252 # `set_tracked` is replacing various `normallookup` call. So we mark |
250 # the files as needing lookup | 253 # the files as needing lookup |
251 # | 254 # |
252 # Consider dropping this in the future in favor of something less broad. | 255 # Consider dropping this in the future in favor of something less broad. |
253 self._mtime = None | 256 self._mtime_s = None |
257 self._mtime_ns = None | |
254 | 258 |
255 def set_untracked(self): | 259 def set_untracked(self): |
256 """mark a file as untracked in the working copy | 260 """mark a file as untracked in the working copy |
257 | 261 |
258 This will ultimately be called by command like `hg remove`. | 262 This will ultimately be called by command like `hg remove`. |
259 """ | 263 """ |
260 self._wc_tracked = False | 264 self._wc_tracked = False |
261 self._mode = None | 265 self._mode = None |
262 self._size = None | 266 self._size = None |
263 self._mtime = None | 267 self._mtime_s = None |
268 self._mtime_ns = None | |
264 | 269 |
265 def drop_merge_data(self): | 270 def drop_merge_data(self): |
266 """remove all "merge-only" from a DirstateItem | 271 """remove all "merge-only" from a DirstateItem |
267 | 272 |
268 This is to be call by the dirstatemap code when the second parent is dropped | 273 This is to be call by the dirstatemap code when the second parent is dropped |
269 """ | 274 """ |
270 if self._p2_info: | 275 if self._p2_info: |
271 self._p2_info = False | 276 self._p2_info = False |
272 self._mode = None | 277 self._mode = None |
273 self._size = None | 278 self._size = None |
274 self._mtime = None | 279 self._mtime_s = None |
280 self._mtime_ns = None | |
275 | 281 |
276 @property | 282 @property |
277 def mode(self): | 283 def mode(self): |
278 return self.v1_mode() | 284 return self.v1_mode() |
279 | 285 |
282 return self.v1_size() | 288 return self.v1_size() |
283 | 289 |
284 @property | 290 @property |
285 def mtime(self): | 291 def mtime(self): |
286 return self.v1_mtime() | 292 return self.v1_mtime() |
293 | |
294 def mtime_likely_equal_to(self, other_mtime): | |
295 self_sec = self._mtime_s | |
296 if self_sec is None: | |
297 return False | |
298 self_ns = self._mtime_ns | |
299 other_sec, other_ns = other_mtime | |
300 return self_sec == other_sec and self_ns == other_ns | |
287 | 301 |
288 @property | 302 @property |
289 def state(self): | 303 def state(self): |
290 """ | 304 """ |
291 States are: | 305 States are: |
438 flags |= DIRSTATE_V2_HAS_MODE_AND_SIZE | 452 flags |= DIRSTATE_V2_HAS_MODE_AND_SIZE |
439 if self.mode & stat.S_IXUSR: | 453 if self.mode & stat.S_IXUSR: |
440 flags |= DIRSTATE_V2_MODE_EXEC_PERM | 454 flags |= DIRSTATE_V2_MODE_EXEC_PERM |
441 if stat.S_ISLNK(self.mode): | 455 if stat.S_ISLNK(self.mode): |
442 flags |= DIRSTATE_V2_MODE_IS_SYMLINK | 456 flags |= DIRSTATE_V2_MODE_IS_SYMLINK |
443 if self._mtime is not None: | 457 if self._mtime_s is not None: |
444 flags |= DIRSTATE_V2_HAS_FILE_MTIME | 458 flags |= DIRSTATE_V2_HAS_FILE_MTIME |
445 | 459 |
446 if self._fallback_exec is not None: | 460 if self._fallback_exec is not None: |
447 flags |= DIRSTATE_V2_HAS_FALLBACK_EXEC | 461 flags |= DIRSTATE_V2_HAS_FALLBACK_EXEC |
448 if self._fallback_exec: | 462 if self._fallback_exec: |
454 flags |= DIRSTATE_V2_FALLBACK_SYMLINK | 468 flags |= DIRSTATE_V2_FALLBACK_SYMLINK |
455 | 469 |
456 # Note: we do not need to do anything regarding | 470 # Note: we do not need to do anything regarding |
457 # DIRSTATE_V2_ALL_UNKNOWN_RECORDED and DIRSTATE_V2_ALL_IGNORED_RECORDED | 471 # DIRSTATE_V2_ALL_UNKNOWN_RECORDED and DIRSTATE_V2_ALL_IGNORED_RECORDED |
458 # since we never set _DIRSTATE_V2_HAS_DIRCTORY_MTIME | 472 # since we never set _DIRSTATE_V2_HAS_DIRCTORY_MTIME |
459 return (flags, self._size or 0, self._mtime or 0) | 473 return (flags, self._size or 0, self._mtime_s or 0, self._mtime_ns or 0) |
460 | 474 |
461 def v1_state(self): | 475 def v1_state(self): |
462 """return a "state" suitable for v1 serialization""" | 476 """return a "state" suitable for v1 serialization""" |
463 if not self.any_tracked: | 477 if not self.any_tracked: |
464 # the object has no state to record, this is -currently- | 478 # the object has no state to record, this is -currently- |
502 # the object has no state to record, this is -currently- | 516 # the object has no state to record, this is -currently- |
503 # unsupported | 517 # unsupported |
504 raise RuntimeError('untracked item') | 518 raise RuntimeError('untracked item') |
505 elif self.removed: | 519 elif self.removed: |
506 return 0 | 520 return 0 |
507 elif self._mtime is None: | 521 elif self._mtime_s is None: |
508 return AMBIGUOUS_TIME | 522 return AMBIGUOUS_TIME |
509 elif self._p2_info: | 523 elif self._p2_info: |
510 return AMBIGUOUS_TIME | 524 return AMBIGUOUS_TIME |
511 elif not self._p1_tracked: | 525 elif not self._p1_tracked: |
512 return AMBIGUOUS_TIME | 526 return AMBIGUOUS_TIME |
513 else: | 527 else: |
514 return self._mtime | 528 return self._mtime_s |
515 | 529 |
516 def need_delay(self, now): | 530 def need_delay(self, now): |
517 """True if the stored mtime would be ambiguous with the current time""" | 531 """True if the stored mtime would be ambiguous with the current time""" |
518 return self.v1_state() == b'n' and self.v1_mtime() == now | 532 return self.v1_state() == b'n' and self._mtime_s == now[0] |
519 | 533 |
520 | 534 |
521 def gettype(q): | 535 def gettype(q): |
522 return int(q & 0xFFFF) | 536 return int(q & 0xFFFF) |
523 | 537 |
881 dmap[f] = DirstateItem.from_v1_data(*e[:4]) | 895 dmap[f] = DirstateItem.from_v1_data(*e[:4]) |
882 return parents | 896 return parents |
883 | 897 |
884 | 898 |
885 def pack_dirstate(dmap, copymap, pl, now): | 899 def pack_dirstate(dmap, copymap, pl, now): |
886 now = int(now) | |
887 cs = stringio() | 900 cs = stringio() |
888 write = cs.write | 901 write = cs.write |
889 write(b"".join(pl)) | 902 write(b"".join(pl)) |
890 for f, e in pycompat.iteritems(dmap): | 903 for f, e in pycompat.iteritems(dmap): |
891 if e.need_delay(now): | 904 if e.need_delay(now): |