##// 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.
485 482
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 483
493 - To be a directory
494 - With the modification time given in `mtime`
495 - That time was already strictly in the past when observed,
484 It means the `mtime` was already strictly in the past when observed,
496 485 meaning that later changes cannot happen in the same clock tick
497 486 and must cause a different modification time
498 487 (unless the system clock jumps back and we get unlucky,
499 488 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.
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 339 Ok(Some(self.mtime.try_into()?))
341 }
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