Show More
@@ -9,8 +9,8 b'' | |||||
9 | //! It is currently missing a lot of functionality compared to the Python one |
|
9 | //! It is currently missing a lot of functionality compared to the Python one | |
10 | //! and will only be triggered in narrow cases. |
|
10 | //! and will only be triggered in narrow cases. | |
11 |
|
11 | |||
|
12 | use crate::dirstate::entry::TruncatedTimestamp; | |||
12 | use crate::dirstate_tree::on_disk::DirstateV2ParseError; |
|
13 | use crate::dirstate_tree::on_disk::DirstateV2ParseError; | |
13 |
|
||||
14 | use crate::{ |
|
14 | use crate::{ | |
15 | utils::hg_path::{HgPath, HgPathError}, |
|
15 | utils::hg_path::{HgPath, HgPathError}, | |
16 | PatternError, |
|
16 | PatternError, | |
@@ -77,7 +77,7 b' pub struct StatusOptions {' | |||||
77 | pub struct DirstateStatus<'a> { |
|
77 | pub struct DirstateStatus<'a> { | |
78 | /// The current time at the start of the `status()` algorithm, as measured |
|
78 | /// The current time at the start of the `status()` algorithm, as measured | |
79 | /// and possibly truncated by the filesystem. |
|
79 | /// and possibly truncated by the filesystem. | |
80 |
pub filesystem_time_at_status_start: Option< |
|
80 | pub filesystem_time_at_status_start: Option<TruncatedTimestamp>, | |
81 |
|
81 | |||
82 | /// Tracked files whose contents have changed since the parent revision |
|
82 | /// Tracked files whose contents have changed since the parent revision | |
83 | pub modified: Vec<StatusPath<'a>>, |
|
83 | pub modified: Vec<StatusPath<'a>>, |
@@ -382,7 +382,7 b' impl Node {' | |||||
382 | && self.flags().contains(Flags::HAS_MTIME) |
|
382 | && self.flags().contains(Flags::HAS_MTIME) | |
383 | && self.flags().contains(Flags::ALL_UNKNOWN_RECORDED) |
|
383 | && self.flags().contains(Flags::ALL_UNKNOWN_RECORDED) | |
384 | { |
|
384 | { | |
385 |
Ok(Some(self.mtime |
|
385 | Ok(Some(self.mtime()?)) | |
386 | } else { |
|
386 | } else { | |
387 | Ok(None) |
|
387 | Ok(None) | |
388 | } |
|
388 | } | |
@@ -402,6 +402,14 b' impl Node {' | |||||
402 | file_type | permisions |
|
402 | file_type | permisions | |
403 | } |
|
403 | } | |
404 |
|
404 | |||
|
405 | fn mtime(&self) -> Result<TruncatedTimestamp, DirstateV2ParseError> { | |||
|
406 | let mut m: TruncatedTimestamp = self.mtime.try_into()?; | |||
|
407 | if self.flags().contains(Flags::MTIME_SECOND_AMBIGUOUS) { | |||
|
408 | m.second_ambiguous = true; | |||
|
409 | } | |||
|
410 | Ok(m) | |||
|
411 | } | |||
|
412 | ||||
405 | fn assume_entry(&self) -> Result<DirstateEntry, DirstateV2ParseError> { |
|
413 | fn assume_entry(&self) -> Result<DirstateEntry, DirstateV2ParseError> { | |
406 | // TODO: convert through raw bits instead? |
|
414 | // TODO: convert through raw bits instead? | |
407 | let wdir_tracked = self.flags().contains(Flags::WDIR_TRACKED); |
|
415 | let wdir_tracked = self.flags().contains(Flags::WDIR_TRACKED); | |
@@ -418,11 +426,7 b' impl Node {' | |||||
418 | && !self.flags().contains(Flags::DIRECTORY) |
|
426 | && !self.flags().contains(Flags::DIRECTORY) | |
419 | && !self.flags().contains(Flags::EXPECTED_STATE_IS_MODIFIED) |
|
427 | && !self.flags().contains(Flags::EXPECTED_STATE_IS_MODIFIED) | |
420 | { |
|
428 | { | |
421 | let mut m: TruncatedTimestamp = self.mtime.try_into()?; |
|
429 | Some(self.mtime()?) | |
422 | if self.flags().contains(Flags::MTIME_SECOND_AMBIGUOUS) { |
|
|||
423 | m.second_ambiguous = true; |
|
|||
424 | } |
|
|||
425 | Some(m) |
|
|||
426 | } else { |
|
430 | } else { | |
427 | None |
|
431 | None | |
428 | }; |
|
432 | }; | |
@@ -681,7 +685,7 b" impl Writer<'_, '_> {" | |||||
681 | dirstate_map::NodeData::Entry(entry) => { |
|
685 | dirstate_map::NodeData::Entry(entry) => { | |
682 | Node::from_dirstate_entry(entry) |
|
686 | Node::from_dirstate_entry(entry) | |
683 | } |
|
687 | } | |
684 |
dirstate_map::NodeData::CachedDirectory { mtime } => |
|
688 | dirstate_map::NodeData::CachedDirectory { mtime } => { | |
685 | // we currently never set a mtime if unknown file |
|
689 | // we currently never set a mtime if unknown file | |
686 | // are present. |
|
690 | // are present. | |
687 | // So if we have a mtime for a directory, we know |
|
691 | // So if we have a mtime for a directory, we know | |
@@ -692,12 +696,14 b" impl Writer<'_, '_> {" | |||||
692 | // We never set ALL_IGNORED_RECORDED since we |
|
696 | // We never set ALL_IGNORED_RECORDED since we | |
693 | // don't track that case |
|
697 | // don't track that case | |
694 | // currently. |
|
698 | // currently. | |
695 | Flags::DIRECTORY |
|
699 | let mut flags = Flags::DIRECTORY | |
696 | | Flags::HAS_MTIME |
|
700 | | Flags::HAS_MTIME | |
697 |
| Flags::ALL_UNKNOWN_RECORDED |
|
701 | | Flags::ALL_UNKNOWN_RECORDED; | |
698 |
|
|
702 | if mtime.second_ambiguous { | |
699 | (*mtime).into(), |
|
703 | flags.insert(Flags::MTIME_SECOND_AMBIGUOUS) | |
700 |
|
|
704 | } | |
|
705 | (flags, 0.into(), (*mtime).into()) | |||
|
706 | } | |||
701 | dirstate_map::NodeData::None => ( |
|
707 | dirstate_map::NodeData::None => ( | |
702 | Flags::DIRECTORY, |
|
708 | Flags::DIRECTORY, | |
703 | 0.into(), |
|
709 | 0.into(), |
@@ -63,7 +63,8 b" pub fn status<'tree, 'on_disk: 'tree>(" | |||||
63 | (Box::new(|&_| true), vec![], None) |
|
63 | (Box::new(|&_| true), vec![], None) | |
64 | }; |
|
64 | }; | |
65 |
|
65 | |||
66 |
let filesystem_time_at_status_start = |
|
66 | let filesystem_time_at_status_start = | |
|
67 | filesystem_now(&root_dir).ok().map(TruncatedTimestamp::from); | |||
67 | let outcome = DirstateStatus { |
|
68 | let outcome = DirstateStatus { | |
68 | filesystem_time_at_status_start, |
|
69 | filesystem_time_at_status_start, | |
69 | ..Default::default() |
|
70 | ..Default::default() | |
@@ -145,7 +146,7 b" struct StatusCommon<'a, 'tree, 'on_disk:" | |||||
145 |
|
146 | |||
146 | /// The current time at the start of the `status()` algorithm, as measured |
|
147 | /// The current time at the start of the `status()` algorithm, as measured | |
147 | /// and possibly truncated by the filesystem. |
|
148 | /// and possibly truncated by the filesystem. | |
148 |
filesystem_time_at_status_start: Option< |
|
149 | filesystem_time_at_status_start: Option<TruncatedTimestamp>, | |
149 | } |
|
150 | } | |
150 |
|
151 | |||
151 | enum Outcome { |
|
152 | enum Outcome { | |
@@ -472,25 +473,39 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
472 | directory_metadata: &std::fs::Metadata, |
|
473 | directory_metadata: &std::fs::Metadata, | |
473 | dirstate_node: NodeRef<'tree, 'on_disk>, |
|
474 | dirstate_node: NodeRef<'tree, 'on_disk>, | |
474 | ) -> Result<(), DirstateV2ParseError> { |
|
475 | ) -> Result<(), DirstateV2ParseError> { | |
475 | if children_all_have_dirstate_node_or_are_ignored { |
|
476 | if !children_all_have_dirstate_node_or_are_ignored { | |
|
477 | return Ok(()); | |||
|
478 | } | |||
476 |
|
|
479 | // All filesystem directory entries from `read_dir` have a | |
477 |
|
|
480 | // corresponding node in the dirstate, so we can reconstitute the | |
478 |
|
|
481 | // names of those entries without calling `read_dir` again. | |
479 | if let (Some(status_start), Ok(directory_mtime)) = ( |
|
482 | ||
480 | &self.filesystem_time_at_status_start, |
|
483 | // TODO: use let-else here and below when available: | |
481 | directory_metadata.modified(), |
|
484 | // https://github.com/rust-lang/rust/issues/87335 | |
482 | ) { |
|
485 | let status_start = if let Some(status_start) = | |
|
486 | &self.filesystem_time_at_status_start | |||
|
487 | { | |||
|
488 | status_start | |||
|
489 | } else { | |||
|
490 | return Ok(()); | |||
|
491 | }; | |||
|
492 | ||||
483 |
|
|
493 | // Although the Rust standard library’s `SystemTime` type | |
484 |
|
|
494 | // has nanosecond precision, the times reported for a | |
485 |
|
|
495 | // directory’s (or file’s) modified time may have lower | |
486 |
|
|
496 | // resolution based on the filesystem (for example ext3 | |
487 |
|
|
497 | // only stores integer seconds), kernel (see | |
488 |
|
|
498 | // https://stackoverflow.com/a/14393315/1162888), etc. | |
489 | if &directory_mtime >= status_start { |
|
499 | let directory_mtime = if let Ok(option) = | |
490 | // The directory was modified too recently, don’t cache its |
|
500 | TruncatedTimestamp::for_reliable_mtime_of( | |
491 | // `read_dir` results. |
|
501 | directory_metadata, | |
492 |
|
|
502 | status_start, | |
493 | // A timeline like this is possible: |
|
503 | ) { | |
|
504 | if let Some(directory_mtime) = option { | |||
|
505 | directory_mtime | |||
|
506 | } else { | |||
|
507 | // The directory was modified too recently, | |||
|
508 | // don’t cache its `read_dir` results. | |||
494 |
|
|
509 | // | |
495 |
|
|
510 | // 1. A change to this directory (direct child was | |
496 |
|
|
511 | // added or removed) cause its mtime to be set | |
@@ -504,7 +519,12 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
504 |
|
|
519 | // On a system where the time resolution poor, this | |
505 |
|
|
520 | // scenario is not unlikely if all three steps are caused | |
506 |
|
|
521 | // by the same script. | |
|
522 | return Ok(()); | |||
|
523 | } | |||
507 |
|
|
524 | } else { | |
|
525 | // OS/libc does not support mtime? | |||
|
526 | return Ok(()); | |||
|
527 | }; | |||
508 |
|
|
528 | // We’ve observed (through `status_start`) that time has | |
509 |
|
|
529 | // “progressed” since `directory_mtime`, so any further | |
510 |
|
|
530 | // change to this directory is extremely likely to cause a | |
@@ -518,11 +538,10 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
518 |
|
|
538 | // | |
519 |
|
|
539 | // We deem this scenario (unlike the previous one) to be | |
520 |
|
|
540 | // unlikely enough in practice. | |
521 | let truncated = TruncatedTimestamp::from(directory_mtime); |
|
541 | ||
522 |
|
|
542 | let is_up_to_date = | |
523 |
|
|
543 | if let Some(cached) = dirstate_node.cached_directory_mtime()? { | |
524 | { |
|
544 | cached.likely_equal(directory_mtime) | |
525 | cached.likely_equal(truncated) |
|
|||
526 |
|
|
545 | } else { | |
527 |
|
|
546 | false | |
528 |
|
|
547 | }; | |
@@ -533,10 +552,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
533 |
|
|
552 | self.new_cachable_directories | |
534 |
|
|
553 | .lock() | |
535 |
|
|
554 | .unwrap() | |
536 |
|
|
555 | .push((hg_path, directory_mtime)) | |
537 | } |
|
|||
538 | } |
|
|||
539 | } |
|
|||
540 | } |
|
556 | } | |
541 | Ok(()) |
|
557 | Ok(()) | |
542 | } |
|
558 | } |
@@ -315,9 +315,8 b' pub fn run(invocation: &crate::CliInvoca' | |||||
315 | } |
|
315 | } | |
316 |
|
316 | |||
317 | let mut dirstate_write_needed = ds_status.dirty; |
|
317 | let mut dirstate_write_needed = ds_status.dirty; | |
318 |
let filesystem_time_at_status_start = |
|
318 | let filesystem_time_at_status_start = | |
319 | .filesystem_time_at_status_start |
|
319 | ds_status.filesystem_time_at_status_start; | |
320 | .map(TruncatedTimestamp::from); |
|
|||
321 |
|
320 | |||
322 | if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) |
|
321 | if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) | |
323 | && !dirstate_write_needed |
|
322 | && !dirstate_write_needed |
General Comments 0
You need to be logged in to leave comments.
Login now