##// END OF EJS Templates
dirstate-v2: adjust the meaning of directory flags...
marmoute -
r49083:bb240915 default
parent child Browse files
Show More
@@ -129,7 +129,7 b' static PyObject *dirstate_item_new(PyTyp'
129 129 t->size = 0;
130 130 }
131 131 if (has_meaningful_mtime) {
132 t->flags |= dirstate_flag_has_file_mtime;
132 t->flags |= dirstate_flag_has_mtime;
133 133 t->mtime_s = mtime_s;
134 134 t->mtime_ns = mtime_ns;
135 135 } else {
@@ -246,7 +246,7 b' static inline int dirstate_item_c_v1_mti'
246 246 {
247 247 if (dirstate_item_c_removed(self)) {
248 248 return 0;
249 } else if (!(self->flags & dirstate_flag_has_file_mtime) ||
249 } else if (!(self->flags & dirstate_flag_has_mtime) ||
250 250 !(self->flags & dirstate_flag_p1_tracked) ||
251 251 !(self->flags & dirstate_flag_wc_tracked) ||
252 252 (self->flags & dirstate_flag_p2_info)) {
@@ -318,7 +318,7 b' static PyObject *dirstate_item_mtime_lik'
318 318 if (!PyArg_ParseTuple(other, "ii", &other_s, &other_ns)) {
319 319 return NULL;
320 320 }
321 if ((self->flags & dirstate_flag_has_file_mtime) &&
321 if ((self->flags & dirstate_flag_has_mtime) &&
322 322 self->mtime_s == other_s &&
323 323 (self->mtime_ns == other_ns || self->mtime_ns == 0 ||
324 324 other_ns == 0)) {
@@ -375,7 +375,7 b' dirstate_item_from_v1_data(char state, i'
375 375 t->flags = (dirstate_flag_wc_tracked |
376 376 dirstate_flag_p1_tracked |
377 377 dirstate_flag_has_meaningful_data |
378 dirstate_flag_has_file_mtime);
378 dirstate_flag_has_mtime);
379 379 t->mode = mode;
380 380 t->size = size;
381 381 t->mtime_s = mtime;
@@ -420,7 +420,7 b' static PyObject *dirstate_item_from_v2_m'
420 420 if (t->flags & dirstate_flag_expected_state_is_modified) {
421 421 t->flags &= ~(dirstate_flag_expected_state_is_modified |
422 422 dirstate_flag_has_meaningful_data |
423 dirstate_flag_has_file_mtime);
423 dirstate_flag_has_mtime);
424 424 }
425 425 if (t->flags & dirstate_flag_mtime_second_ambiguous) {
426 426 /* The current code is not able to do the more subtle comparison
@@ -428,7 +428,7 b' static PyObject *dirstate_item_from_v2_m'
428 428 * mtime */
429 429 t->flags &= ~(dirstate_flag_mtime_second_ambiguous |
430 430 dirstate_flag_has_meaningful_data |
431 dirstate_flag_has_file_mtime);
431 dirstate_flag_has_mtime);
432 432 }
433 433 t->mode = 0;
434 434 if (t->flags & dirstate_flag_has_meaningful_data) {
@@ -450,7 +450,7 b' static PyObject *dirstate_item_from_v2_m'
450 450 to make sure it is correct. */
451 451 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
452 452 {
453 self->flags &= ~dirstate_flag_has_file_mtime;
453 self->flags &= ~dirstate_flag_has_mtime;
454 454 Py_RETURN_NONE;
455 455 }
456 456
@@ -465,7 +465,7 b' static PyObject *dirstate_item_set_clean'
465 465 }
466 466 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
467 467 dirstate_flag_has_meaningful_data |
468 dirstate_flag_has_file_mtime;
468 dirstate_flag_has_mtime;
469 469 self->mode = mode;
470 470 self->size = size;
471 471 self->mtime_s = mtime_s;
@@ -476,7 +476,7 b' static PyObject *dirstate_item_set_clean'
476 476 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
477 477 {
478 478 self->flags |= dirstate_flag_wc_tracked;
479 self->flags &= ~dirstate_flag_has_file_mtime;
479 self->flags &= ~dirstate_flag_has_mtime;
480 480 Py_RETURN_NONE;
481 481 }
482 482
@@ -495,7 +495,7 b' static PyObject *dirstate_item_drop_merg'
495 495 if (self->flags & dirstate_flag_p2_info) {
496 496 self->flags &= ~(dirstate_flag_p2_info |
497 497 dirstate_flag_has_meaningful_data |
498 dirstate_flag_has_file_mtime);
498 dirstate_flag_has_mtime);
499 499 self->mode = 0;
500 500 self->size = 0;
501 501 self->mtime_s = 0;
@@ -36,8 +36,8 b' static const int dirstate_flag_wc_tracke'
36 36 static const int dirstate_flag_p1_tracked = 1 << 1;
37 37 static const int dirstate_flag_p2_info = 1 << 2;
38 38 static const int dirstate_flag_has_meaningful_data = 1 << 3;
39 static const int dirstate_flag_has_file_mtime = 1 << 4;
40 static const int dirstate_flag_has_directory_mtime = 1 << 5;
39 static const int dirstate_flag_has_mtime = 1 << 4;
40 static const int dirstate_flag_directory = 1 << 5;
41 41 static const int dirstate_flag_mode_exec_perm = 1 << 6;
42 42 static const int dirstate_flag_mode_is_symlink = 1 << 7;
43 43 static const int dirstate_flag_expected_state_is_modified = 1 << 8;
@@ -56,6 +56,9 b" NODE = struct.Struct('>LHHLHLLLLHlll')"
56 56 assert TREE_METADATA_SIZE == TREE_METADATA.size
57 57 assert NODE_SIZE == NODE.size
58 58
59 # match constant in mercurial/pure/parsers.py
60 DIRSTATE_V2_DIRECTORY = 1 << 5
61
59 62
60 63 def parse_dirstate(map, copy_map, data, tree_metadata):
61 64 """parse a full v2-dirstate from a binary data into dictionnaries:
@@ -83,7 +86,7 b' def parse_nodes(map, copy_map, data, sta'
83 86 This is used by parse_dirstate to recursively fill `map` and `copy_map`.
84 87
85 88 All directory specific information is ignored and do not need any
86 processing (HAS_DIRECTORY_MTIME, ALL_UNKNOWN_RECORDED, ALL_IGNORED_RECORDED)
89 processing (DIRECTORY, ALL_UNKNOWN_RECORDED, ALL_IGNORED_RECORDED)
87 90 """
88 91 for i in range(len):
89 92 node_start = start + NODE_SIZE * i
@@ -150,7 +153,7 b' class Node(object):'
150 153 flags, size, mtime_s, mtime_ns = entry.v2_data()
151 154 else:
152 155 # There are no mtime-cached directories in the Python implementation
153 flags = 0
156 flags = DIRSTATE_V2_DIRECTORY
154 157 size = 0
155 158 mtime_s = 0
156 159 mtime_ns = 0
@@ -379,8 +379,8 b' Node components are:'
379 379 P1_TRACKED = 1 << 1
380 380 P2_INFO = 1 << 2
381 381 HAS_MODE_AND_SIZE = 1 << 3
382 HAS_FILE_MTIME = 1 << 4
383 HAS_DIRECTORY_MTIME = 1 << 5
382 HAS_MTIME = 1 << 4
383 DIRECTORY = 1 << 5
384 384 MODE_EXEC_PERM = 1 << 6
385 385 MODE_IS_SYMLINK = 1 << 7
386 386 EXPECTED_STATE_IS_MODIFIED = 1 << 8
@@ -477,37 +477,45 b' by enabling it to skip `readdir` in more'
477 477 If this is unset the expected size, permission, and file type are unknown.
478 478 The `size` field is unused (set to zero).
479 479
480 `HAS_FILE_MTIME`
481 Must be unset for untracked nodes.
482 If this and `HAS_DIRECTORY_MTIME` are both unset,
483 the `mtime` field is unused (set to zero).
484 If this is set, `mtime` is the expected modification time.
480 `HAS_MTIME`
481 The nodes contains a "valid" last modification time in the `mtime` field.
482
485 483
486 `HAS_DIRECTORY_MTIME`
487 Must be unset for file tracked anywhere.
488 If this and `HAS_DIRECTORY_MTIME` are both unset,
489 the `mtime` field is unused (set to zero).
490 If this is set, at some point,
491 this path in the working directory was observed:
492
493 - To be a directory
494 - With the modification time given in `mtime`
495 - That time was already strictly in the past when observed,
496 meaning that later changes cannot happen in the same clock tick
497 and must cause a different modification time
498 (unless the system clock jumps back and we get unlucky,
499 which is not impossible but deemed unlikely enough).
500 - All direct children of this directory
501 (as returned by `std::fs::read_dir`)
502 either have a corresponding dirstate node,
503 or are ignored by ignore patterns whose hash is in tree metadata.
484 It means the `mtime` was already strictly in the past when observed,
485 meaning that later changes cannot happen in the same clock tick
486 and must cause a different modification time
487 (unless the system clock jumps back and we get unlucky,
488 which is not impossible but deemed unlikely enough).
504 489
505 490 This means that if `std::fs::symlink_metadata` later reports
506 491 the same modification time
507 492 and ignored patterns haven’t changed,
508 a run of status that is not listing ignored files
509 can skip calling `std::fs::read_dir` again for this directory,
493 we can assume the node to be unchanged on disk.
494
495 The `mtime` field can then be used to skip more expensive lookup when
496 checking the status of "tracked" nodes.
497
498 It can also be set for node where `DIRECTORY` is set.
499 See `DIRECTORY` documentation for details.
500
501 `DIRECTORY`
502 When set, this entry will match a directory that exists or existed on the
503 file system.
504
505 * When `HAS_MTIME` is set a directory has been seen on the file system and
506 `mtime` matches its last modificiation time. However, `HAS_MTIME` not being set
507 does not indicate the lack of directory on the file system.
508
509 * When not tracked anywhere, this node does not represent an ignored or
510 unknown file on disk.
511
512 If `HAS_MTIME` is set
513 and `mtime` matches the last modification time of the directory on disk,
514 the directory is unchanged
515 and we can skip calling `std::fs::read_dir` again for this directory,
510 516 and iterate child dirstate nodes instead.
517 (as long as `ALL_UNKNOWN_RECORDED` and `ALL_IGNORED_RECORDED` are taken
518 into account)
511 519
512 520 `MODE_EXEC_PERM`
513 521 Must be unset if `HAS_MODE_AND_SIZE` is unset.
@@ -525,7 +533,7 b' by enabling it to skip `readdir` in more'
525 533 Must be unset for untracked nodes.
526 534 For:
527 535 - a file tracked anywhere
528 - that has expected metadata (`HAS_MODE_AND_SIZE` and `HAS_FILE_MTIME`)
536 - that has expected metadata (`HAS_MODE_AND_SIZE` and `HAS_MTIME`)
529 537 - if that metadata matches
530 538 metadata found in the working directory with `stat`
531 539 This bit indicates the status of the file.
@@ -541,7 +549,7 b' by enabling it to skip `readdir` in more'
541 549 `ALL_UNKNOWN_RECORDED`
542 550 If set, all "unknown" children existing on disk (at the time of the last
543 551 status) have been recorded and the `mtime` associated with
544 `HAS_DIRECTORY_MTIME` can be used for optimization even when "unknown" file
552 `DIRECTORY` can be used for optimization even when "unknown" file
545 553 are listed.
546 554
547 555 Note that the amount recorded "unknown" children can still be zero if None
@@ -554,7 +562,7 b' by enabling it to skip `readdir` in more'
554 562 `ALL_IGNORED_RECORDED`
555 563 If set, all "ignored" children existing on disk (at the time of the last
556 564 status) have been recorded and the `mtime` associated with
557 `HAS_DIRECTORY_MTIME` can be used for optimization even when "ignored" file
565 `DIRECTORY` can be used for optimization even when "ignored" file
558 566 are listed.
559 567
560 568 Note that the amount recorded "ignored" children can still be zero if None
@@ -49,8 +49,8 b' DIRSTATE_V2_WDIR_TRACKED = 1 << 0'
49 49 DIRSTATE_V2_P1_TRACKED = 1 << 1
50 50 DIRSTATE_V2_P2_INFO = 1 << 2
51 51 DIRSTATE_V2_HAS_MODE_AND_SIZE = 1 << 3
52 DIRSTATE_V2_HAS_FILE_MTIME = 1 << 4
53 _DIRSTATE_V2_HAS_DIRCTORY_MTIME = 1 << 5 # Unused when Rust is not available
52 DIRSTATE_V2_HAS_MTIME = 1 << 4
53 DIRSTATE_V2_DIRECTORY = 1 << 5
54 54 DIRSTATE_V2_MODE_EXEC_PERM = 1 << 6
55 55 DIRSTATE_V2_MODE_IS_SYMLINK = 1 << 7
56 56 DIRSTATE_V2_EXPECTED_STATE_IS_MODIFIED = 1 << 8
@@ -140,7 +140,7 b' class DirstateItem(object):'
140 140 def from_v2_data(cls, flags, size, mtime_s, mtime_ns):
141 141 """Build a new DirstateItem object from V2 data"""
142 142 has_mode_size = bool(flags & DIRSTATE_V2_HAS_MODE_AND_SIZE)
143 has_meaningful_mtime = bool(flags & DIRSTATE_V2_HAS_FILE_MTIME)
143 has_meaningful_mtime = bool(flags & DIRSTATE_V2_HAS_MTIME)
144 144 if flags & DIRSTATE_V2_MTIME_SECOND_AMBIGUOUS:
145 145 # The current code is not able to do the more subtle comparison that the
146 146 # MTIME_SECOND_AMBIGUOUS requires. So we ignore the mtime
@@ -462,7 +462,7 b' class DirstateItem(object):'
462 462 if stat.S_ISLNK(self.mode):
463 463 flags |= DIRSTATE_V2_MODE_IS_SYMLINK
464 464 if self._mtime_s is not None:
465 flags |= DIRSTATE_V2_HAS_FILE_MTIME
465 flags |= DIRSTATE_V2_HAS_MTIME
466 466
467 467 if self._fallback_exec is not None:
468 468 flags |= DIRSTATE_V2_HAS_FALLBACK_EXEC
@@ -106,8 +106,8 b' bitflags! {'
106 106 const P1_TRACKED = 1 << 1;
107 107 const P2_INFO = 1 << 2;
108 108 const HAS_MODE_AND_SIZE = 1 << 3;
109 const HAS_FILE_MTIME = 1 << 4;
110 const HAS_DIRECTORY_MTIME = 1 << 5;
109 const HAS_MTIME = 1 << 4;
110 const DIRECTORY = 1 << 5;
111 111 const MODE_EXEC_PERM = 1 << 6;
112 112 const MODE_IS_SYMLINK = 1 << 7;
113 113 const EXPECTED_STATE_IS_MODIFIED = 1 << 8;
@@ -329,16 +329,14 b' impl Node {'
329 329 pub(super) fn cached_directory_mtime(
330 330 &self,
331 331 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
332 // For now we do not have code to handle ALL_UNKNOWN_RECORDED, so we
333 // ignore the mtime if the flag is set.
334 if self.flags().contains(Flags::HAS_DIRECTORY_MTIME)
332 // For now we do not have code to handle the absence of
333 // ALL_UNKNOWN_RECORDED, so we ignore the mtime if the flag is
334 // unset.
335 if self.flags().contains(Flags::DIRECTORY)
336 && self.flags().contains(Flags::HAS_MTIME)
335 337 && self.flags().contains(Flags::ALL_UNKNOWN_RECORDED)
336 338 {
337 if self.flags().contains(Flags::HAS_FILE_MTIME) {
338 Err(DirstateV2ParseError)
339 } else {
340 Ok(Some(self.mtime.try_into()?))
341 }
339 Ok(Some(self.mtime.try_into()?))
342 340 } else {
343 341 Ok(None)
344 342 }
@@ -370,7 +368,8 b' impl Node {'
370 368 } else {
371 369 None
372 370 };
373 let mtime = if self.flags().contains(Flags::HAS_FILE_MTIME)
371 let mtime = if self.flags().contains(Flags::HAS_MTIME)
372 && !self.flags().contains(Flags::DIRECTORY)
374 373 && !self.flags().contains(Flags::EXPECTED_STATE_IS_MODIFIED)
375 374 // The current code is not able to do the more subtle comparison that the
376 375 // MTIME_SECOND_AMBIGUOUS requires. So we ignore the mtime
@@ -453,7 +452,7 b' impl Node {'
453 452 0.into()
454 453 };
455 454 let mtime = if let Some(m) = mtime_opt {
456 flags.insert(Flags::HAS_FILE_MTIME);
455 flags.insert(Flags::HAS_MTIME);
457 456 m.into()
458 457 } else {
459 458 PackedTruncatedTimestamp::null()
@@ -631,13 +630,14 b" impl Writer<'_, '_> {"
631 630 // We never set ALL_IGNORED_RECORDED since we
632 631 // don't track that case
633 632 // currently.
634 Flags::HAS_DIRECTORY_MTIME
633 Flags::DIRECTORY
634 | Flags::HAS_MTIME
635 635 | Flags::ALL_UNKNOWN_RECORDED,
636 636 0.into(),
637 637 (*mtime).into(),
638 638 ),
639 639 dirstate_map::NodeData::None => (
640 Flags::empty(),
640 Flags::DIRECTORY,
641 641 0.into(),
642 642 PackedTruncatedTimestamp::null(),
643 643 ),
General Comments 0
You need to be logged in to leave comments. Login now