##// END OF EJS Templates
dirstate-v2: Apply SECOND_AMBIGUOUS to directory mtimes too...
Simon Sapin -
r49332:4afb9627 default
parent child Browse files
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<std::time::SystemTime>,
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.try_into()?))
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 0.into(),
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 = filesystem_now(&root_dir).ok();
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<SystemTime>,
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 // All filesystem directory entries from `read_dir` have a
479 // All filesystem directory entries from `read_dir` have a
477 // corresponding node in the dirstate, so we can reconstitute the
480 // corresponding node in the dirstate, so we can reconstitute the
478 // names of those entries without calling `read_dir` again.
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 // Although the Rust standard library’s `SystemTime` type
493 // Although the Rust standard library’s `SystemTime` type
484 // has nanosecond precision, the times reported for a
494 // has nanosecond precision, the times reported for a
485 // directory’s (or file’s) modified time may have lower
495 // directory’s (or file’s) modified time may have lower
486 // resolution based on the filesystem (for example ext3
496 // resolution based on the filesystem (for example ext3
487 // only stores integer seconds), kernel (see
497 // only stores integer seconds), kernel (see
488 // https://stackoverflow.com/a/14393315/1162888), etc.
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 // 1. A change to this directory (direct child was
510 // 1. A change to this directory (direct child was
496 // added or removed) cause its mtime to be set
511 // added or removed) cause its mtime to be set
@@ -504,7 +519,12 b" impl<'a, 'tree, 'on_disk> StatusCommon<'"
504 // On a system where the time resolution poor, this
519 // On a system where the time resolution poor, this
505 // scenario is not unlikely if all three steps are caused
520 // scenario is not unlikely if all three steps are caused
506 // by the same script.
521 // by the same script.
522 return Ok(());
523 }
507 } else {
524 } else {
525 // OS/libc does not support mtime?
526 return Ok(());
527 };
508 // We’ve observed (through `status_start`) that time has
528 // We’ve observed (through `status_start`) that time has
509 // “progressed” since `directory_mtime`, so any further
529 // “progressed” since `directory_mtime`, so any further
510 // change to this directory is extremely likely to cause a
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 // We deem this scenario (unlike the previous one) to be
539 // We deem this scenario (unlike the previous one) to be
520 // unlikely enough in practice.
540 // unlikely enough in practice.
521 let truncated = TruncatedTimestamp::from(directory_mtime);
541
522 let is_up_to_date = if let Some(cached) =
542 let is_up_to_date =
523 dirstate_node.cached_directory_mtime()?
543 if let Some(cached) = dirstate_node.cached_directory_mtime()? {
524 {
544 cached.likely_equal(directory_mtime)
525 cached.likely_equal(truncated)
526 } else {
545 } else {
527 false
546 false
528 };
547 };
@@ -533,10 +552,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'"
533 self.new_cachable_directories
552 self.new_cachable_directories
534 .lock()
553 .lock()
535 .unwrap()
554 .unwrap()
536 .push((hg_path, truncated))
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 = ds_status
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