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