Show More
@@ -1,6 +1,7 b'' | |||||
1 | use crate::errors::HgError; |
|
1 | use crate::errors::HgError; | |
2 | use bitflags::bitflags; |
|
2 | use bitflags::bitflags; | |
3 | use std::convert::TryFrom; |
|
3 | use std::convert::TryFrom; | |
|
4 | use std::time::{SystemTime, UNIX_EPOCH}; | |||
4 |
|
5 | |||
5 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
|
6 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
6 | pub enum EntryState { |
|
7 | pub enum EntryState { | |
@@ -28,6 +29,73 b' bitflags! {' | |||||
28 | } |
|
29 | } | |
29 | } |
|
30 | } | |
30 |
|
31 | |||
|
32 | #[derive(Copy, Clone, PartialEq)] | |||
|
33 | pub struct Timestamp { | |||
|
34 | seconds: i64, | |||
|
35 | ||||
|
36 | /// In `0 .. 1_000_000_000`. | |||
|
37 | /// | |||
|
38 | /// This timestamp is after `(seconds, 0)` by this many nanoseconds. | |||
|
39 | nanoseconds: u32, | |||
|
40 | } | |||
|
41 | ||||
|
42 | impl Timestamp { | |||
|
43 | pub fn new(seconds: i64, nanoseconds: u32) -> Self { | |||
|
44 | Self { | |||
|
45 | seconds, | |||
|
46 | nanoseconds, | |||
|
47 | } | |||
|
48 | } | |||
|
49 | ||||
|
50 | pub fn seconds(&self) -> i64 { | |||
|
51 | self.seconds | |||
|
52 | } | |||
|
53 | ||||
|
54 | pub fn nanoseconds(&self) -> u32 { | |||
|
55 | self.nanoseconds | |||
|
56 | } | |||
|
57 | } | |||
|
58 | ||||
|
59 | impl From<SystemTime> for Timestamp { | |||
|
60 | fn from(system_time: SystemTime) -> Self { | |||
|
61 | // On Unix, `SystemTime` is a wrapper for the `timespec` C struct: | |||
|
62 | // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec | |||
|
63 | // We want to effectively access its fields, but the Rust standard | |||
|
64 | // library does not expose them. The best we can do is: | |||
|
65 | let seconds; | |||
|
66 | let nanoseconds; | |||
|
67 | match system_time.duration_since(UNIX_EPOCH) { | |||
|
68 | Ok(duration) => { | |||
|
69 | seconds = duration.as_secs() as i64; | |||
|
70 | nanoseconds = duration.subsec_nanos(); | |||
|
71 | } | |||
|
72 | Err(error) => { | |||
|
73 | // `system_time` is before `UNIX_EPOCH`. | |||
|
74 | // We need to undo this algorithm: | |||
|
75 | // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41 | |||
|
76 | let negative = error.duration(); | |||
|
77 | let negative_secs = negative.as_secs() as i64; | |||
|
78 | let negative_nanos = negative.subsec_nanos(); | |||
|
79 | if negative_nanos == 0 { | |||
|
80 | seconds = -negative_secs; | |||
|
81 | nanoseconds = 0; | |||
|
82 | } else { | |||
|
83 | // For example if `system_time` was 4.3Β seconds before | |||
|
84 | // the Unix epoch we get a Duration that represents | |||
|
85 | // `(-4, -0.3)` but we want `(-5, +0.7)`: | |||
|
86 | const NSEC_PER_SEC: u32 = 1_000_000_000; | |||
|
87 | seconds = -1 - negative_secs; | |||
|
88 | nanoseconds = NSEC_PER_SEC - negative_nanos; | |||
|
89 | } | |||
|
90 | } | |||
|
91 | }; | |||
|
92 | Self { | |||
|
93 | seconds, | |||
|
94 | nanoseconds, | |||
|
95 | } | |||
|
96 | } | |||
|
97 | } | |||
|
98 | ||||
31 | pub const V1_RANGEMASK: i32 = 0x7FFFFFFF; |
|
99 | pub const V1_RANGEMASK: i32 = 0x7FFFFFFF; | |
32 |
|
100 | |||
33 | pub const MTIME_UNSET: i32 = -1; |
|
101 | pub const MTIME_UNSET: i32 = -1; |
@@ -330,9 +330,9 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on" | |||||
330 |
|
330 | |||
331 | pub(super) fn cached_directory_mtime( |
|
331 | pub(super) fn cached_directory_mtime( | |
332 | &self, |
|
332 | &self, | |
333 |
) -> Option< |
|
333 | ) -> Option<crate::dirstate::Timestamp> { | |
334 | match self { |
|
334 | match self { | |
335 |
NodeRef::InMemory(_path, node) => match |
|
335 | NodeRef::InMemory(_path, node) => match node.data { | |
336 | NodeData::CachedDirectory { mtime } => Some(mtime), |
|
336 | NodeData::CachedDirectory { mtime } => Some(mtime), | |
337 | _ => None, |
|
337 | _ => None, | |
338 | }, |
|
338 | }, | |
@@ -376,7 +376,7 b" pub(super) struct Node<'on_disk> {" | |||||
376 |
|
376 | |||
377 | pub(super) enum NodeData { |
|
377 | pub(super) enum NodeData { | |
378 | Entry(DirstateEntry), |
|
378 | Entry(DirstateEntry), | |
379 |
CachedDirectory { mtime: |
|
379 | CachedDirectory { mtime: crate::dirstate::Timestamp }, | |
380 | None, |
|
380 | None, | |
381 | } |
|
381 | } | |
382 |
|
382 |
@@ -2,6 +2,7 b'' | |||||
2 | //! |
|
2 | //! | |
3 | //! See `mercurial/helptext/internals/dirstate-v2.txt` |
|
3 | //! See `mercurial/helptext/internals/dirstate-v2.txt` | |
4 |
|
4 | |||
|
5 | use crate::dirstate::Timestamp; | |||
5 | use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef}; |
|
6 | use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef}; | |
6 | use crate::dirstate_tree::path_with_basename::WithBasename; |
|
7 | use crate::dirstate_tree::path_with_basename::WithBasename; | |
7 | use crate::errors::HgError; |
|
8 | use crate::errors::HgError; | |
@@ -15,7 +16,6 b' use bytes_cast::BytesCast;' | |||||
15 | use format_bytes::format_bytes; |
|
16 | use format_bytes::format_bytes; | |
16 | use std::borrow::Cow; |
|
17 | use std::borrow::Cow; | |
17 | use std::convert::{TryFrom, TryInto}; |
|
18 | use std::convert::{TryFrom, TryInto}; | |
18 | use std::time::{SystemTime, UNIX_EPOCH}; |
|
|||
19 |
|
19 | |||
20 | /// Added at the start of `.hg/dirstate` when the "v2" format is used. |
|
20 | /// Added at the start of `.hg/dirstate` when the "v2" format is used. | |
21 | /// This a redundant sanity check more than an actual "magic number" since |
|
21 | /// This a redundant sanity check more than an actual "magic number" since | |
@@ -119,9 +119,9 b' struct Entry {' | |||||
119 | } |
|
119 | } | |
120 |
|
120 | |||
121 | /// Duration since the Unix epoch |
|
121 | /// Duration since the Unix epoch | |
122 |
#[derive(BytesCast, Copy, Clone |
|
122 | #[derive(BytesCast, Copy, Clone)] | |
123 | #[repr(C)] |
|
123 | #[repr(C)] | |
124 |
|
|
124 | struct PackedTimestamp { | |
125 | seconds: I64Be, |
|
125 | seconds: I64Be, | |
126 |
|
126 | |||
127 | /// In `0 .. 1_000_000_000`. |
|
127 | /// In `0 .. 1_000_000_000`. | |
@@ -316,14 +316,14 b' impl Node {' | |||||
316 | ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> { |
|
316 | ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> { | |
317 | if self.has_entry() { |
|
317 | if self.has_entry() { | |
318 | Ok(dirstate_map::NodeData::Entry(self.assume_entry())) |
|
318 | Ok(dirstate_map::NodeData::Entry(self.assume_entry())) | |
319 |
} else if let Some( |
|
319 | } else if let Some(mtime) = self.cached_directory_mtime() { | |
320 | Ok(dirstate_map::NodeData::CachedDirectory { mtime }) |
|
320 | Ok(dirstate_map::NodeData::CachedDirectory { mtime }) | |
321 | } else { |
|
321 | } else { | |
322 | Ok(dirstate_map::NodeData::None) |
|
322 | Ok(dirstate_map::NodeData::None) | |
323 | } |
|
323 | } | |
324 | } |
|
324 | } | |
325 |
|
325 | |||
326 |
pub(super) fn cached_directory_mtime(&self) -> Option< |
|
326 | pub(super) fn cached_directory_mtime(&self) -> Option<Timestamp> { | |
327 | if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() { |
|
327 | if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() { | |
328 | Some(self.data.as_timestamp()) |
|
328 | Some(self.data.as_timestamp()) | |
329 | } else { |
|
329 | } else { | |
@@ -423,58 +423,23 b' impl Entry {' | |||||
423 | } |
|
423 | } | |
424 |
|
424 | |||
425 | fn from_timestamp(timestamp: Timestamp) -> Self { |
|
425 | fn from_timestamp(timestamp: Timestamp) -> Self { | |
|
426 | let packed = PackedTimestamp { | |||
|
427 | seconds: timestamp.seconds().into(), | |||
|
428 | nanoseconds: timestamp.nanoseconds().into(), | |||
|
429 | }; | |||
426 | // Safety: both types implement the `ByteCast` trait, so we could |
|
430 | // Safety: both types implement the `ByteCast` trait, so we could | |
427 | // safely use `as_bytes` and `from_bytes` to do this conversion. Using |
|
431 | // safely use `as_bytes` and `from_bytes` to do this conversion. Using | |
428 | // `transmute` instead makes the compiler check that the two types |
|
432 | // `transmute` instead makes the compiler check that the two types | |
429 | // have the same size, which eliminates the error case of |
|
433 | // have the same size, which eliminates the error case of | |
430 | // `from_bytes`. |
|
434 | // `from_bytes`. | |
431 |
unsafe { std::mem::transmute::<Timestamp, Entry>( |
|
435 | unsafe { std::mem::transmute::<PackedTimestamp, Entry>(packed) } | |
432 | } |
|
436 | } | |
433 |
|
437 | |||
434 |
fn as_timestamp( |
|
438 | fn as_timestamp(self) -> Timestamp { | |
435 | // Safety: same as above in `from_timestamp` |
|
439 | // Safety: same as above in `from_timestamp` | |
436 | unsafe { &*(self as *const Entry as *const Timestamp) } |
|
440 | let packed = | |
437 | } |
|
441 | unsafe { std::mem::transmute::<Entry, PackedTimestamp>(self) }; | |
438 | } |
|
442 | Timestamp::new(packed.seconds.get(), packed.nanoseconds.get()) | |
439 |
|
||||
440 | impl Timestamp { |
|
|||
441 | pub fn seconds(&self) -> i64 { |
|
|||
442 | self.seconds.get() |
|
|||
443 | } |
|
|||
444 | } |
|
|||
445 |
|
||||
446 | impl From<SystemTime> for Timestamp { |
|
|||
447 | fn from(system_time: SystemTime) -> Self { |
|
|||
448 | // On Unix, `SystemTime` is a wrapper for the `timespec` C struct: |
|
|||
449 | // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec |
|
|||
450 | // We want to effectively access its fields, but the Rust standard |
|
|||
451 | // library does not expose them. The best we can do is: |
|
|||
452 | let (secs, nanos) = match system_time.duration_since(UNIX_EPOCH) { |
|
|||
453 | Ok(duration) => { |
|
|||
454 | (duration.as_secs() as i64, duration.subsec_nanos()) |
|
|||
455 | } |
|
|||
456 | Err(error) => { |
|
|||
457 | // `system_time` is before `UNIX_EPOCH`. |
|
|||
458 | // We need to undo this algorithm: |
|
|||
459 | // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41 |
|
|||
460 | let negative = error.duration(); |
|
|||
461 | let negative_secs = negative.as_secs() as i64; |
|
|||
462 | let negative_nanos = negative.subsec_nanos(); |
|
|||
463 | if negative_nanos == 0 { |
|
|||
464 | (-negative_secs, 0) |
|
|||
465 | } else { |
|
|||
466 | // For example if `system_time` was 4.3Β seconds before |
|
|||
467 | // the Unix epoch we get a Duration that represents |
|
|||
468 | // `(-4, -0.3)` but we want `(-5, +0.7)`: |
|
|||
469 | const NSEC_PER_SEC: u32 = 1_000_000_000; |
|
|||
470 | (-1 - negative_secs, NSEC_PER_SEC - negative_nanos) |
|
|||
471 | } |
|
|||
472 | } |
|
|||
473 | }; |
|
|||
474 | Timestamp { |
|
|||
475 | seconds: secs.into(), |
|
|||
476 | nanoseconds: nanos.into(), |
|
|||
477 | } |
|
|||
478 | } |
|
443 | } | |
479 | } |
|
444 | } | |
480 |
|
445 |
@@ -1,3 +1,4 b'' | |||||
|
1 | use crate::dirstate::entry::Timestamp; | |||
1 | use crate::dirstate::status::IgnoreFnType; |
|
2 | use crate::dirstate::status::IgnoreFnType; | |
2 | use crate::dirstate_tree::dirstate_map::BorrowedPath; |
|
3 | use crate::dirstate_tree::dirstate_map::BorrowedPath; | |
3 | use crate::dirstate_tree::dirstate_map::ChildNodesRef; |
|
4 | use crate::dirstate_tree::dirstate_map::ChildNodesRef; | |
@@ -5,7 +6,6 b' use crate::dirstate_tree::dirstate_map::' | |||||
5 | use crate::dirstate_tree::dirstate_map::NodeData; |
|
6 | use crate::dirstate_tree::dirstate_map::NodeData; | |
6 | use crate::dirstate_tree::dirstate_map::NodeRef; |
|
7 | use crate::dirstate_tree::dirstate_map::NodeRef; | |
7 | use crate::dirstate_tree::on_disk::DirstateV2ParseError; |
|
8 | use crate::dirstate_tree::on_disk::DirstateV2ParseError; | |
8 | use crate::dirstate_tree::on_disk::Timestamp; |
|
|||
9 | use crate::matchers::get_ignore_function; |
|
9 | use crate::matchers::get_ignore_function; | |
10 | use crate::matchers::Matcher; |
|
10 | use crate::matchers::Matcher; | |
11 | use crate::utils::files::get_bytes_from_os_string; |
|
11 | use crate::utils::files::get_bytes_from_os_string; | |
@@ -182,7 +182,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
182 | fn can_skip_fs_readdir( |
|
182 | fn can_skip_fs_readdir( | |
183 | &self, |
|
183 | &self, | |
184 | directory_metadata: Option<&std::fs::Metadata>, |
|
184 | directory_metadata: Option<&std::fs::Metadata>, | |
185 |
cached_directory_mtime: Option< |
|
185 | cached_directory_mtime: Option<Timestamp>, | |
186 | ) -> bool { |
|
186 | ) -> bool { | |
187 | if !self.options.list_unknown && !self.options.list_ignored { |
|
187 | if !self.options.list_unknown && !self.options.list_ignored { | |
188 | // All states that we care about listing have corresponding |
|
188 | // All states that we care about listing have corresponding | |
@@ -200,7 +200,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
200 | if let Some(meta) = directory_metadata { |
|
200 | if let Some(meta) = directory_metadata { | |
201 | if let Ok(current_mtime) = meta.modified() { |
|
201 | if let Ok(current_mtime) = meta.modified() { | |
202 | let current_mtime = Timestamp::from(current_mtime); |
|
202 | let current_mtime = Timestamp::from(current_mtime); | |
203 |
if current_mtime == |
|
203 | if current_mtime == cached_mtime { | |
204 | // The mtime of that directory has not changed |
|
204 | // The mtime of that directory has not changed | |
205 | // since then, which means that the results of |
|
205 | // since then, which means that the results of | |
206 | // `read_dir` should also be unchanged. |
|
206 | // `read_dir` should also be unchanged. | |
@@ -222,7 +222,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
222 | directory_hg_path: &BorrowedPath<'tree, 'on_disk>, |
|
222 | directory_hg_path: &BorrowedPath<'tree, 'on_disk>, | |
223 | directory_fs_path: &Path, |
|
223 | directory_fs_path: &Path, | |
224 | directory_metadata: Option<&std::fs::Metadata>, |
|
224 | directory_metadata: Option<&std::fs::Metadata>, | |
225 |
cached_directory_mtime: Option< |
|
225 | cached_directory_mtime: Option<Timestamp>, | |
226 | is_at_repo_root: bool, |
|
226 | is_at_repo_root: bool, | |
227 | ) -> Result<bool, DirstateV2ParseError> { |
|
227 | ) -> Result<bool, DirstateV2ParseError> { | |
228 | if self.can_skip_fs_readdir(directory_metadata, cached_directory_mtime) |
|
228 | if self.can_skip_fs_readdir(directory_metadata, cached_directory_mtime) | |
@@ -468,7 +468,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
468 | // unlikely enough in practice. |
|
468 | // unlikely enough in practice. | |
469 | let timestamp = directory_mtime.into(); |
|
469 | let timestamp = directory_mtime.into(); | |
470 | let cached = dirstate_node.cached_directory_mtime(); |
|
470 | let cached = dirstate_node.cached_directory_mtime(); | |
471 |
if cached != Some( |
|
471 | if cached != Some(timestamp) { | |
472 | let hg_path = dirstate_node |
|
472 | let hg_path = dirstate_node | |
473 | .full_path_borrowed(self.dmap.on_disk)? |
|
473 | .full_path_borrowed(self.dmap.on_disk)? | |
474 | .detach_from_tree(); |
|
474 | .detach_from_tree(); |
General Comments 0
You need to be logged in to leave comments.
Login now