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