##// END OF EJS Templates
dirstate: align Rust function name to `need_delay`...
marmoute -
r49077:c591944f default
parent child Browse files
Show More
@@ -1,637 +1,638 b''
1 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
1 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
2 use crate::errors::HgError;
2 use crate::errors::HgError;
3 use bitflags::bitflags;
3 use bitflags::bitflags;
4 use std::convert::{TryFrom, TryInto};
4 use std::convert::{TryFrom, TryInto};
5 use std::fs;
5 use std::fs;
6 use std::io;
6 use std::io;
7 use std::time::{SystemTime, UNIX_EPOCH};
7 use std::time::{SystemTime, UNIX_EPOCH};
8
8
9 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
9 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
10 pub enum EntryState {
10 pub enum EntryState {
11 Normal,
11 Normal,
12 Added,
12 Added,
13 Removed,
13 Removed,
14 Merged,
14 Merged,
15 }
15 }
16
16
17 /// The C implementation uses all signed types. This will be an issue
17 /// The C implementation uses all signed types. This will be an issue
18 /// either when 4GB+ source files are commonplace or in 2038, whichever
18 /// either when 4GB+ source files are commonplace or in 2038, whichever
19 /// comes first.
19 /// comes first.
20 #[derive(Debug, PartialEq, Copy, Clone)]
20 #[derive(Debug, PartialEq, Copy, Clone)]
21 pub struct DirstateEntry {
21 pub struct DirstateEntry {
22 pub(crate) flags: Flags,
22 pub(crate) flags: Flags,
23 mode_size: Option<(u32, u32)>,
23 mode_size: Option<(u32, u32)>,
24 mtime: Option<u32>,
24 mtime: Option<u32>,
25 }
25 }
26
26
27 bitflags! {
27 bitflags! {
28 pub(crate) struct Flags: u8 {
28 pub(crate) struct Flags: u8 {
29 const WDIR_TRACKED = 1 << 0;
29 const WDIR_TRACKED = 1 << 0;
30 const P1_TRACKED = 1 << 1;
30 const P1_TRACKED = 1 << 1;
31 const P2_INFO = 1 << 2;
31 const P2_INFO = 1 << 2;
32 const HAS_FALLBACK_EXEC = 1 << 3;
32 const HAS_FALLBACK_EXEC = 1 << 3;
33 const FALLBACK_EXEC = 1 << 4;
33 const FALLBACK_EXEC = 1 << 4;
34 const HAS_FALLBACK_SYMLINK = 1 << 5;
34 const HAS_FALLBACK_SYMLINK = 1 << 5;
35 const FALLBACK_SYMLINK = 1 << 6;
35 const FALLBACK_SYMLINK = 1 << 6;
36 }
36 }
37 }
37 }
38
38
39 /// A Unix timestamp with nanoseconds precision
39 /// A Unix timestamp with nanoseconds precision
40 #[derive(Copy, Clone)]
40 #[derive(Copy, Clone)]
41 pub struct TruncatedTimestamp {
41 pub struct TruncatedTimestamp {
42 truncated_seconds: u32,
42 truncated_seconds: u32,
43 /// Always in the `0 .. 1_000_000_000` range.
43 /// Always in the `0 .. 1_000_000_000` range.
44 nanoseconds: u32,
44 nanoseconds: u32,
45 }
45 }
46
46
47 impl TruncatedTimestamp {
47 impl TruncatedTimestamp {
48 /// Constructs from a timestamp potentially outside of the supported range,
48 /// Constructs from a timestamp potentially outside of the supported range,
49 /// and truncate the seconds components to its lower 31 bits.
49 /// and truncate the seconds components to its lower 31 bits.
50 ///
50 ///
51 /// Panics if the nanoseconds components is not in the expected range.
51 /// Panics if the nanoseconds components is not in the expected range.
52 pub fn new_truncate(seconds: i64, nanoseconds: u32) -> Self {
52 pub fn new_truncate(seconds: i64, nanoseconds: u32) -> Self {
53 assert!(nanoseconds < NSEC_PER_SEC);
53 assert!(nanoseconds < NSEC_PER_SEC);
54 Self {
54 Self {
55 truncated_seconds: seconds as u32 & RANGE_MASK_31BIT,
55 truncated_seconds: seconds as u32 & RANGE_MASK_31BIT,
56 nanoseconds,
56 nanoseconds,
57 }
57 }
58 }
58 }
59
59
60 /// Construct from components. Returns an error if they are not in the
60 /// Construct from components. Returns an error if they are not in the
61 /// expcted range.
61 /// expcted range.
62 pub fn from_already_truncated(
62 pub fn from_already_truncated(
63 truncated_seconds: u32,
63 truncated_seconds: u32,
64 nanoseconds: u32,
64 nanoseconds: u32,
65 ) -> Result<Self, DirstateV2ParseError> {
65 ) -> Result<Self, DirstateV2ParseError> {
66 if truncated_seconds & !RANGE_MASK_31BIT == 0
66 if truncated_seconds & !RANGE_MASK_31BIT == 0
67 && nanoseconds < NSEC_PER_SEC
67 && nanoseconds < NSEC_PER_SEC
68 {
68 {
69 Ok(Self {
69 Ok(Self {
70 truncated_seconds,
70 truncated_seconds,
71 nanoseconds,
71 nanoseconds,
72 })
72 })
73 } else {
73 } else {
74 Err(DirstateV2ParseError)
74 Err(DirstateV2ParseError)
75 }
75 }
76 }
76 }
77
77
78 pub fn for_mtime_of(metadata: &fs::Metadata) -> io::Result<Self> {
78 pub fn for_mtime_of(metadata: &fs::Metadata) -> io::Result<Self> {
79 #[cfg(unix)]
79 #[cfg(unix)]
80 {
80 {
81 use std::os::unix::fs::MetadataExt;
81 use std::os::unix::fs::MetadataExt;
82 let seconds = metadata.mtime();
82 let seconds = metadata.mtime();
83 // i64 -> u32 with value always in the `0 .. NSEC_PER_SEC` range
83 // i64 -> u32 with value always in the `0 .. NSEC_PER_SEC` range
84 let nanoseconds = metadata.mtime_nsec().try_into().unwrap();
84 let nanoseconds = metadata.mtime_nsec().try_into().unwrap();
85 Ok(Self::new_truncate(seconds, nanoseconds))
85 Ok(Self::new_truncate(seconds, nanoseconds))
86 }
86 }
87 #[cfg(not(unix))]
87 #[cfg(not(unix))]
88 {
88 {
89 metadata.modified().map(Self::from)
89 metadata.modified().map(Self::from)
90 }
90 }
91 }
91 }
92
92
93 /// The lower 31 bits of the number of seconds since the epoch.
93 /// The lower 31 bits of the number of seconds since the epoch.
94 pub fn truncated_seconds(&self) -> u32 {
94 pub fn truncated_seconds(&self) -> u32 {
95 self.truncated_seconds
95 self.truncated_seconds
96 }
96 }
97
97
98 /// The sub-second component of this timestamp, in nanoseconds.
98 /// The sub-second component of this timestamp, in nanoseconds.
99 /// Always in the `0 .. 1_000_000_000` range.
99 /// Always in the `0 .. 1_000_000_000` range.
100 ///
100 ///
101 /// This timestamp is after `(seconds, 0)` by this many nanoseconds.
101 /// This timestamp is after `(seconds, 0)` by this many nanoseconds.
102 pub fn nanoseconds(&self) -> u32 {
102 pub fn nanoseconds(&self) -> u32 {
103 self.nanoseconds
103 self.nanoseconds
104 }
104 }
105
105
106 /// Returns whether two timestamps are equal modulo 2**31 seconds.
106 /// Returns whether two timestamps are equal modulo 2**31 seconds.
107 ///
107 ///
108 /// If this returns `true`, the original values converted from `SystemTime`
108 /// If this returns `true`, the original values converted from `SystemTime`
109 /// or given to `new_truncate` were very likely equal. A false positive is
109 /// or given to `new_truncate` were very likely equal. A false positive is
110 /// possible if they were exactly a multiple of 2**31 seconds apart (around
110 /// possible if they were exactly a multiple of 2**31 seconds apart (around
111 /// 68 years). This is deemed very unlikely to happen by chance, especially
111 /// 68 years). This is deemed very unlikely to happen by chance, especially
112 /// on filesystems that support sub-second precision.
112 /// on filesystems that support sub-second precision.
113 ///
113 ///
114 /// If someone is manipulating the modification times of some files to
114 /// If someone is manipulating the modification times of some files to
115 /// intentionally make `hg status` return incorrect results, not truncating
115 /// intentionally make `hg status` return incorrect results, not truncating
116 /// wouldn’t help much since they can set exactly the expected timestamp.
116 /// wouldn’t help much since they can set exactly the expected timestamp.
117 pub fn likely_equal(self, other: Self) -> bool {
117 pub fn likely_equal(self, other: Self) -> bool {
118 self.truncated_seconds == other.truncated_seconds
118 self.truncated_seconds == other.truncated_seconds
119 && self.nanoseconds == other.nanoseconds
119 && self.nanoseconds == other.nanoseconds
120 }
120 }
121
121
122 pub fn likely_equal_to_mtime_of(
122 pub fn likely_equal_to_mtime_of(
123 self,
123 self,
124 metadata: &fs::Metadata,
124 metadata: &fs::Metadata,
125 ) -> io::Result<bool> {
125 ) -> io::Result<bool> {
126 Ok(self.likely_equal(Self::for_mtime_of(metadata)?))
126 Ok(self.likely_equal(Self::for_mtime_of(metadata)?))
127 }
127 }
128 }
128 }
129
129
130 impl From<SystemTime> for TruncatedTimestamp {
130 impl From<SystemTime> for TruncatedTimestamp {
131 fn from(system_time: SystemTime) -> Self {
131 fn from(system_time: SystemTime) -> Self {
132 // On Unix, `SystemTime` is a wrapper for the `timespec` C struct:
132 // On Unix, `SystemTime` is a wrapper for the `timespec` C struct:
133 // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec
133 // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec
134 // We want to effectively access its fields, but the Rust standard
134 // We want to effectively access its fields, but the Rust standard
135 // library does not expose them. The best we can do is:
135 // library does not expose them. The best we can do is:
136 let seconds;
136 let seconds;
137 let nanoseconds;
137 let nanoseconds;
138 match system_time.duration_since(UNIX_EPOCH) {
138 match system_time.duration_since(UNIX_EPOCH) {
139 Ok(duration) => {
139 Ok(duration) => {
140 seconds = duration.as_secs() as i64;
140 seconds = duration.as_secs() as i64;
141 nanoseconds = duration.subsec_nanos();
141 nanoseconds = duration.subsec_nanos();
142 }
142 }
143 Err(error) => {
143 Err(error) => {
144 // `system_time` is before `UNIX_EPOCH`.
144 // `system_time` is before `UNIX_EPOCH`.
145 // We need to undo this algorithm:
145 // We need to undo this algorithm:
146 // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41
146 // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41
147 let negative = error.duration();
147 let negative = error.duration();
148 let negative_secs = negative.as_secs() as i64;
148 let negative_secs = negative.as_secs() as i64;
149 let negative_nanos = negative.subsec_nanos();
149 let negative_nanos = negative.subsec_nanos();
150 if negative_nanos == 0 {
150 if negative_nanos == 0 {
151 seconds = -negative_secs;
151 seconds = -negative_secs;
152 nanoseconds = 0;
152 nanoseconds = 0;
153 } else {
153 } else {
154 // For example if `system_time` was 4.3 seconds before
154 // For example if `system_time` was 4.3 seconds before
155 // the Unix epoch we get a Duration that represents
155 // the Unix epoch we get a Duration that represents
156 // `(-4, -0.3)` but we want `(-5, +0.7)`:
156 // `(-4, -0.3)` but we want `(-5, +0.7)`:
157 seconds = -1 - negative_secs;
157 seconds = -1 - negative_secs;
158 nanoseconds = NSEC_PER_SEC - negative_nanos;
158 nanoseconds = NSEC_PER_SEC - negative_nanos;
159 }
159 }
160 }
160 }
161 };
161 };
162 Self::new_truncate(seconds, nanoseconds)
162 Self::new_truncate(seconds, nanoseconds)
163 }
163 }
164 }
164 }
165
165
166 const NSEC_PER_SEC: u32 = 1_000_000_000;
166 const NSEC_PER_SEC: u32 = 1_000_000_000;
167 const RANGE_MASK_31BIT: u32 = 0x7FFF_FFFF;
167 const RANGE_MASK_31BIT: u32 = 0x7FFF_FFFF;
168
168
169 pub const MTIME_UNSET: i32 = -1;
169 pub const MTIME_UNSET: i32 = -1;
170
170
171 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
171 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
172 /// other parent. This allows revert to pick the right status back during a
172 /// other parent. This allows revert to pick the right status back during a
173 /// merge.
173 /// merge.
174 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
174 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
175 /// A special value used for internal representation of special case in
175 /// A special value used for internal representation of special case in
176 /// dirstate v1 format.
176 /// dirstate v1 format.
177 pub const SIZE_NON_NORMAL: i32 = -1;
177 pub const SIZE_NON_NORMAL: i32 = -1;
178
178
179 impl DirstateEntry {
179 impl DirstateEntry {
180 pub fn from_v2_data(
180 pub fn from_v2_data(
181 wdir_tracked: bool,
181 wdir_tracked: bool,
182 p1_tracked: bool,
182 p1_tracked: bool,
183 p2_info: bool,
183 p2_info: bool,
184 mode_size: Option<(u32, u32)>,
184 mode_size: Option<(u32, u32)>,
185 mtime: Option<u32>,
185 mtime: Option<u32>,
186 fallback_exec: Option<bool>,
186 fallback_exec: Option<bool>,
187 fallback_symlink: Option<bool>,
187 fallback_symlink: Option<bool>,
188 ) -> Self {
188 ) -> Self {
189 if let Some((mode, size)) = mode_size {
189 if let Some((mode, size)) = mode_size {
190 // TODO: return an error for out of range values?
190 // TODO: return an error for out of range values?
191 assert!(mode & !RANGE_MASK_31BIT == 0);
191 assert!(mode & !RANGE_MASK_31BIT == 0);
192 assert!(size & !RANGE_MASK_31BIT == 0);
192 assert!(size & !RANGE_MASK_31BIT == 0);
193 }
193 }
194 if let Some(mtime) = mtime {
194 if let Some(mtime) = mtime {
195 assert!(mtime & !RANGE_MASK_31BIT == 0);
195 assert!(mtime & !RANGE_MASK_31BIT == 0);
196 }
196 }
197 let mut flags = Flags::empty();
197 let mut flags = Flags::empty();
198 flags.set(Flags::WDIR_TRACKED, wdir_tracked);
198 flags.set(Flags::WDIR_TRACKED, wdir_tracked);
199 flags.set(Flags::P1_TRACKED, p1_tracked);
199 flags.set(Flags::P1_TRACKED, p1_tracked);
200 flags.set(Flags::P2_INFO, p2_info);
200 flags.set(Flags::P2_INFO, p2_info);
201 if let Some(exec) = fallback_exec {
201 if let Some(exec) = fallback_exec {
202 flags.insert(Flags::HAS_FALLBACK_EXEC);
202 flags.insert(Flags::HAS_FALLBACK_EXEC);
203 if exec {
203 if exec {
204 flags.insert(Flags::FALLBACK_EXEC);
204 flags.insert(Flags::FALLBACK_EXEC);
205 }
205 }
206 }
206 }
207 if let Some(exec) = fallback_symlink {
207 if let Some(exec) = fallback_symlink {
208 flags.insert(Flags::HAS_FALLBACK_SYMLINK);
208 flags.insert(Flags::HAS_FALLBACK_SYMLINK);
209 if exec {
209 if exec {
210 flags.insert(Flags::FALLBACK_SYMLINK);
210 flags.insert(Flags::FALLBACK_SYMLINK);
211 }
211 }
212 }
212 }
213 Self {
213 Self {
214 flags,
214 flags,
215 mode_size,
215 mode_size,
216 mtime,
216 mtime,
217 }
217 }
218 }
218 }
219
219
220 pub fn from_v1_data(
220 pub fn from_v1_data(
221 state: EntryState,
221 state: EntryState,
222 mode: i32,
222 mode: i32,
223 size: i32,
223 size: i32,
224 mtime: i32,
224 mtime: i32,
225 ) -> Self {
225 ) -> Self {
226 match state {
226 match state {
227 EntryState::Normal => {
227 EntryState::Normal => {
228 if size == SIZE_FROM_OTHER_PARENT {
228 if size == SIZE_FROM_OTHER_PARENT {
229 Self {
229 Self {
230 // might be missing P1_TRACKED
230 // might be missing P1_TRACKED
231 flags: Flags::WDIR_TRACKED | Flags::P2_INFO,
231 flags: Flags::WDIR_TRACKED | Flags::P2_INFO,
232 mode_size: None,
232 mode_size: None,
233 mtime: None,
233 mtime: None,
234 }
234 }
235 } else if size == SIZE_NON_NORMAL {
235 } else if size == SIZE_NON_NORMAL {
236 Self {
236 Self {
237 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
237 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
238 mode_size: None,
238 mode_size: None,
239 mtime: None,
239 mtime: None,
240 }
240 }
241 } else if mtime == MTIME_UNSET {
241 } else if mtime == MTIME_UNSET {
242 // TODO: return an error for negative values?
242 // TODO: return an error for negative values?
243 let mode = u32::try_from(mode).unwrap();
243 let mode = u32::try_from(mode).unwrap();
244 let size = u32::try_from(size).unwrap();
244 let size = u32::try_from(size).unwrap();
245 Self {
245 Self {
246 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
246 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
247 mode_size: Some((mode, size)),
247 mode_size: Some((mode, size)),
248 mtime: None,
248 mtime: None,
249 }
249 }
250 } else {
250 } else {
251 // TODO: return an error for negative values?
251 // TODO: return an error for negative values?
252 let mode = u32::try_from(mode).unwrap();
252 let mode = u32::try_from(mode).unwrap();
253 let size = u32::try_from(size).unwrap();
253 let size = u32::try_from(size).unwrap();
254 let mtime = u32::try_from(mtime).unwrap();
254 let mtime = u32::try_from(mtime).unwrap();
255 Self {
255 Self {
256 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
256 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
257 mode_size: Some((mode, size)),
257 mode_size: Some((mode, size)),
258 mtime: Some(mtime),
258 mtime: Some(mtime),
259 }
259 }
260 }
260 }
261 }
261 }
262 EntryState::Added => Self {
262 EntryState::Added => Self {
263 flags: Flags::WDIR_TRACKED,
263 flags: Flags::WDIR_TRACKED,
264 mode_size: None,
264 mode_size: None,
265 mtime: None,
265 mtime: None,
266 },
266 },
267 EntryState::Removed => Self {
267 EntryState::Removed => Self {
268 flags: if size == SIZE_NON_NORMAL {
268 flags: if size == SIZE_NON_NORMAL {
269 Flags::P1_TRACKED | Flags::P2_INFO
269 Flags::P1_TRACKED | Flags::P2_INFO
270 } else if size == SIZE_FROM_OTHER_PARENT {
270 } else if size == SIZE_FROM_OTHER_PARENT {
271 // We don’t know if P1_TRACKED should be set (file history)
271 // We don’t know if P1_TRACKED should be set (file history)
272 Flags::P2_INFO
272 Flags::P2_INFO
273 } else {
273 } else {
274 Flags::P1_TRACKED
274 Flags::P1_TRACKED
275 },
275 },
276 mode_size: None,
276 mode_size: None,
277 mtime: None,
277 mtime: None,
278 },
278 },
279 EntryState::Merged => Self {
279 EntryState::Merged => Self {
280 flags: Flags::WDIR_TRACKED
280 flags: Flags::WDIR_TRACKED
281 | Flags::P1_TRACKED // might not be true because of rename ?
281 | Flags::P1_TRACKED // might not be true because of rename ?
282 | Flags::P2_INFO, // might not be true because of rename ?
282 | Flags::P2_INFO, // might not be true because of rename ?
283 mode_size: None,
283 mode_size: None,
284 mtime: None,
284 mtime: None,
285 },
285 },
286 }
286 }
287 }
287 }
288
288
289 /// Creates a new entry in "removed" state.
289 /// Creates a new entry in "removed" state.
290 ///
290 ///
291 /// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
291 /// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
292 /// `SIZE_FROM_OTHER_PARENT`
292 /// `SIZE_FROM_OTHER_PARENT`
293 pub fn new_removed(size: i32) -> Self {
293 pub fn new_removed(size: i32) -> Self {
294 Self::from_v1_data(EntryState::Removed, 0, size, 0)
294 Self::from_v1_data(EntryState::Removed, 0, size, 0)
295 }
295 }
296
296
297 pub fn tracked(&self) -> bool {
297 pub fn tracked(&self) -> bool {
298 self.flags.contains(Flags::WDIR_TRACKED)
298 self.flags.contains(Flags::WDIR_TRACKED)
299 }
299 }
300
300
301 pub fn p1_tracked(&self) -> bool {
301 pub fn p1_tracked(&self) -> bool {
302 self.flags.contains(Flags::P1_TRACKED)
302 self.flags.contains(Flags::P1_TRACKED)
303 }
303 }
304
304
305 fn in_either_parent(&self) -> bool {
305 fn in_either_parent(&self) -> bool {
306 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO)
306 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO)
307 }
307 }
308
308
309 pub fn removed(&self) -> bool {
309 pub fn removed(&self) -> bool {
310 self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED)
310 self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED)
311 }
311 }
312
312
313 pub fn p2_info(&self) -> bool {
313 pub fn p2_info(&self) -> bool {
314 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
314 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
315 }
315 }
316
316
317 pub fn added(&self) -> bool {
317 pub fn added(&self) -> bool {
318 self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent()
318 self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent()
319 }
319 }
320
320
321 pub fn maybe_clean(&self) -> bool {
321 pub fn maybe_clean(&self) -> bool {
322 if !self.flags.contains(Flags::WDIR_TRACKED) {
322 if !self.flags.contains(Flags::WDIR_TRACKED) {
323 false
323 false
324 } else if !self.flags.contains(Flags::P1_TRACKED) {
324 } else if !self.flags.contains(Flags::P1_TRACKED) {
325 false
325 false
326 } else if self.flags.contains(Flags::P2_INFO) {
326 } else if self.flags.contains(Flags::P2_INFO) {
327 false
327 false
328 } else {
328 } else {
329 true
329 true
330 }
330 }
331 }
331 }
332
332
333 pub fn any_tracked(&self) -> bool {
333 pub fn any_tracked(&self) -> bool {
334 self.flags.intersects(
334 self.flags.intersects(
335 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
335 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
336 )
336 )
337 }
337 }
338
338
339 /// Returns `(wdir_tracked, p1_tracked, p2_info, mode_size, mtime)`
339 /// Returns `(wdir_tracked, p1_tracked, p2_info, mode_size, mtime)`
340 pub(crate) fn v2_data(
340 pub(crate) fn v2_data(
341 &self,
341 &self,
342 ) -> (
342 ) -> (
343 bool,
343 bool,
344 bool,
344 bool,
345 bool,
345 bool,
346 Option<(u32, u32)>,
346 Option<(u32, u32)>,
347 Option<u32>,
347 Option<u32>,
348 Option<bool>,
348 Option<bool>,
349 Option<bool>,
349 Option<bool>,
350 ) {
350 ) {
351 if !self.any_tracked() {
351 if !self.any_tracked() {
352 // TODO: return an Option instead?
352 // TODO: return an Option instead?
353 panic!("Accessing v1_state of an untracked DirstateEntry")
353 panic!("Accessing v1_state of an untracked DirstateEntry")
354 }
354 }
355 let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED);
355 let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED);
356 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
356 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
357 let p2_info = self.flags.contains(Flags::P2_INFO);
357 let p2_info = self.flags.contains(Flags::P2_INFO);
358 let mode_size = self.mode_size;
358 let mode_size = self.mode_size;
359 let mtime = self.mtime;
359 let mtime = self.mtime;
360 (
360 (
361 wdir_tracked,
361 wdir_tracked,
362 p1_tracked,
362 p1_tracked,
363 p2_info,
363 p2_info,
364 mode_size,
364 mode_size,
365 mtime,
365 mtime,
366 self.get_fallback_exec(),
366 self.get_fallback_exec(),
367 self.get_fallback_symlink(),
367 self.get_fallback_symlink(),
368 )
368 )
369 }
369 }
370
370
371 fn v1_state(&self) -> EntryState {
371 fn v1_state(&self) -> EntryState {
372 if !self.any_tracked() {
372 if !self.any_tracked() {
373 // TODO: return an Option instead?
373 // TODO: return an Option instead?
374 panic!("Accessing v1_state of an untracked DirstateEntry")
374 panic!("Accessing v1_state of an untracked DirstateEntry")
375 }
375 }
376 if self.removed() {
376 if self.removed() {
377 EntryState::Removed
377 EntryState::Removed
378 } else if self
378 } else if self
379 .flags
379 .flags
380 .contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO)
380 .contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO)
381 {
381 {
382 EntryState::Merged
382 EntryState::Merged
383 } else if self.added() {
383 } else if self.added() {
384 EntryState::Added
384 EntryState::Added
385 } else {
385 } else {
386 EntryState::Normal
386 EntryState::Normal
387 }
387 }
388 }
388 }
389
389
390 fn v1_mode(&self) -> i32 {
390 fn v1_mode(&self) -> i32 {
391 if let Some((mode, _size)) = self.mode_size {
391 if let Some((mode, _size)) = self.mode_size {
392 i32::try_from(mode).unwrap()
392 i32::try_from(mode).unwrap()
393 } else {
393 } else {
394 0
394 0
395 }
395 }
396 }
396 }
397
397
398 fn v1_size(&self) -> i32 {
398 fn v1_size(&self) -> i32 {
399 if !self.any_tracked() {
399 if !self.any_tracked() {
400 // TODO: return an Option instead?
400 // TODO: return an Option instead?
401 panic!("Accessing v1_size of an untracked DirstateEntry")
401 panic!("Accessing v1_size of an untracked DirstateEntry")
402 }
402 }
403 if self.removed()
403 if self.removed()
404 && self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO)
404 && self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO)
405 {
405 {
406 SIZE_NON_NORMAL
406 SIZE_NON_NORMAL
407 } else if self.flags.contains(Flags::P2_INFO) {
407 } else if self.flags.contains(Flags::P2_INFO) {
408 SIZE_FROM_OTHER_PARENT
408 SIZE_FROM_OTHER_PARENT
409 } else if self.removed() {
409 } else if self.removed() {
410 0
410 0
411 } else if self.added() {
411 } else if self.added() {
412 SIZE_NON_NORMAL
412 SIZE_NON_NORMAL
413 } else if let Some((_mode, size)) = self.mode_size {
413 } else if let Some((_mode, size)) = self.mode_size {
414 i32::try_from(size).unwrap()
414 i32::try_from(size).unwrap()
415 } else {
415 } else {
416 SIZE_NON_NORMAL
416 SIZE_NON_NORMAL
417 }
417 }
418 }
418 }
419
419
420 fn v1_mtime(&self) -> i32 {
420 fn v1_mtime(&self) -> i32 {
421 if !self.any_tracked() {
421 if !self.any_tracked() {
422 // TODO: return an Option instead?
422 // TODO: return an Option instead?
423 panic!("Accessing v1_mtime of an untracked DirstateEntry")
423 panic!("Accessing v1_mtime of an untracked DirstateEntry")
424 }
424 }
425 if self.removed() {
425 if self.removed() {
426 0
426 0
427 } else if self.flags.contains(Flags::P2_INFO) {
427 } else if self.flags.contains(Flags::P2_INFO) {
428 MTIME_UNSET
428 MTIME_UNSET
429 } else if !self.flags.contains(Flags::P1_TRACKED) {
429 } else if !self.flags.contains(Flags::P1_TRACKED) {
430 MTIME_UNSET
430 MTIME_UNSET
431 } else if let Some(mtime) = self.mtime {
431 } else if let Some(mtime) = self.mtime {
432 i32::try_from(mtime).unwrap()
432 i32::try_from(mtime).unwrap()
433 } else {
433 } else {
434 MTIME_UNSET
434 MTIME_UNSET
435 }
435 }
436 }
436 }
437
437
438 // TODO: return `Option<EntryState>`? None when `!self.any_tracked`
438 // TODO: return `Option<EntryState>`? None when `!self.any_tracked`
439 pub fn state(&self) -> EntryState {
439 pub fn state(&self) -> EntryState {
440 self.v1_state()
440 self.v1_state()
441 }
441 }
442
442
443 // TODO: return Option?
443 // TODO: return Option?
444 pub fn mode(&self) -> i32 {
444 pub fn mode(&self) -> i32 {
445 self.v1_mode()
445 self.v1_mode()
446 }
446 }
447
447
448 // TODO: return Option?
448 // TODO: return Option?
449 pub fn size(&self) -> i32 {
449 pub fn size(&self) -> i32 {
450 self.v1_size()
450 self.v1_size()
451 }
451 }
452
452
453 // TODO: return Option?
453 // TODO: return Option?
454 pub fn mtime(&self) -> i32 {
454 pub fn mtime(&self) -> i32 {
455 self.v1_mtime()
455 self.v1_mtime()
456 }
456 }
457
457
458 pub fn get_fallback_exec(&self) -> Option<bool> {
458 pub fn get_fallback_exec(&self) -> Option<bool> {
459 if self.flags.contains(Flags::HAS_FALLBACK_EXEC) {
459 if self.flags.contains(Flags::HAS_FALLBACK_EXEC) {
460 Some(self.flags.contains(Flags::FALLBACK_EXEC))
460 Some(self.flags.contains(Flags::FALLBACK_EXEC))
461 } else {
461 } else {
462 None
462 None
463 }
463 }
464 }
464 }
465
465
466 pub fn set_fallback_exec(&mut self, value: Option<bool>) {
466 pub fn set_fallback_exec(&mut self, value: Option<bool>) {
467 match value {
467 match value {
468 None => {
468 None => {
469 self.flags.remove(Flags::HAS_FALLBACK_EXEC);
469 self.flags.remove(Flags::HAS_FALLBACK_EXEC);
470 self.flags.remove(Flags::FALLBACK_EXEC);
470 self.flags.remove(Flags::FALLBACK_EXEC);
471 }
471 }
472 Some(exec) => {
472 Some(exec) => {
473 self.flags.insert(Flags::HAS_FALLBACK_EXEC);
473 self.flags.insert(Flags::HAS_FALLBACK_EXEC);
474 if exec {
474 if exec {
475 self.flags.insert(Flags::FALLBACK_EXEC);
475 self.flags.insert(Flags::FALLBACK_EXEC);
476 }
476 }
477 }
477 }
478 }
478 }
479 }
479 }
480
480
481 pub fn get_fallback_symlink(&self) -> Option<bool> {
481 pub fn get_fallback_symlink(&self) -> Option<bool> {
482 if self.flags.contains(Flags::HAS_FALLBACK_SYMLINK) {
482 if self.flags.contains(Flags::HAS_FALLBACK_SYMLINK) {
483 Some(self.flags.contains(Flags::FALLBACK_SYMLINK))
483 Some(self.flags.contains(Flags::FALLBACK_SYMLINK))
484 } else {
484 } else {
485 None
485 None
486 }
486 }
487 }
487 }
488
488
489 pub fn set_fallback_symlink(&mut self, value: Option<bool>) {
489 pub fn set_fallback_symlink(&mut self, value: Option<bool>) {
490 match value {
490 match value {
491 None => {
491 None => {
492 self.flags.remove(Flags::HAS_FALLBACK_SYMLINK);
492 self.flags.remove(Flags::HAS_FALLBACK_SYMLINK);
493 self.flags.remove(Flags::FALLBACK_SYMLINK);
493 self.flags.remove(Flags::FALLBACK_SYMLINK);
494 }
494 }
495 Some(symlink) => {
495 Some(symlink) => {
496 self.flags.insert(Flags::HAS_FALLBACK_SYMLINK);
496 self.flags.insert(Flags::HAS_FALLBACK_SYMLINK);
497 if symlink {
497 if symlink {
498 self.flags.insert(Flags::FALLBACK_SYMLINK);
498 self.flags.insert(Flags::FALLBACK_SYMLINK);
499 }
499 }
500 }
500 }
501 }
501 }
502 }
502 }
503
503
504 pub fn drop_merge_data(&mut self) {
504 pub fn drop_merge_data(&mut self) {
505 if self.flags.contains(Flags::P2_INFO) {
505 if self.flags.contains(Flags::P2_INFO) {
506 self.flags.remove(Flags::P2_INFO);
506 self.flags.remove(Flags::P2_INFO);
507 self.mode_size = None;
507 self.mode_size = None;
508 self.mtime = None;
508 self.mtime = None;
509 }
509 }
510 }
510 }
511
511
512 pub fn set_possibly_dirty(&mut self) {
512 pub fn set_possibly_dirty(&mut self) {
513 self.mtime = None
513 self.mtime = None
514 }
514 }
515
515
516 pub fn set_clean(&mut self, mode: u32, size: u32, mtime: u32) {
516 pub fn set_clean(&mut self, mode: u32, size: u32, mtime: u32) {
517 let size = size & RANGE_MASK_31BIT;
517 let size = size & RANGE_MASK_31BIT;
518 let mtime = mtime & RANGE_MASK_31BIT;
518 let mtime = mtime & RANGE_MASK_31BIT;
519 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
519 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
520 self.mode_size = Some((mode, size));
520 self.mode_size = Some((mode, size));
521 self.mtime = Some(mtime);
521 self.mtime = Some(mtime);
522 }
522 }
523
523
524 pub fn set_tracked(&mut self) {
524 pub fn set_tracked(&mut self) {
525 self.flags.insert(Flags::WDIR_TRACKED);
525 self.flags.insert(Flags::WDIR_TRACKED);
526 // `set_tracked` is replacing various `normallookup` call. So we mark
526 // `set_tracked` is replacing various `normallookup` call. So we mark
527 // the files as needing lookup
527 // the files as needing lookup
528 //
528 //
529 // Consider dropping this in the future in favor of something less
529 // Consider dropping this in the future in favor of something less
530 // broad.
530 // broad.
531 self.mtime = None;
531 self.mtime = None;
532 }
532 }
533
533
534 pub fn set_untracked(&mut self) {
534 pub fn set_untracked(&mut self) {
535 self.flags.remove(Flags::WDIR_TRACKED);
535 self.flags.remove(Flags::WDIR_TRACKED);
536 self.mode_size = None;
536 self.mode_size = None;
537 self.mtime = None;
537 self.mtime = None;
538 }
538 }
539
539
540 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
540 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
541 /// in the dirstate-v1 format.
541 /// in the dirstate-v1 format.
542 ///
542 ///
543 /// This includes marker values such as `mtime == -1`. In the future we may
543 /// This includes marker values such as `mtime == -1`. In the future we may
544 /// want to not represent these cases that way in memory, but serialization
544 /// want to not represent these cases that way in memory, but serialization
545 /// will need to keep the same format.
545 /// will need to keep the same format.
546 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
546 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
547 (
547 (
548 self.v1_state().into(),
548 self.v1_state().into(),
549 self.v1_mode(),
549 self.v1_mode(),
550 self.v1_size(),
550 self.v1_size(),
551 self.v1_mtime(),
551 self.v1_mtime(),
552 )
552 )
553 }
553 }
554
554
555 pub(crate) fn is_from_other_parent(&self) -> bool {
555 pub(crate) fn is_from_other_parent(&self) -> bool {
556 self.state() == EntryState::Normal
556 self.state() == EntryState::Normal
557 && self.size() == SIZE_FROM_OTHER_PARENT
557 && self.size() == SIZE_FROM_OTHER_PARENT
558 }
558 }
559
559
560 // TODO: other platforms
560 // TODO: other platforms
561 #[cfg(unix)]
561 #[cfg(unix)]
562 pub fn mode_changed(
562 pub fn mode_changed(
563 &self,
563 &self,
564 filesystem_metadata: &std::fs::Metadata,
564 filesystem_metadata: &std::fs::Metadata,
565 ) -> bool {
565 ) -> bool {
566 use std::os::unix::fs::MetadataExt;
566 use std::os::unix::fs::MetadataExt;
567 const EXEC_BIT_MASK: u32 = 0o100;
567 const EXEC_BIT_MASK: u32 = 0o100;
568 let dirstate_exec_bit = (self.mode() as u32) & EXEC_BIT_MASK;
568 let dirstate_exec_bit = (self.mode() as u32) & EXEC_BIT_MASK;
569 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
569 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
570 dirstate_exec_bit != fs_exec_bit
570 dirstate_exec_bit != fs_exec_bit
571 }
571 }
572
572
573 /// Returns a `(state, mode, size, mtime)` tuple as for
573 /// Returns a `(state, mode, size, mtime)` tuple as for
574 /// `DirstateMapMethods::debug_iter`.
574 /// `DirstateMapMethods::debug_iter`.
575 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
575 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
576 (self.state().into(), self.mode(), self.size(), self.mtime())
576 (self.state().into(), self.mode(), self.size(), self.mtime())
577 }
577 }
578
578
579 pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
579 /// True if the stored mtime would be ambiguous with the current time
580 pub fn need_delay(&self, now: i32) -> bool {
580 self.state() == EntryState::Normal && self.mtime() == now
581 self.state() == EntryState::Normal && self.mtime() == now
581 }
582 }
582
583
583 pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {
584 pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {
584 let ambiguous = self.mtime_is_ambiguous(now);
585 let ambiguous = self.need_delay(now);
585 if ambiguous {
586 if ambiguous {
586 // The file was last modified "simultaneously" with the current
587 // The file was last modified "simultaneously" with the current
587 // write to dirstate (i.e. within the same second for file-
588 // write to dirstate (i.e. within the same second for file-
588 // systems with a granularity of 1 sec). This commonly happens
589 // systems with a granularity of 1 sec). This commonly happens
589 // for at least a couple of files on 'update'.
590 // for at least a couple of files on 'update'.
590 // The user could change the file without changing its size
591 // The user could change the file without changing its size
591 // within the same second. Invalidate the file's mtime in
592 // within the same second. Invalidate the file's mtime in
592 // dirstate, forcing future 'status' calls to compare the
593 // dirstate, forcing future 'status' calls to compare the
593 // contents of the file if the size is the same. This prevents
594 // contents of the file if the size is the same. This prevents
594 // mistakenly treating such files as clean.
595 // mistakenly treating such files as clean.
595 self.set_possibly_dirty()
596 self.set_possibly_dirty()
596 }
597 }
597 ambiguous
598 ambiguous
598 }
599 }
599 }
600 }
600
601
601 impl EntryState {
602 impl EntryState {
602 pub fn is_tracked(self) -> bool {
603 pub fn is_tracked(self) -> bool {
603 use EntryState::*;
604 use EntryState::*;
604 match self {
605 match self {
605 Normal | Added | Merged => true,
606 Normal | Added | Merged => true,
606 Removed => false,
607 Removed => false,
607 }
608 }
608 }
609 }
609 }
610 }
610
611
611 impl TryFrom<u8> for EntryState {
612 impl TryFrom<u8> for EntryState {
612 type Error = HgError;
613 type Error = HgError;
613
614
614 fn try_from(value: u8) -> Result<Self, Self::Error> {
615 fn try_from(value: u8) -> Result<Self, Self::Error> {
615 match value {
616 match value {
616 b'n' => Ok(EntryState::Normal),
617 b'n' => Ok(EntryState::Normal),
617 b'a' => Ok(EntryState::Added),
618 b'a' => Ok(EntryState::Added),
618 b'r' => Ok(EntryState::Removed),
619 b'r' => Ok(EntryState::Removed),
619 b'm' => Ok(EntryState::Merged),
620 b'm' => Ok(EntryState::Merged),
620 _ => Err(HgError::CorruptedRepository(format!(
621 _ => Err(HgError::CorruptedRepository(format!(
621 "Incorrect dirstate entry state {}",
622 "Incorrect dirstate entry state {}",
622 value
623 value
623 ))),
624 ))),
624 }
625 }
625 }
626 }
626 }
627 }
627
628
628 impl Into<u8> for EntryState {
629 impl Into<u8> for EntryState {
629 fn into(self) -> u8 {
630 fn into(self) -> u8 {
630 match self {
631 match self {
631 EntryState::Normal => b'n',
632 EntryState::Normal => b'n',
632 EntryState::Added => b'a',
633 EntryState::Added => b'a',
633 EntryState::Removed => b'r',
634 EntryState::Removed => b'r',
634 EntryState::Merged => b'm',
635 EntryState::Merged => b'm',
635 }
636 }
636 }
637 }
637 }
638 }
@@ -1,1189 +1,1189 b''
1 use bytes_cast::BytesCast;
1 use bytes_cast::BytesCast;
2 use micro_timer::timed;
2 use micro_timer::timed;
3 use std::borrow::Cow;
3 use std::borrow::Cow;
4 use std::convert::TryInto;
4 use std::convert::TryInto;
5 use std::path::PathBuf;
5 use std::path::PathBuf;
6
6
7 use super::on_disk;
7 use super::on_disk;
8 use super::on_disk::DirstateV2ParseError;
8 use super::on_disk::DirstateV2ParseError;
9 use super::owning::OwningDirstateMap;
9 use super::owning::OwningDirstateMap;
10 use super::path_with_basename::WithBasename;
10 use super::path_with_basename::WithBasename;
11 use crate::dirstate::parsers::pack_entry;
11 use crate::dirstate::parsers::pack_entry;
12 use crate::dirstate::parsers::packed_entry_size;
12 use crate::dirstate::parsers::packed_entry_size;
13 use crate::dirstate::parsers::parse_dirstate_entries;
13 use crate::dirstate::parsers::parse_dirstate_entries;
14 use crate::dirstate::parsers::Timestamp;
14 use crate::dirstate::parsers::Timestamp;
15 use crate::dirstate::CopyMapIter;
15 use crate::dirstate::CopyMapIter;
16 use crate::dirstate::StateMapIter;
16 use crate::dirstate::StateMapIter;
17 use crate::dirstate::TruncatedTimestamp;
17 use crate::dirstate::TruncatedTimestamp;
18 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
18 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
19 use crate::dirstate::SIZE_NON_NORMAL;
19 use crate::dirstate::SIZE_NON_NORMAL;
20 use crate::matchers::Matcher;
20 use crate::matchers::Matcher;
21 use crate::utils::hg_path::{HgPath, HgPathBuf};
21 use crate::utils::hg_path::{HgPath, HgPathBuf};
22 use crate::DirstateEntry;
22 use crate::DirstateEntry;
23 use crate::DirstateError;
23 use crate::DirstateError;
24 use crate::DirstateParents;
24 use crate::DirstateParents;
25 use crate::DirstateStatus;
25 use crate::DirstateStatus;
26 use crate::EntryState;
26 use crate::EntryState;
27 use crate::FastHashMap;
27 use crate::FastHashMap;
28 use crate::PatternFileWarning;
28 use crate::PatternFileWarning;
29 use crate::StatusError;
29 use crate::StatusError;
30 use crate::StatusOptions;
30 use crate::StatusOptions;
31
31
32 /// Append to an existing data file if the amount of unreachable data (not used
32 /// Append to an existing data file if the amount of unreachable data (not used
33 /// anymore) is less than this fraction of the total amount of existing data.
33 /// anymore) is less than this fraction of the total amount of existing data.
34 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
34 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
35
35
36 pub struct DirstateMap<'on_disk> {
36 pub struct DirstateMap<'on_disk> {
37 /// Contents of the `.hg/dirstate` file
37 /// Contents of the `.hg/dirstate` file
38 pub(super) on_disk: &'on_disk [u8],
38 pub(super) on_disk: &'on_disk [u8],
39
39
40 pub(super) root: ChildNodes<'on_disk>,
40 pub(super) root: ChildNodes<'on_disk>,
41
41
42 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
42 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
43 pub(super) nodes_with_entry_count: u32,
43 pub(super) nodes_with_entry_count: u32,
44
44
45 /// Number of nodes anywhere in the tree that have
45 /// Number of nodes anywhere in the tree that have
46 /// `.copy_source.is_some()`.
46 /// `.copy_source.is_some()`.
47 pub(super) nodes_with_copy_source_count: u32,
47 pub(super) nodes_with_copy_source_count: u32,
48
48
49 /// See on_disk::Header
49 /// See on_disk::Header
50 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
50 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
51
51
52 /// How many bytes of `on_disk` are not used anymore
52 /// How many bytes of `on_disk` are not used anymore
53 pub(super) unreachable_bytes: u32,
53 pub(super) unreachable_bytes: u32,
54 }
54 }
55
55
56 /// Using a plain `HgPathBuf` of the full path from the repository root as a
56 /// Using a plain `HgPathBuf` of the full path from the repository root as a
57 /// map key would also work: all paths in a given map have the same parent
57 /// map key would also work: all paths in a given map have the same parent
58 /// path, so comparing full paths gives the same result as comparing base
58 /// path, so comparing full paths gives the same result as comparing base
59 /// names. However `HashMap` would waste time always re-hashing the same
59 /// names. However `HashMap` would waste time always re-hashing the same
60 /// string prefix.
60 /// string prefix.
61 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
61 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
62
62
63 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
63 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
64 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
64 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
65 pub(super) enum BorrowedPath<'tree, 'on_disk> {
65 pub(super) enum BorrowedPath<'tree, 'on_disk> {
66 InMemory(&'tree HgPathBuf),
66 InMemory(&'tree HgPathBuf),
67 OnDisk(&'on_disk HgPath),
67 OnDisk(&'on_disk HgPath),
68 }
68 }
69
69
70 pub(super) enum ChildNodes<'on_disk> {
70 pub(super) enum ChildNodes<'on_disk> {
71 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
71 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
72 OnDisk(&'on_disk [on_disk::Node]),
72 OnDisk(&'on_disk [on_disk::Node]),
73 }
73 }
74
74
75 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
75 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
76 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
76 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
77 OnDisk(&'on_disk [on_disk::Node]),
77 OnDisk(&'on_disk [on_disk::Node]),
78 }
78 }
79
79
80 pub(super) enum NodeRef<'tree, 'on_disk> {
80 pub(super) enum NodeRef<'tree, 'on_disk> {
81 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
81 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
82 OnDisk(&'on_disk on_disk::Node),
82 OnDisk(&'on_disk on_disk::Node),
83 }
83 }
84
84
85 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
85 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
86 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
86 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
87 match *self {
87 match *self {
88 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
88 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
89 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
89 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
90 }
90 }
91 }
91 }
92 }
92 }
93
93
94 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
94 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
95 type Target = HgPath;
95 type Target = HgPath;
96
96
97 fn deref(&self) -> &HgPath {
97 fn deref(&self) -> &HgPath {
98 match *self {
98 match *self {
99 BorrowedPath::InMemory(in_memory) => in_memory,
99 BorrowedPath::InMemory(in_memory) => in_memory,
100 BorrowedPath::OnDisk(on_disk) => on_disk,
100 BorrowedPath::OnDisk(on_disk) => on_disk,
101 }
101 }
102 }
102 }
103 }
103 }
104
104
105 impl Default for ChildNodes<'_> {
105 impl Default for ChildNodes<'_> {
106 fn default() -> Self {
106 fn default() -> Self {
107 ChildNodes::InMemory(Default::default())
107 ChildNodes::InMemory(Default::default())
108 }
108 }
109 }
109 }
110
110
111 impl<'on_disk> ChildNodes<'on_disk> {
111 impl<'on_disk> ChildNodes<'on_disk> {
112 pub(super) fn as_ref<'tree>(
112 pub(super) fn as_ref<'tree>(
113 &'tree self,
113 &'tree self,
114 ) -> ChildNodesRef<'tree, 'on_disk> {
114 ) -> ChildNodesRef<'tree, 'on_disk> {
115 match self {
115 match self {
116 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
116 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
117 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
117 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
118 }
118 }
119 }
119 }
120
120
121 pub(super) fn is_empty(&self) -> bool {
121 pub(super) fn is_empty(&self) -> bool {
122 match self {
122 match self {
123 ChildNodes::InMemory(nodes) => nodes.is_empty(),
123 ChildNodes::InMemory(nodes) => nodes.is_empty(),
124 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
124 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
125 }
125 }
126 }
126 }
127
127
128 fn make_mut(
128 fn make_mut(
129 &mut self,
129 &mut self,
130 on_disk: &'on_disk [u8],
130 on_disk: &'on_disk [u8],
131 unreachable_bytes: &mut u32,
131 unreachable_bytes: &mut u32,
132 ) -> Result<
132 ) -> Result<
133 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
133 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
134 DirstateV2ParseError,
134 DirstateV2ParseError,
135 > {
135 > {
136 match self {
136 match self {
137 ChildNodes::InMemory(nodes) => Ok(nodes),
137 ChildNodes::InMemory(nodes) => Ok(nodes),
138 ChildNodes::OnDisk(nodes) => {
138 ChildNodes::OnDisk(nodes) => {
139 *unreachable_bytes +=
139 *unreachable_bytes +=
140 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
140 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
141 let nodes = nodes
141 let nodes = nodes
142 .iter()
142 .iter()
143 .map(|node| {
143 .map(|node| {
144 Ok((
144 Ok((
145 node.path(on_disk)?,
145 node.path(on_disk)?,
146 node.to_in_memory_node(on_disk)?,
146 node.to_in_memory_node(on_disk)?,
147 ))
147 ))
148 })
148 })
149 .collect::<Result<_, _>>()?;
149 .collect::<Result<_, _>>()?;
150 *self = ChildNodes::InMemory(nodes);
150 *self = ChildNodes::InMemory(nodes);
151 match self {
151 match self {
152 ChildNodes::InMemory(nodes) => Ok(nodes),
152 ChildNodes::InMemory(nodes) => Ok(nodes),
153 ChildNodes::OnDisk(_) => unreachable!(),
153 ChildNodes::OnDisk(_) => unreachable!(),
154 }
154 }
155 }
155 }
156 }
156 }
157 }
157 }
158 }
158 }
159
159
160 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
160 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
161 pub(super) fn get(
161 pub(super) fn get(
162 &self,
162 &self,
163 base_name: &HgPath,
163 base_name: &HgPath,
164 on_disk: &'on_disk [u8],
164 on_disk: &'on_disk [u8],
165 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
165 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
166 match self {
166 match self {
167 ChildNodesRef::InMemory(nodes) => Ok(nodes
167 ChildNodesRef::InMemory(nodes) => Ok(nodes
168 .get_key_value(base_name)
168 .get_key_value(base_name)
169 .map(|(k, v)| NodeRef::InMemory(k, v))),
169 .map(|(k, v)| NodeRef::InMemory(k, v))),
170 ChildNodesRef::OnDisk(nodes) => {
170 ChildNodesRef::OnDisk(nodes) => {
171 let mut parse_result = Ok(());
171 let mut parse_result = Ok(());
172 let search_result = nodes.binary_search_by(|node| {
172 let search_result = nodes.binary_search_by(|node| {
173 match node.base_name(on_disk) {
173 match node.base_name(on_disk) {
174 Ok(node_base_name) => node_base_name.cmp(base_name),
174 Ok(node_base_name) => node_base_name.cmp(base_name),
175 Err(e) => {
175 Err(e) => {
176 parse_result = Err(e);
176 parse_result = Err(e);
177 // Dummy comparison result, `search_result` won’t
177 // Dummy comparison result, `search_result` won’t
178 // be used since `parse_result` is an error
178 // be used since `parse_result` is an error
179 std::cmp::Ordering::Equal
179 std::cmp::Ordering::Equal
180 }
180 }
181 }
181 }
182 });
182 });
183 parse_result.map(|()| {
183 parse_result.map(|()| {
184 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
184 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
185 })
185 })
186 }
186 }
187 }
187 }
188 }
188 }
189
189
190 /// Iterate in undefined order
190 /// Iterate in undefined order
191 pub(super) fn iter(
191 pub(super) fn iter(
192 &self,
192 &self,
193 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
193 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
194 match self {
194 match self {
195 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
195 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
196 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
196 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
197 ),
197 ),
198 ChildNodesRef::OnDisk(nodes) => {
198 ChildNodesRef::OnDisk(nodes) => {
199 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
199 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
200 }
200 }
201 }
201 }
202 }
202 }
203
203
204 /// Iterate in parallel in undefined order
204 /// Iterate in parallel in undefined order
205 pub(super) fn par_iter(
205 pub(super) fn par_iter(
206 &self,
206 &self,
207 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
207 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
208 {
208 {
209 use rayon::prelude::*;
209 use rayon::prelude::*;
210 match self {
210 match self {
211 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
211 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
212 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
212 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
213 ),
213 ),
214 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
214 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
215 nodes.par_iter().map(NodeRef::OnDisk),
215 nodes.par_iter().map(NodeRef::OnDisk),
216 ),
216 ),
217 }
217 }
218 }
218 }
219
219
220 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
220 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
221 match self {
221 match self {
222 ChildNodesRef::InMemory(nodes) => {
222 ChildNodesRef::InMemory(nodes) => {
223 let mut vec: Vec<_> = nodes
223 let mut vec: Vec<_> = nodes
224 .iter()
224 .iter()
225 .map(|(k, v)| NodeRef::InMemory(k, v))
225 .map(|(k, v)| NodeRef::InMemory(k, v))
226 .collect();
226 .collect();
227 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
227 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
228 match node {
228 match node {
229 NodeRef::InMemory(path, _node) => path.base_name(),
229 NodeRef::InMemory(path, _node) => path.base_name(),
230 NodeRef::OnDisk(_) => unreachable!(),
230 NodeRef::OnDisk(_) => unreachable!(),
231 }
231 }
232 }
232 }
233 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
233 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
234 // value: https://github.com/rust-lang/rust/issues/34162
234 // value: https://github.com/rust-lang/rust/issues/34162
235 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
235 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
236 vec
236 vec
237 }
237 }
238 ChildNodesRef::OnDisk(nodes) => {
238 ChildNodesRef::OnDisk(nodes) => {
239 // Nodes on disk are already sorted
239 // Nodes on disk are already sorted
240 nodes.iter().map(NodeRef::OnDisk).collect()
240 nodes.iter().map(NodeRef::OnDisk).collect()
241 }
241 }
242 }
242 }
243 }
243 }
244 }
244 }
245
245
246 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
246 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
247 pub(super) fn full_path(
247 pub(super) fn full_path(
248 &self,
248 &self,
249 on_disk: &'on_disk [u8],
249 on_disk: &'on_disk [u8],
250 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
250 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
251 match self {
251 match self {
252 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
252 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
253 NodeRef::OnDisk(node) => node.full_path(on_disk),
253 NodeRef::OnDisk(node) => node.full_path(on_disk),
254 }
254 }
255 }
255 }
256
256
257 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
257 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
258 /// HgPath>` detached from `'tree`
258 /// HgPath>` detached from `'tree`
259 pub(super) fn full_path_borrowed(
259 pub(super) fn full_path_borrowed(
260 &self,
260 &self,
261 on_disk: &'on_disk [u8],
261 on_disk: &'on_disk [u8],
262 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
262 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
263 match self {
263 match self {
264 NodeRef::InMemory(path, _node) => match path.full_path() {
264 NodeRef::InMemory(path, _node) => match path.full_path() {
265 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
265 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
266 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
266 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
267 },
267 },
268 NodeRef::OnDisk(node) => {
268 NodeRef::OnDisk(node) => {
269 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
269 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
270 }
270 }
271 }
271 }
272 }
272 }
273
273
274 pub(super) fn base_name(
274 pub(super) fn base_name(
275 &self,
275 &self,
276 on_disk: &'on_disk [u8],
276 on_disk: &'on_disk [u8],
277 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
277 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
278 match self {
278 match self {
279 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
279 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
280 NodeRef::OnDisk(node) => node.base_name(on_disk),
280 NodeRef::OnDisk(node) => node.base_name(on_disk),
281 }
281 }
282 }
282 }
283
283
284 pub(super) fn children(
284 pub(super) fn children(
285 &self,
285 &self,
286 on_disk: &'on_disk [u8],
286 on_disk: &'on_disk [u8],
287 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
287 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
288 match self {
288 match self {
289 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
289 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
290 NodeRef::OnDisk(node) => {
290 NodeRef::OnDisk(node) => {
291 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
291 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
292 }
292 }
293 }
293 }
294 }
294 }
295
295
296 pub(super) fn has_copy_source(&self) -> bool {
296 pub(super) fn has_copy_source(&self) -> bool {
297 match self {
297 match self {
298 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
298 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
299 NodeRef::OnDisk(node) => node.has_copy_source(),
299 NodeRef::OnDisk(node) => node.has_copy_source(),
300 }
300 }
301 }
301 }
302
302
303 pub(super) fn copy_source(
303 pub(super) fn copy_source(
304 &self,
304 &self,
305 on_disk: &'on_disk [u8],
305 on_disk: &'on_disk [u8],
306 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
306 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
307 match self {
307 match self {
308 NodeRef::InMemory(_path, node) => {
308 NodeRef::InMemory(_path, node) => {
309 Ok(node.copy_source.as_ref().map(|s| &**s))
309 Ok(node.copy_source.as_ref().map(|s| &**s))
310 }
310 }
311 NodeRef::OnDisk(node) => node.copy_source(on_disk),
311 NodeRef::OnDisk(node) => node.copy_source(on_disk),
312 }
312 }
313 }
313 }
314
314
315 pub(super) fn entry(
315 pub(super) fn entry(
316 &self,
316 &self,
317 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
317 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
318 match self {
318 match self {
319 NodeRef::InMemory(_path, node) => {
319 NodeRef::InMemory(_path, node) => {
320 Ok(node.data.as_entry().copied())
320 Ok(node.data.as_entry().copied())
321 }
321 }
322 NodeRef::OnDisk(node) => node.entry(),
322 NodeRef::OnDisk(node) => node.entry(),
323 }
323 }
324 }
324 }
325
325
326 pub(super) fn state(
326 pub(super) fn state(
327 &self,
327 &self,
328 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
328 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
329 Ok(self.entry()?.map(|e| e.state()))
329 Ok(self.entry()?.map(|e| e.state()))
330 }
330 }
331
331
332 pub(super) fn cached_directory_mtime(
332 pub(super) fn cached_directory_mtime(
333 &self,
333 &self,
334 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
334 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
335 match self {
335 match self {
336 NodeRef::InMemory(_path, node) => Ok(match node.data {
336 NodeRef::InMemory(_path, node) => Ok(match node.data {
337 NodeData::CachedDirectory { mtime } => Some(mtime),
337 NodeData::CachedDirectory { mtime } => Some(mtime),
338 _ => None,
338 _ => None,
339 }),
339 }),
340 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
340 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
341 }
341 }
342 }
342 }
343
343
344 pub(super) fn descendants_with_entry_count(&self) -> u32 {
344 pub(super) fn descendants_with_entry_count(&self) -> u32 {
345 match self {
345 match self {
346 NodeRef::InMemory(_path, node) => {
346 NodeRef::InMemory(_path, node) => {
347 node.descendants_with_entry_count
347 node.descendants_with_entry_count
348 }
348 }
349 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
349 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
350 }
350 }
351 }
351 }
352
352
353 pub(super) fn tracked_descendants_count(&self) -> u32 {
353 pub(super) fn tracked_descendants_count(&self) -> u32 {
354 match self {
354 match self {
355 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
355 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
356 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
356 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
357 }
357 }
358 }
358 }
359 }
359 }
360
360
361 /// Represents a file or a directory
361 /// Represents a file or a directory
362 #[derive(Default)]
362 #[derive(Default)]
363 pub(super) struct Node<'on_disk> {
363 pub(super) struct Node<'on_disk> {
364 pub(super) data: NodeData,
364 pub(super) data: NodeData,
365
365
366 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
366 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
367
367
368 pub(super) children: ChildNodes<'on_disk>,
368 pub(super) children: ChildNodes<'on_disk>,
369
369
370 /// How many (non-inclusive) descendants of this node have an entry.
370 /// How many (non-inclusive) descendants of this node have an entry.
371 pub(super) descendants_with_entry_count: u32,
371 pub(super) descendants_with_entry_count: u32,
372
372
373 /// How many (non-inclusive) descendants of this node have an entry whose
373 /// How many (non-inclusive) descendants of this node have an entry whose
374 /// state is "tracked".
374 /// state is "tracked".
375 pub(super) tracked_descendants_count: u32,
375 pub(super) tracked_descendants_count: u32,
376 }
376 }
377
377
378 pub(super) enum NodeData {
378 pub(super) enum NodeData {
379 Entry(DirstateEntry),
379 Entry(DirstateEntry),
380 CachedDirectory { mtime: TruncatedTimestamp },
380 CachedDirectory { mtime: TruncatedTimestamp },
381 None,
381 None,
382 }
382 }
383
383
384 impl Default for NodeData {
384 impl Default for NodeData {
385 fn default() -> Self {
385 fn default() -> Self {
386 NodeData::None
386 NodeData::None
387 }
387 }
388 }
388 }
389
389
390 impl NodeData {
390 impl NodeData {
391 fn has_entry(&self) -> bool {
391 fn has_entry(&self) -> bool {
392 match self {
392 match self {
393 NodeData::Entry(_) => true,
393 NodeData::Entry(_) => true,
394 _ => false,
394 _ => false,
395 }
395 }
396 }
396 }
397
397
398 fn as_entry(&self) -> Option<&DirstateEntry> {
398 fn as_entry(&self) -> Option<&DirstateEntry> {
399 match self {
399 match self {
400 NodeData::Entry(entry) => Some(entry),
400 NodeData::Entry(entry) => Some(entry),
401 _ => None,
401 _ => None,
402 }
402 }
403 }
403 }
404 }
404 }
405
405
406 impl<'on_disk> DirstateMap<'on_disk> {
406 impl<'on_disk> DirstateMap<'on_disk> {
407 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
407 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
408 Self {
408 Self {
409 on_disk,
409 on_disk,
410 root: ChildNodes::default(),
410 root: ChildNodes::default(),
411 nodes_with_entry_count: 0,
411 nodes_with_entry_count: 0,
412 nodes_with_copy_source_count: 0,
412 nodes_with_copy_source_count: 0,
413 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
413 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
414 unreachable_bytes: 0,
414 unreachable_bytes: 0,
415 }
415 }
416 }
416 }
417
417
418 #[timed]
418 #[timed]
419 pub fn new_v2(
419 pub fn new_v2(
420 on_disk: &'on_disk [u8],
420 on_disk: &'on_disk [u8],
421 data_size: usize,
421 data_size: usize,
422 metadata: &[u8],
422 metadata: &[u8],
423 ) -> Result<Self, DirstateError> {
423 ) -> Result<Self, DirstateError> {
424 if let Some(data) = on_disk.get(..data_size) {
424 if let Some(data) = on_disk.get(..data_size) {
425 Ok(on_disk::read(data, metadata)?)
425 Ok(on_disk::read(data, metadata)?)
426 } else {
426 } else {
427 Err(DirstateV2ParseError.into())
427 Err(DirstateV2ParseError.into())
428 }
428 }
429 }
429 }
430
430
431 #[timed]
431 #[timed]
432 pub fn new_v1(
432 pub fn new_v1(
433 on_disk: &'on_disk [u8],
433 on_disk: &'on_disk [u8],
434 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
434 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
435 let mut map = Self::empty(on_disk);
435 let mut map = Self::empty(on_disk);
436 if map.on_disk.is_empty() {
436 if map.on_disk.is_empty() {
437 return Ok((map, None));
437 return Ok((map, None));
438 }
438 }
439
439
440 let parents = parse_dirstate_entries(
440 let parents = parse_dirstate_entries(
441 map.on_disk,
441 map.on_disk,
442 |path, entry, copy_source| {
442 |path, entry, copy_source| {
443 let tracked = entry.state().is_tracked();
443 let tracked = entry.state().is_tracked();
444 let node = Self::get_or_insert_node(
444 let node = Self::get_or_insert_node(
445 map.on_disk,
445 map.on_disk,
446 &mut map.unreachable_bytes,
446 &mut map.unreachable_bytes,
447 &mut map.root,
447 &mut map.root,
448 path,
448 path,
449 WithBasename::to_cow_borrowed,
449 WithBasename::to_cow_borrowed,
450 |ancestor| {
450 |ancestor| {
451 if tracked {
451 if tracked {
452 ancestor.tracked_descendants_count += 1
452 ancestor.tracked_descendants_count += 1
453 }
453 }
454 ancestor.descendants_with_entry_count += 1
454 ancestor.descendants_with_entry_count += 1
455 },
455 },
456 )?;
456 )?;
457 assert!(
457 assert!(
458 !node.data.has_entry(),
458 !node.data.has_entry(),
459 "duplicate dirstate entry in read"
459 "duplicate dirstate entry in read"
460 );
460 );
461 assert!(
461 assert!(
462 node.copy_source.is_none(),
462 node.copy_source.is_none(),
463 "duplicate dirstate entry in read"
463 "duplicate dirstate entry in read"
464 );
464 );
465 node.data = NodeData::Entry(*entry);
465 node.data = NodeData::Entry(*entry);
466 node.copy_source = copy_source.map(Cow::Borrowed);
466 node.copy_source = copy_source.map(Cow::Borrowed);
467 map.nodes_with_entry_count += 1;
467 map.nodes_with_entry_count += 1;
468 if copy_source.is_some() {
468 if copy_source.is_some() {
469 map.nodes_with_copy_source_count += 1
469 map.nodes_with_copy_source_count += 1
470 }
470 }
471 Ok(())
471 Ok(())
472 },
472 },
473 )?;
473 )?;
474 let parents = Some(parents.clone());
474 let parents = Some(parents.clone());
475
475
476 Ok((map, parents))
476 Ok((map, parents))
477 }
477 }
478
478
479 /// Assuming dirstate-v2 format, returns whether the next write should
479 /// Assuming dirstate-v2 format, returns whether the next write should
480 /// append to the existing data file that contains `self.on_disk` (true),
480 /// append to the existing data file that contains `self.on_disk` (true),
481 /// or create a new data file from scratch (false).
481 /// or create a new data file from scratch (false).
482 pub(super) fn write_should_append(&self) -> bool {
482 pub(super) fn write_should_append(&self) -> bool {
483 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
483 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
484 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
484 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
485 }
485 }
486
486
487 fn get_node<'tree>(
487 fn get_node<'tree>(
488 &'tree self,
488 &'tree self,
489 path: &HgPath,
489 path: &HgPath,
490 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
490 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
491 let mut children = self.root.as_ref();
491 let mut children = self.root.as_ref();
492 let mut components = path.components();
492 let mut components = path.components();
493 let mut component =
493 let mut component =
494 components.next().expect("expected at least one components");
494 components.next().expect("expected at least one components");
495 loop {
495 loop {
496 if let Some(child) = children.get(component, self.on_disk)? {
496 if let Some(child) = children.get(component, self.on_disk)? {
497 if let Some(next_component) = components.next() {
497 if let Some(next_component) = components.next() {
498 component = next_component;
498 component = next_component;
499 children = child.children(self.on_disk)?;
499 children = child.children(self.on_disk)?;
500 } else {
500 } else {
501 return Ok(Some(child));
501 return Ok(Some(child));
502 }
502 }
503 } else {
503 } else {
504 return Ok(None);
504 return Ok(None);
505 }
505 }
506 }
506 }
507 }
507 }
508
508
509 /// Returns a mutable reference to the node at `path` if it exists
509 /// Returns a mutable reference to the node at `path` if it exists
510 ///
510 ///
511 /// This takes `root` instead of `&mut self` so that callers can mutate
511 /// This takes `root` instead of `&mut self` so that callers can mutate
512 /// other fields while the returned borrow is still valid
512 /// other fields while the returned borrow is still valid
513 fn get_node_mut<'tree>(
513 fn get_node_mut<'tree>(
514 on_disk: &'on_disk [u8],
514 on_disk: &'on_disk [u8],
515 unreachable_bytes: &mut u32,
515 unreachable_bytes: &mut u32,
516 root: &'tree mut ChildNodes<'on_disk>,
516 root: &'tree mut ChildNodes<'on_disk>,
517 path: &HgPath,
517 path: &HgPath,
518 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
518 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
519 let mut children = root;
519 let mut children = root;
520 let mut components = path.components();
520 let mut components = path.components();
521 let mut component =
521 let mut component =
522 components.next().expect("expected at least one components");
522 components.next().expect("expected at least one components");
523 loop {
523 loop {
524 if let Some(child) = children
524 if let Some(child) = children
525 .make_mut(on_disk, unreachable_bytes)?
525 .make_mut(on_disk, unreachable_bytes)?
526 .get_mut(component)
526 .get_mut(component)
527 {
527 {
528 if let Some(next_component) = components.next() {
528 if let Some(next_component) = components.next() {
529 component = next_component;
529 component = next_component;
530 children = &mut child.children;
530 children = &mut child.children;
531 } else {
531 } else {
532 return Ok(Some(child));
532 return Ok(Some(child));
533 }
533 }
534 } else {
534 } else {
535 return Ok(None);
535 return Ok(None);
536 }
536 }
537 }
537 }
538 }
538 }
539
539
540 pub(super) fn get_or_insert<'tree, 'path>(
540 pub(super) fn get_or_insert<'tree, 'path>(
541 &'tree mut self,
541 &'tree mut self,
542 path: &HgPath,
542 path: &HgPath,
543 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
543 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
544 Self::get_or_insert_node(
544 Self::get_or_insert_node(
545 self.on_disk,
545 self.on_disk,
546 &mut self.unreachable_bytes,
546 &mut self.unreachable_bytes,
547 &mut self.root,
547 &mut self.root,
548 path,
548 path,
549 WithBasename::to_cow_owned,
549 WithBasename::to_cow_owned,
550 |_| {},
550 |_| {},
551 )
551 )
552 }
552 }
553
553
554 fn get_or_insert_node<'tree, 'path>(
554 fn get_or_insert_node<'tree, 'path>(
555 on_disk: &'on_disk [u8],
555 on_disk: &'on_disk [u8],
556 unreachable_bytes: &mut u32,
556 unreachable_bytes: &mut u32,
557 root: &'tree mut ChildNodes<'on_disk>,
557 root: &'tree mut ChildNodes<'on_disk>,
558 path: &'path HgPath,
558 path: &'path HgPath,
559 to_cow: impl Fn(
559 to_cow: impl Fn(
560 WithBasename<&'path HgPath>,
560 WithBasename<&'path HgPath>,
561 ) -> WithBasename<Cow<'on_disk, HgPath>>,
561 ) -> WithBasename<Cow<'on_disk, HgPath>>,
562 mut each_ancestor: impl FnMut(&mut Node),
562 mut each_ancestor: impl FnMut(&mut Node),
563 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
563 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
564 let mut child_nodes = root;
564 let mut child_nodes = root;
565 let mut inclusive_ancestor_paths =
565 let mut inclusive_ancestor_paths =
566 WithBasename::inclusive_ancestors_of(path);
566 WithBasename::inclusive_ancestors_of(path);
567 let mut ancestor_path = inclusive_ancestor_paths
567 let mut ancestor_path = inclusive_ancestor_paths
568 .next()
568 .next()
569 .expect("expected at least one inclusive ancestor");
569 .expect("expected at least one inclusive ancestor");
570 loop {
570 loop {
571 // TODO: can we avoid allocating an owned key in cases where the
571 // TODO: can we avoid allocating an owned key in cases where the
572 // map already contains that key, without introducing double
572 // map already contains that key, without introducing double
573 // lookup?
573 // lookup?
574 let child_node = child_nodes
574 let child_node = child_nodes
575 .make_mut(on_disk, unreachable_bytes)?
575 .make_mut(on_disk, unreachable_bytes)?
576 .entry(to_cow(ancestor_path))
576 .entry(to_cow(ancestor_path))
577 .or_default();
577 .or_default();
578 if let Some(next) = inclusive_ancestor_paths.next() {
578 if let Some(next) = inclusive_ancestor_paths.next() {
579 each_ancestor(child_node);
579 each_ancestor(child_node);
580 ancestor_path = next;
580 ancestor_path = next;
581 child_nodes = &mut child_node.children;
581 child_nodes = &mut child_node.children;
582 } else {
582 } else {
583 return Ok(child_node);
583 return Ok(child_node);
584 }
584 }
585 }
585 }
586 }
586 }
587
587
588 fn add_or_remove_file(
588 fn add_or_remove_file(
589 &mut self,
589 &mut self,
590 path: &HgPath,
590 path: &HgPath,
591 old_state: Option<EntryState>,
591 old_state: Option<EntryState>,
592 new_entry: DirstateEntry,
592 new_entry: DirstateEntry,
593 ) -> Result<(), DirstateV2ParseError> {
593 ) -> Result<(), DirstateV2ParseError> {
594 let had_entry = old_state.is_some();
594 let had_entry = old_state.is_some();
595 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
595 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
596 let tracked_count_increment =
596 let tracked_count_increment =
597 match (was_tracked, new_entry.state().is_tracked()) {
597 match (was_tracked, new_entry.state().is_tracked()) {
598 (false, true) => 1,
598 (false, true) => 1,
599 (true, false) => -1,
599 (true, false) => -1,
600 _ => 0,
600 _ => 0,
601 };
601 };
602
602
603 let node = Self::get_or_insert_node(
603 let node = Self::get_or_insert_node(
604 self.on_disk,
604 self.on_disk,
605 &mut self.unreachable_bytes,
605 &mut self.unreachable_bytes,
606 &mut self.root,
606 &mut self.root,
607 path,
607 path,
608 WithBasename::to_cow_owned,
608 WithBasename::to_cow_owned,
609 |ancestor| {
609 |ancestor| {
610 if !had_entry {
610 if !had_entry {
611 ancestor.descendants_with_entry_count += 1;
611 ancestor.descendants_with_entry_count += 1;
612 }
612 }
613
613
614 // We can’t use `+= increment` because the counter is unsigned,
614 // We can’t use `+= increment` because the counter is unsigned,
615 // and we want debug builds to detect accidental underflow
615 // and we want debug builds to detect accidental underflow
616 // through zero
616 // through zero
617 match tracked_count_increment {
617 match tracked_count_increment {
618 1 => ancestor.tracked_descendants_count += 1,
618 1 => ancestor.tracked_descendants_count += 1,
619 -1 => ancestor.tracked_descendants_count -= 1,
619 -1 => ancestor.tracked_descendants_count -= 1,
620 _ => {}
620 _ => {}
621 }
621 }
622 },
622 },
623 )?;
623 )?;
624 if !had_entry {
624 if !had_entry {
625 self.nodes_with_entry_count += 1
625 self.nodes_with_entry_count += 1
626 }
626 }
627 node.data = NodeData::Entry(new_entry);
627 node.data = NodeData::Entry(new_entry);
628 Ok(())
628 Ok(())
629 }
629 }
630
630
631 fn iter_nodes<'tree>(
631 fn iter_nodes<'tree>(
632 &'tree self,
632 &'tree self,
633 ) -> impl Iterator<
633 ) -> impl Iterator<
634 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
634 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
635 > + 'tree {
635 > + 'tree {
636 // Depth first tree traversal.
636 // Depth first tree traversal.
637 //
637 //
638 // If we could afford internal iteration and recursion,
638 // If we could afford internal iteration and recursion,
639 // this would look like:
639 // this would look like:
640 //
640 //
641 // ```
641 // ```
642 // fn traverse_children(
642 // fn traverse_children(
643 // children: &ChildNodes,
643 // children: &ChildNodes,
644 // each: &mut impl FnMut(&Node),
644 // each: &mut impl FnMut(&Node),
645 // ) {
645 // ) {
646 // for child in children.values() {
646 // for child in children.values() {
647 // traverse_children(&child.children, each);
647 // traverse_children(&child.children, each);
648 // each(child);
648 // each(child);
649 // }
649 // }
650 // }
650 // }
651 // ```
651 // ```
652 //
652 //
653 // However we want an external iterator and therefore can’t use the
653 // However we want an external iterator and therefore can’t use the
654 // call stack. Use an explicit stack instead:
654 // call stack. Use an explicit stack instead:
655 let mut stack = Vec::new();
655 let mut stack = Vec::new();
656 let mut iter = self.root.as_ref().iter();
656 let mut iter = self.root.as_ref().iter();
657 std::iter::from_fn(move || {
657 std::iter::from_fn(move || {
658 while let Some(child_node) = iter.next() {
658 while let Some(child_node) = iter.next() {
659 let children = match child_node.children(self.on_disk) {
659 let children = match child_node.children(self.on_disk) {
660 Ok(children) => children,
660 Ok(children) => children,
661 Err(error) => return Some(Err(error)),
661 Err(error) => return Some(Err(error)),
662 };
662 };
663 // Pseudo-recursion
663 // Pseudo-recursion
664 let new_iter = children.iter();
664 let new_iter = children.iter();
665 let old_iter = std::mem::replace(&mut iter, new_iter);
665 let old_iter = std::mem::replace(&mut iter, new_iter);
666 stack.push((child_node, old_iter));
666 stack.push((child_node, old_iter));
667 }
667 }
668 // Found the end of a `children.iter()` iterator.
668 // Found the end of a `children.iter()` iterator.
669 if let Some((child_node, next_iter)) = stack.pop() {
669 if let Some((child_node, next_iter)) = stack.pop() {
670 // "Return" from pseudo-recursion by restoring state from the
670 // "Return" from pseudo-recursion by restoring state from the
671 // explicit stack
671 // explicit stack
672 iter = next_iter;
672 iter = next_iter;
673
673
674 Some(Ok(child_node))
674 Some(Ok(child_node))
675 } else {
675 } else {
676 // Reached the bottom of the stack, we’re done
676 // Reached the bottom of the stack, we’re done
677 None
677 None
678 }
678 }
679 })
679 })
680 }
680 }
681
681
682 fn clear_known_ambiguous_mtimes(
682 fn clear_known_ambiguous_mtimes(
683 &mut self,
683 &mut self,
684 paths: &[impl AsRef<HgPath>],
684 paths: &[impl AsRef<HgPath>],
685 ) -> Result<(), DirstateV2ParseError> {
685 ) -> Result<(), DirstateV2ParseError> {
686 for path in paths {
686 for path in paths {
687 if let Some(node) = Self::get_node_mut(
687 if let Some(node) = Self::get_node_mut(
688 self.on_disk,
688 self.on_disk,
689 &mut self.unreachable_bytes,
689 &mut self.unreachable_bytes,
690 &mut self.root,
690 &mut self.root,
691 path.as_ref(),
691 path.as_ref(),
692 )? {
692 )? {
693 if let NodeData::Entry(entry) = &mut node.data {
693 if let NodeData::Entry(entry) = &mut node.data {
694 entry.set_possibly_dirty();
694 entry.set_possibly_dirty();
695 }
695 }
696 }
696 }
697 }
697 }
698 Ok(())
698 Ok(())
699 }
699 }
700
700
701 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
701 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
702 if let Cow::Borrowed(path) = path {
702 if let Cow::Borrowed(path) = path {
703 *unreachable_bytes += path.len() as u32
703 *unreachable_bytes += path.len() as u32
704 }
704 }
705 }
705 }
706 }
706 }
707
707
708 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
708 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
709 ///
709 ///
710 /// The callback is only called for incoming `Ok` values. Errors are passed
710 /// The callback is only called for incoming `Ok` values. Errors are passed
711 /// through as-is. In order to let it use the `?` operator the callback is
711 /// through as-is. In order to let it use the `?` operator the callback is
712 /// expected to return a `Result` of `Option`, instead of an `Option` of
712 /// expected to return a `Result` of `Option`, instead of an `Option` of
713 /// `Result`.
713 /// `Result`.
714 fn filter_map_results<'a, I, F, A, B, E>(
714 fn filter_map_results<'a, I, F, A, B, E>(
715 iter: I,
715 iter: I,
716 f: F,
716 f: F,
717 ) -> impl Iterator<Item = Result<B, E>> + 'a
717 ) -> impl Iterator<Item = Result<B, E>> + 'a
718 where
718 where
719 I: Iterator<Item = Result<A, E>> + 'a,
719 I: Iterator<Item = Result<A, E>> + 'a,
720 F: Fn(A) -> Result<Option<B>, E> + 'a,
720 F: Fn(A) -> Result<Option<B>, E> + 'a,
721 {
721 {
722 iter.filter_map(move |result| match result {
722 iter.filter_map(move |result| match result {
723 Ok(node) => f(node).transpose(),
723 Ok(node) => f(node).transpose(),
724 Err(e) => Some(Err(e)),
724 Err(e) => Some(Err(e)),
725 })
725 })
726 }
726 }
727
727
728 impl OwningDirstateMap {
728 impl OwningDirstateMap {
729 pub fn clear(&mut self) {
729 pub fn clear(&mut self) {
730 let map = self.get_map_mut();
730 let map = self.get_map_mut();
731 map.root = Default::default();
731 map.root = Default::default();
732 map.nodes_with_entry_count = 0;
732 map.nodes_with_entry_count = 0;
733 map.nodes_with_copy_source_count = 0;
733 map.nodes_with_copy_source_count = 0;
734 }
734 }
735
735
736 pub fn set_entry(
736 pub fn set_entry(
737 &mut self,
737 &mut self,
738 filename: &HgPath,
738 filename: &HgPath,
739 entry: DirstateEntry,
739 entry: DirstateEntry,
740 ) -> Result<(), DirstateV2ParseError> {
740 ) -> Result<(), DirstateV2ParseError> {
741 let map = self.get_map_mut();
741 let map = self.get_map_mut();
742 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
742 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
743 Ok(())
743 Ok(())
744 }
744 }
745
745
746 pub fn add_file(
746 pub fn add_file(
747 &mut self,
747 &mut self,
748 filename: &HgPath,
748 filename: &HgPath,
749 entry: DirstateEntry,
749 entry: DirstateEntry,
750 ) -> Result<(), DirstateError> {
750 ) -> Result<(), DirstateError> {
751 let old_state = self.get(filename)?.map(|e| e.state());
751 let old_state = self.get(filename)?.map(|e| e.state());
752 let map = self.get_map_mut();
752 let map = self.get_map_mut();
753 Ok(map.add_or_remove_file(filename, old_state, entry)?)
753 Ok(map.add_or_remove_file(filename, old_state, entry)?)
754 }
754 }
755
755
756 pub fn remove_file(
756 pub fn remove_file(
757 &mut self,
757 &mut self,
758 filename: &HgPath,
758 filename: &HgPath,
759 in_merge: bool,
759 in_merge: bool,
760 ) -> Result<(), DirstateError> {
760 ) -> Result<(), DirstateError> {
761 let old_entry_opt = self.get(filename)?;
761 let old_entry_opt = self.get(filename)?;
762 let old_state = old_entry_opt.map(|e| e.state());
762 let old_state = old_entry_opt.map(|e| e.state());
763 let mut size = 0;
763 let mut size = 0;
764 if in_merge {
764 if in_merge {
765 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
765 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
766 // during a merge. So I (marmoute) am not sure we need the
766 // during a merge. So I (marmoute) am not sure we need the
767 // conditionnal at all. Adding double checking this with assert
767 // conditionnal at all. Adding double checking this with assert
768 // would be nice.
768 // would be nice.
769 if let Some(old_entry) = old_entry_opt {
769 if let Some(old_entry) = old_entry_opt {
770 // backup the previous state
770 // backup the previous state
771 if old_entry.state() == EntryState::Merged {
771 if old_entry.state() == EntryState::Merged {
772 size = SIZE_NON_NORMAL;
772 size = SIZE_NON_NORMAL;
773 } else if old_entry.state() == EntryState::Normal
773 } else if old_entry.state() == EntryState::Normal
774 && old_entry.size() == SIZE_FROM_OTHER_PARENT
774 && old_entry.size() == SIZE_FROM_OTHER_PARENT
775 {
775 {
776 // other parent
776 // other parent
777 size = SIZE_FROM_OTHER_PARENT;
777 size = SIZE_FROM_OTHER_PARENT;
778 }
778 }
779 }
779 }
780 }
780 }
781 if size == 0 {
781 if size == 0 {
782 self.copy_map_remove(filename)?;
782 self.copy_map_remove(filename)?;
783 }
783 }
784 let map = self.get_map_mut();
784 let map = self.get_map_mut();
785 let entry = DirstateEntry::new_removed(size);
785 let entry = DirstateEntry::new_removed(size);
786 Ok(map.add_or_remove_file(filename, old_state, entry)?)
786 Ok(map.add_or_remove_file(filename, old_state, entry)?)
787 }
787 }
788
788
789 pub fn drop_entry_and_copy_source(
789 pub fn drop_entry_and_copy_source(
790 &mut self,
790 &mut self,
791 filename: &HgPath,
791 filename: &HgPath,
792 ) -> Result<(), DirstateError> {
792 ) -> Result<(), DirstateError> {
793 let was_tracked = self
793 let was_tracked = self
794 .get(filename)?
794 .get(filename)?
795 .map_or(false, |e| e.state().is_tracked());
795 .map_or(false, |e| e.state().is_tracked());
796 let map = self.get_map_mut();
796 let map = self.get_map_mut();
797 struct Dropped {
797 struct Dropped {
798 was_tracked: bool,
798 was_tracked: bool,
799 had_entry: bool,
799 had_entry: bool,
800 had_copy_source: bool,
800 had_copy_source: bool,
801 }
801 }
802
802
803 /// If this returns `Ok(Some((dropped, removed)))`, then
803 /// If this returns `Ok(Some((dropped, removed)))`, then
804 ///
804 ///
805 /// * `dropped` is about the leaf node that was at `filename`
805 /// * `dropped` is about the leaf node that was at `filename`
806 /// * `removed` is whether this particular level of recursion just
806 /// * `removed` is whether this particular level of recursion just
807 /// removed a node in `nodes`.
807 /// removed a node in `nodes`.
808 fn recur<'on_disk>(
808 fn recur<'on_disk>(
809 on_disk: &'on_disk [u8],
809 on_disk: &'on_disk [u8],
810 unreachable_bytes: &mut u32,
810 unreachable_bytes: &mut u32,
811 nodes: &mut ChildNodes<'on_disk>,
811 nodes: &mut ChildNodes<'on_disk>,
812 path: &HgPath,
812 path: &HgPath,
813 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
813 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
814 let (first_path_component, rest_of_path) =
814 let (first_path_component, rest_of_path) =
815 path.split_first_component();
815 path.split_first_component();
816 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
816 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
817 let node = if let Some(node) = nodes.get_mut(first_path_component)
817 let node = if let Some(node) = nodes.get_mut(first_path_component)
818 {
818 {
819 node
819 node
820 } else {
820 } else {
821 return Ok(None);
821 return Ok(None);
822 };
822 };
823 let dropped;
823 let dropped;
824 if let Some(rest) = rest_of_path {
824 if let Some(rest) = rest_of_path {
825 if let Some((d, removed)) = recur(
825 if let Some((d, removed)) = recur(
826 on_disk,
826 on_disk,
827 unreachable_bytes,
827 unreachable_bytes,
828 &mut node.children,
828 &mut node.children,
829 rest,
829 rest,
830 )? {
830 )? {
831 dropped = d;
831 dropped = d;
832 if dropped.had_entry {
832 if dropped.had_entry {
833 node.descendants_with_entry_count -= 1;
833 node.descendants_with_entry_count -= 1;
834 }
834 }
835 if dropped.was_tracked {
835 if dropped.was_tracked {
836 node.tracked_descendants_count -= 1;
836 node.tracked_descendants_count -= 1;
837 }
837 }
838
838
839 // Directory caches must be invalidated when removing a
839 // Directory caches must be invalidated when removing a
840 // child node
840 // child node
841 if removed {
841 if removed {
842 if let NodeData::CachedDirectory { .. } = &node.data {
842 if let NodeData::CachedDirectory { .. } = &node.data {
843 node.data = NodeData::None
843 node.data = NodeData::None
844 }
844 }
845 }
845 }
846 } else {
846 } else {
847 return Ok(None);
847 return Ok(None);
848 }
848 }
849 } else {
849 } else {
850 let had_entry = node.data.has_entry();
850 let had_entry = node.data.has_entry();
851 if had_entry {
851 if had_entry {
852 node.data = NodeData::None
852 node.data = NodeData::None
853 }
853 }
854 if let Some(source) = &node.copy_source {
854 if let Some(source) = &node.copy_source {
855 DirstateMap::count_dropped_path(unreachable_bytes, source);
855 DirstateMap::count_dropped_path(unreachable_bytes, source);
856 node.copy_source = None
856 node.copy_source = None
857 }
857 }
858 dropped = Dropped {
858 dropped = Dropped {
859 was_tracked: node
859 was_tracked: node
860 .data
860 .data
861 .as_entry()
861 .as_entry()
862 .map_or(false, |entry| entry.state().is_tracked()),
862 .map_or(false, |entry| entry.state().is_tracked()),
863 had_entry,
863 had_entry,
864 had_copy_source: node.copy_source.take().is_some(),
864 had_copy_source: node.copy_source.take().is_some(),
865 };
865 };
866 }
866 }
867 // After recursion, for both leaf (rest_of_path is None) nodes and
867 // After recursion, for both leaf (rest_of_path is None) nodes and
868 // parent nodes, remove a node if it just became empty.
868 // parent nodes, remove a node if it just became empty.
869 let remove = !node.data.has_entry()
869 let remove = !node.data.has_entry()
870 && node.copy_source.is_none()
870 && node.copy_source.is_none()
871 && node.children.is_empty();
871 && node.children.is_empty();
872 if remove {
872 if remove {
873 let (key, _) =
873 let (key, _) =
874 nodes.remove_entry(first_path_component).unwrap();
874 nodes.remove_entry(first_path_component).unwrap();
875 DirstateMap::count_dropped_path(
875 DirstateMap::count_dropped_path(
876 unreachable_bytes,
876 unreachable_bytes,
877 key.full_path(),
877 key.full_path(),
878 )
878 )
879 }
879 }
880 Ok(Some((dropped, remove)))
880 Ok(Some((dropped, remove)))
881 }
881 }
882
882
883 if let Some((dropped, _removed)) = recur(
883 if let Some((dropped, _removed)) = recur(
884 map.on_disk,
884 map.on_disk,
885 &mut map.unreachable_bytes,
885 &mut map.unreachable_bytes,
886 &mut map.root,
886 &mut map.root,
887 filename,
887 filename,
888 )? {
888 )? {
889 if dropped.had_entry {
889 if dropped.had_entry {
890 map.nodes_with_entry_count -= 1
890 map.nodes_with_entry_count -= 1
891 }
891 }
892 if dropped.had_copy_source {
892 if dropped.had_copy_source {
893 map.nodes_with_copy_source_count -= 1
893 map.nodes_with_copy_source_count -= 1
894 }
894 }
895 } else {
895 } else {
896 debug_assert!(!was_tracked);
896 debug_assert!(!was_tracked);
897 }
897 }
898 Ok(())
898 Ok(())
899 }
899 }
900
900
901 pub fn has_tracked_dir(
901 pub fn has_tracked_dir(
902 &mut self,
902 &mut self,
903 directory: &HgPath,
903 directory: &HgPath,
904 ) -> Result<bool, DirstateError> {
904 ) -> Result<bool, DirstateError> {
905 let map = self.get_map_mut();
905 let map = self.get_map_mut();
906 if let Some(node) = map.get_node(directory)? {
906 if let Some(node) = map.get_node(directory)? {
907 // A node without a `DirstateEntry` was created to hold child
907 // A node without a `DirstateEntry` was created to hold child
908 // nodes, and is therefore a directory.
908 // nodes, and is therefore a directory.
909 let state = node.state()?;
909 let state = node.state()?;
910 Ok(state.is_none() && node.tracked_descendants_count() > 0)
910 Ok(state.is_none() && node.tracked_descendants_count() > 0)
911 } else {
911 } else {
912 Ok(false)
912 Ok(false)
913 }
913 }
914 }
914 }
915
915
916 pub fn has_dir(
916 pub fn has_dir(
917 &mut self,
917 &mut self,
918 directory: &HgPath,
918 directory: &HgPath,
919 ) -> Result<bool, DirstateError> {
919 ) -> Result<bool, DirstateError> {
920 let map = self.get_map_mut();
920 let map = self.get_map_mut();
921 if let Some(node) = map.get_node(directory)? {
921 if let Some(node) = map.get_node(directory)? {
922 // A node without a `DirstateEntry` was created to hold child
922 // A node without a `DirstateEntry` was created to hold child
923 // nodes, and is therefore a directory.
923 // nodes, and is therefore a directory.
924 let state = node.state()?;
924 let state = node.state()?;
925 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
925 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
926 } else {
926 } else {
927 Ok(false)
927 Ok(false)
928 }
928 }
929 }
929 }
930
930
931 #[timed]
931 #[timed]
932 pub fn pack_v1(
932 pub fn pack_v1(
933 &mut self,
933 &mut self,
934 parents: DirstateParents,
934 parents: DirstateParents,
935 now: Timestamp,
935 now: Timestamp,
936 ) -> Result<Vec<u8>, DirstateError> {
936 ) -> Result<Vec<u8>, DirstateError> {
937 let map = self.get_map_mut();
937 let map = self.get_map_mut();
938 let now: i32 = now.0.try_into().expect("time overflow");
938 let now: i32 = now.0.try_into().expect("time overflow");
939 let mut ambiguous_mtimes = Vec::new();
939 let mut ambiguous_mtimes = Vec::new();
940 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
940 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
941 // reallocations
941 // reallocations
942 let mut size = parents.as_bytes().len();
942 let mut size = parents.as_bytes().len();
943 for node in map.iter_nodes() {
943 for node in map.iter_nodes() {
944 let node = node?;
944 let node = node?;
945 if let Some(entry) = node.entry()? {
945 if let Some(entry) = node.entry()? {
946 size += packed_entry_size(
946 size += packed_entry_size(
947 node.full_path(map.on_disk)?,
947 node.full_path(map.on_disk)?,
948 node.copy_source(map.on_disk)?,
948 node.copy_source(map.on_disk)?,
949 );
949 );
950 if entry.mtime_is_ambiguous(now) {
950 if entry.need_delay(now) {
951 ambiguous_mtimes.push(
951 ambiguous_mtimes.push(
952 node.full_path_borrowed(map.on_disk)?
952 node.full_path_borrowed(map.on_disk)?
953 .detach_from_tree(),
953 .detach_from_tree(),
954 )
954 )
955 }
955 }
956 }
956 }
957 }
957 }
958 map.clear_known_ambiguous_mtimes(&ambiguous_mtimes)?;
958 map.clear_known_ambiguous_mtimes(&ambiguous_mtimes)?;
959
959
960 let mut packed = Vec::with_capacity(size);
960 let mut packed = Vec::with_capacity(size);
961 packed.extend(parents.as_bytes());
961 packed.extend(parents.as_bytes());
962
962
963 for node in map.iter_nodes() {
963 for node in map.iter_nodes() {
964 let node = node?;
964 let node = node?;
965 if let Some(entry) = node.entry()? {
965 if let Some(entry) = node.entry()? {
966 pack_entry(
966 pack_entry(
967 node.full_path(map.on_disk)?,
967 node.full_path(map.on_disk)?,
968 &entry,
968 &entry,
969 node.copy_source(map.on_disk)?,
969 node.copy_source(map.on_disk)?,
970 &mut packed,
970 &mut packed,
971 );
971 );
972 }
972 }
973 }
973 }
974 Ok(packed)
974 Ok(packed)
975 }
975 }
976
976
977 /// Returns new data and metadata together with whether that data should be
977 /// Returns new data and metadata together with whether that data should be
978 /// appended to the existing data file whose content is at
978 /// appended to the existing data file whose content is at
979 /// `map.on_disk` (true), instead of written to a new data file
979 /// `map.on_disk` (true), instead of written to a new data file
980 /// (false).
980 /// (false).
981 #[timed]
981 #[timed]
982 pub fn pack_v2(
982 pub fn pack_v2(
983 &mut self,
983 &mut self,
984 now: Timestamp,
984 now: Timestamp,
985 can_append: bool,
985 can_append: bool,
986 ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> {
986 ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> {
987 let map = self.get_map_mut();
987 let map = self.get_map_mut();
988 // TODO: how do we want to handle this in 2038?
988 // TODO: how do we want to handle this in 2038?
989 let now: i32 = now.0.try_into().expect("time overflow");
989 let now: i32 = now.0.try_into().expect("time overflow");
990 let mut paths = Vec::new();
990 let mut paths = Vec::new();
991 for node in map.iter_nodes() {
991 for node in map.iter_nodes() {
992 let node = node?;
992 let node = node?;
993 if let Some(entry) = node.entry()? {
993 if let Some(entry) = node.entry()? {
994 if entry.mtime_is_ambiguous(now) {
994 if entry.need_delay(now) {
995 paths.push(
995 paths.push(
996 node.full_path_borrowed(map.on_disk)?
996 node.full_path_borrowed(map.on_disk)?
997 .detach_from_tree(),
997 .detach_from_tree(),
998 )
998 )
999 }
999 }
1000 }
1000 }
1001 }
1001 }
1002 // Borrow of `self` ends here since we collect cloned paths
1002 // Borrow of `self` ends here since we collect cloned paths
1003
1003
1004 map.clear_known_ambiguous_mtimes(&paths)?;
1004 map.clear_known_ambiguous_mtimes(&paths)?;
1005
1005
1006 on_disk::write(map, can_append)
1006 on_disk::write(map, can_append)
1007 }
1007 }
1008
1008
1009 pub fn status<'a>(
1009 pub fn status<'a>(
1010 &'a mut self,
1010 &'a mut self,
1011 matcher: &'a (dyn Matcher + Sync),
1011 matcher: &'a (dyn Matcher + Sync),
1012 root_dir: PathBuf,
1012 root_dir: PathBuf,
1013 ignore_files: Vec<PathBuf>,
1013 ignore_files: Vec<PathBuf>,
1014 options: StatusOptions,
1014 options: StatusOptions,
1015 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
1015 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
1016 {
1016 {
1017 let map = self.get_map_mut();
1017 let map = self.get_map_mut();
1018 super::status::status(map, matcher, root_dir, ignore_files, options)
1018 super::status::status(map, matcher, root_dir, ignore_files, options)
1019 }
1019 }
1020
1020
1021 pub fn copy_map_len(&self) -> usize {
1021 pub fn copy_map_len(&self) -> usize {
1022 let map = self.get_map();
1022 let map = self.get_map();
1023 map.nodes_with_copy_source_count as usize
1023 map.nodes_with_copy_source_count as usize
1024 }
1024 }
1025
1025
1026 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1026 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1027 let map = self.get_map();
1027 let map = self.get_map();
1028 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1028 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1029 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1029 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1030 Some((node.full_path(map.on_disk)?, source))
1030 Some((node.full_path(map.on_disk)?, source))
1031 } else {
1031 } else {
1032 None
1032 None
1033 })
1033 })
1034 }))
1034 }))
1035 }
1035 }
1036
1036
1037 pub fn copy_map_contains_key(
1037 pub fn copy_map_contains_key(
1038 &self,
1038 &self,
1039 key: &HgPath,
1039 key: &HgPath,
1040 ) -> Result<bool, DirstateV2ParseError> {
1040 ) -> Result<bool, DirstateV2ParseError> {
1041 let map = self.get_map();
1041 let map = self.get_map();
1042 Ok(if let Some(node) = map.get_node(key)? {
1042 Ok(if let Some(node) = map.get_node(key)? {
1043 node.has_copy_source()
1043 node.has_copy_source()
1044 } else {
1044 } else {
1045 false
1045 false
1046 })
1046 })
1047 }
1047 }
1048
1048
1049 pub fn copy_map_get(
1049 pub fn copy_map_get(
1050 &self,
1050 &self,
1051 key: &HgPath,
1051 key: &HgPath,
1052 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1052 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1053 let map = self.get_map();
1053 let map = self.get_map();
1054 if let Some(node) = map.get_node(key)? {
1054 if let Some(node) = map.get_node(key)? {
1055 if let Some(source) = node.copy_source(map.on_disk)? {
1055 if let Some(source) = node.copy_source(map.on_disk)? {
1056 return Ok(Some(source));
1056 return Ok(Some(source));
1057 }
1057 }
1058 }
1058 }
1059 Ok(None)
1059 Ok(None)
1060 }
1060 }
1061
1061
1062 pub fn copy_map_remove(
1062 pub fn copy_map_remove(
1063 &mut self,
1063 &mut self,
1064 key: &HgPath,
1064 key: &HgPath,
1065 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1065 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1066 let map = self.get_map_mut();
1066 let map = self.get_map_mut();
1067 let count = &mut map.nodes_with_copy_source_count;
1067 let count = &mut map.nodes_with_copy_source_count;
1068 let unreachable_bytes = &mut map.unreachable_bytes;
1068 let unreachable_bytes = &mut map.unreachable_bytes;
1069 Ok(DirstateMap::get_node_mut(
1069 Ok(DirstateMap::get_node_mut(
1070 map.on_disk,
1070 map.on_disk,
1071 unreachable_bytes,
1071 unreachable_bytes,
1072 &mut map.root,
1072 &mut map.root,
1073 key,
1073 key,
1074 )?
1074 )?
1075 .and_then(|node| {
1075 .and_then(|node| {
1076 if let Some(source) = &node.copy_source {
1076 if let Some(source) = &node.copy_source {
1077 *count -= 1;
1077 *count -= 1;
1078 DirstateMap::count_dropped_path(unreachable_bytes, source);
1078 DirstateMap::count_dropped_path(unreachable_bytes, source);
1079 }
1079 }
1080 node.copy_source.take().map(Cow::into_owned)
1080 node.copy_source.take().map(Cow::into_owned)
1081 }))
1081 }))
1082 }
1082 }
1083
1083
1084 pub fn copy_map_insert(
1084 pub fn copy_map_insert(
1085 &mut self,
1085 &mut self,
1086 key: HgPathBuf,
1086 key: HgPathBuf,
1087 value: HgPathBuf,
1087 value: HgPathBuf,
1088 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1088 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1089 let map = self.get_map_mut();
1089 let map = self.get_map_mut();
1090 let node = DirstateMap::get_or_insert_node(
1090 let node = DirstateMap::get_or_insert_node(
1091 map.on_disk,
1091 map.on_disk,
1092 &mut map.unreachable_bytes,
1092 &mut map.unreachable_bytes,
1093 &mut map.root,
1093 &mut map.root,
1094 &key,
1094 &key,
1095 WithBasename::to_cow_owned,
1095 WithBasename::to_cow_owned,
1096 |_ancestor| {},
1096 |_ancestor| {},
1097 )?;
1097 )?;
1098 if node.copy_source.is_none() {
1098 if node.copy_source.is_none() {
1099 map.nodes_with_copy_source_count += 1
1099 map.nodes_with_copy_source_count += 1
1100 }
1100 }
1101 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1101 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1102 }
1102 }
1103
1103
1104 pub fn len(&self) -> usize {
1104 pub fn len(&self) -> usize {
1105 let map = self.get_map();
1105 let map = self.get_map();
1106 map.nodes_with_entry_count as usize
1106 map.nodes_with_entry_count as usize
1107 }
1107 }
1108
1108
1109 pub fn contains_key(
1109 pub fn contains_key(
1110 &self,
1110 &self,
1111 key: &HgPath,
1111 key: &HgPath,
1112 ) -> Result<bool, DirstateV2ParseError> {
1112 ) -> Result<bool, DirstateV2ParseError> {
1113 Ok(self.get(key)?.is_some())
1113 Ok(self.get(key)?.is_some())
1114 }
1114 }
1115
1115
1116 pub fn get(
1116 pub fn get(
1117 &self,
1117 &self,
1118 key: &HgPath,
1118 key: &HgPath,
1119 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1119 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1120 let map = self.get_map();
1120 let map = self.get_map();
1121 Ok(if let Some(node) = map.get_node(key)? {
1121 Ok(if let Some(node) = map.get_node(key)? {
1122 node.entry()?
1122 node.entry()?
1123 } else {
1123 } else {
1124 None
1124 None
1125 })
1125 })
1126 }
1126 }
1127
1127
1128 pub fn iter(&self) -> StateMapIter<'_> {
1128 pub fn iter(&self) -> StateMapIter<'_> {
1129 let map = self.get_map();
1129 let map = self.get_map();
1130 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1130 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1131 Ok(if let Some(entry) = node.entry()? {
1131 Ok(if let Some(entry) = node.entry()? {
1132 Some((node.full_path(map.on_disk)?, entry))
1132 Some((node.full_path(map.on_disk)?, entry))
1133 } else {
1133 } else {
1134 None
1134 None
1135 })
1135 })
1136 }))
1136 }))
1137 }
1137 }
1138
1138
1139 pub fn iter_tracked_dirs(
1139 pub fn iter_tracked_dirs(
1140 &mut self,
1140 &mut self,
1141 ) -> Result<
1141 ) -> Result<
1142 Box<
1142 Box<
1143 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1143 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1144 + Send
1144 + Send
1145 + '_,
1145 + '_,
1146 >,
1146 >,
1147 DirstateError,
1147 DirstateError,
1148 > {
1148 > {
1149 let map = self.get_map_mut();
1149 let map = self.get_map_mut();
1150 let on_disk = map.on_disk;
1150 let on_disk = map.on_disk;
1151 Ok(Box::new(filter_map_results(
1151 Ok(Box::new(filter_map_results(
1152 map.iter_nodes(),
1152 map.iter_nodes(),
1153 move |node| {
1153 move |node| {
1154 Ok(if node.tracked_descendants_count() > 0 {
1154 Ok(if node.tracked_descendants_count() > 0 {
1155 Some(node.full_path(on_disk)?)
1155 Some(node.full_path(on_disk)?)
1156 } else {
1156 } else {
1157 None
1157 None
1158 })
1158 })
1159 },
1159 },
1160 )))
1160 )))
1161 }
1161 }
1162
1162
1163 pub fn debug_iter(
1163 pub fn debug_iter(
1164 &self,
1164 &self,
1165 all: bool,
1165 all: bool,
1166 ) -> Box<
1166 ) -> Box<
1167 dyn Iterator<
1167 dyn Iterator<
1168 Item = Result<
1168 Item = Result<
1169 (&HgPath, (u8, i32, i32, i32)),
1169 (&HgPath, (u8, i32, i32, i32)),
1170 DirstateV2ParseError,
1170 DirstateV2ParseError,
1171 >,
1171 >,
1172 > + Send
1172 > + Send
1173 + '_,
1173 + '_,
1174 > {
1174 > {
1175 let map = self.get_map();
1175 let map = self.get_map();
1176 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1176 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1177 let debug_tuple = if let Some(entry) = node.entry()? {
1177 let debug_tuple = if let Some(entry) = node.entry()? {
1178 entry.debug_tuple()
1178 entry.debug_tuple()
1179 } else if !all {
1179 } else if !all {
1180 return Ok(None);
1180 return Ok(None);
1181 } else if let Some(mtime) = node.cached_directory_mtime()? {
1181 } else if let Some(mtime) = node.cached_directory_mtime()? {
1182 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1182 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1183 } else {
1183 } else {
1184 (b' ', 0, -1, -1)
1184 (b' ', 0, -1, -1)
1185 };
1185 };
1186 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1186 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1187 }))
1187 }))
1188 }
1188 }
1189 }
1189 }
@@ -1,263 +1,263 b''
1 use cpython::exc;
1 use cpython::exc;
2 use cpython::ObjectProtocol;
2 use cpython::ObjectProtocol;
3 use cpython::PyBytes;
3 use cpython::PyBytes;
4 use cpython::PyErr;
4 use cpython::PyErr;
5 use cpython::PyNone;
5 use cpython::PyNone;
6 use cpython::PyObject;
6 use cpython::PyObject;
7 use cpython::PyResult;
7 use cpython::PyResult;
8 use cpython::Python;
8 use cpython::Python;
9 use cpython::PythonObject;
9 use cpython::PythonObject;
10 use hg::dirstate::DirstateEntry;
10 use hg::dirstate::DirstateEntry;
11 use hg::dirstate::EntryState;
11 use hg::dirstate::EntryState;
12 use std::cell::Cell;
12 use std::cell::Cell;
13 use std::convert::TryFrom;
13 use std::convert::TryFrom;
14
14
15 py_class!(pub class DirstateItem |py| {
15 py_class!(pub class DirstateItem |py| {
16 data entry: Cell<DirstateEntry>;
16 data entry: Cell<DirstateEntry>;
17
17
18 def __new__(
18 def __new__(
19 _cls,
19 _cls,
20 wc_tracked: bool = false,
20 wc_tracked: bool = false,
21 p1_tracked: bool = false,
21 p1_tracked: bool = false,
22 p2_info: bool = false,
22 p2_info: bool = false,
23 has_meaningful_data: bool = true,
23 has_meaningful_data: bool = true,
24 has_meaningful_mtime: bool = true,
24 has_meaningful_mtime: bool = true,
25 parentfiledata: Option<(u32, u32, u32)> = None,
25 parentfiledata: Option<(u32, u32, u32)> = None,
26 fallback_exec: Option<bool> = None,
26 fallback_exec: Option<bool> = None,
27 fallback_symlink: Option<bool> = None,
27 fallback_symlink: Option<bool> = None,
28
28
29 ) -> PyResult<DirstateItem> {
29 ) -> PyResult<DirstateItem> {
30 let mut mode_size_opt = None;
30 let mut mode_size_opt = None;
31 let mut mtime_opt = None;
31 let mut mtime_opt = None;
32 if let Some((mode, size, mtime)) = parentfiledata {
32 if let Some((mode, size, mtime)) = parentfiledata {
33 if has_meaningful_data {
33 if has_meaningful_data {
34 mode_size_opt = Some((mode, size))
34 mode_size_opt = Some((mode, size))
35 }
35 }
36 if has_meaningful_mtime {
36 if has_meaningful_mtime {
37 mtime_opt = Some(mtime)
37 mtime_opt = Some(mtime)
38 }
38 }
39 }
39 }
40 let entry = DirstateEntry::from_v2_data(
40 let entry = DirstateEntry::from_v2_data(
41 wc_tracked,
41 wc_tracked,
42 p1_tracked,
42 p1_tracked,
43 p2_info,
43 p2_info,
44 mode_size_opt,
44 mode_size_opt,
45 mtime_opt,
45 mtime_opt,
46 fallback_exec,
46 fallback_exec,
47 fallback_symlink,
47 fallback_symlink,
48 );
48 );
49 DirstateItem::create_instance(py, Cell::new(entry))
49 DirstateItem::create_instance(py, Cell::new(entry))
50 }
50 }
51
51
52 @property
52 @property
53 def state(&self) -> PyResult<PyBytes> {
53 def state(&self) -> PyResult<PyBytes> {
54 let state_byte: u8 = self.entry(py).get().state().into();
54 let state_byte: u8 = self.entry(py).get().state().into();
55 Ok(PyBytes::new(py, &[state_byte]))
55 Ok(PyBytes::new(py, &[state_byte]))
56 }
56 }
57
57
58 @property
58 @property
59 def mode(&self) -> PyResult<i32> {
59 def mode(&self) -> PyResult<i32> {
60 Ok(self.entry(py).get().mode())
60 Ok(self.entry(py).get().mode())
61 }
61 }
62
62
63 @property
63 @property
64 def size(&self) -> PyResult<i32> {
64 def size(&self) -> PyResult<i32> {
65 Ok(self.entry(py).get().size())
65 Ok(self.entry(py).get().size())
66 }
66 }
67
67
68 @property
68 @property
69 def mtime(&self) -> PyResult<i32> {
69 def mtime(&self) -> PyResult<i32> {
70 Ok(self.entry(py).get().mtime())
70 Ok(self.entry(py).get().mtime())
71 }
71 }
72
72
73 @property
73 @property
74 def has_fallback_exec(&self) -> PyResult<bool> {
74 def has_fallback_exec(&self) -> PyResult<bool> {
75 match self.entry(py).get().get_fallback_exec() {
75 match self.entry(py).get().get_fallback_exec() {
76 Some(_) => Ok(true),
76 Some(_) => Ok(true),
77 None => Ok(false),
77 None => Ok(false),
78 }
78 }
79 }
79 }
80
80
81 @property
81 @property
82 def fallback_exec(&self) -> PyResult<Option<bool>> {
82 def fallback_exec(&self) -> PyResult<Option<bool>> {
83 match self.entry(py).get().get_fallback_exec() {
83 match self.entry(py).get().get_fallback_exec() {
84 Some(exec) => Ok(Some(exec)),
84 Some(exec) => Ok(Some(exec)),
85 None => Ok(None),
85 None => Ok(None),
86 }
86 }
87 }
87 }
88
88
89 @fallback_exec.setter
89 @fallback_exec.setter
90 def set_fallback_exec(&self, value: Option<PyObject>) -> PyResult<()> {
90 def set_fallback_exec(&self, value: Option<PyObject>) -> PyResult<()> {
91 match value {
91 match value {
92 None => {self.entry(py).get().set_fallback_exec(None);},
92 None => {self.entry(py).get().set_fallback_exec(None);},
93 Some(value) => {
93 Some(value) => {
94 if value.is_none(py) {
94 if value.is_none(py) {
95 self.entry(py).get().set_fallback_exec(None);
95 self.entry(py).get().set_fallback_exec(None);
96 } else {
96 } else {
97 self.entry(py).get().set_fallback_exec(
97 self.entry(py).get().set_fallback_exec(
98 Some(value.is_true(py)?)
98 Some(value.is_true(py)?)
99 );
99 );
100 }},
100 }},
101 }
101 }
102 Ok(())
102 Ok(())
103 }
103 }
104
104
105 @property
105 @property
106 def has_fallback_symlink(&self) -> PyResult<bool> {
106 def has_fallback_symlink(&self) -> PyResult<bool> {
107 match self.entry(py).get().get_fallback_symlink() {
107 match self.entry(py).get().get_fallback_symlink() {
108 Some(_) => Ok(true),
108 Some(_) => Ok(true),
109 None => Ok(false),
109 None => Ok(false),
110 }
110 }
111 }
111 }
112
112
113 @property
113 @property
114 def fallback_symlink(&self) -> PyResult<Option<bool>> {
114 def fallback_symlink(&self) -> PyResult<Option<bool>> {
115 match self.entry(py).get().get_fallback_symlink() {
115 match self.entry(py).get().get_fallback_symlink() {
116 Some(symlink) => Ok(Some(symlink)),
116 Some(symlink) => Ok(Some(symlink)),
117 None => Ok(None),
117 None => Ok(None),
118 }
118 }
119 }
119 }
120
120
121 @fallback_symlink.setter
121 @fallback_symlink.setter
122 def set_fallback_symlink(&self, value: Option<PyObject>) -> PyResult<()> {
122 def set_fallback_symlink(&self, value: Option<PyObject>) -> PyResult<()> {
123 match value {
123 match value {
124 None => {self.entry(py).get().set_fallback_symlink(None);},
124 None => {self.entry(py).get().set_fallback_symlink(None);},
125 Some(value) => {
125 Some(value) => {
126 if value.is_none(py) {
126 if value.is_none(py) {
127 self.entry(py).get().set_fallback_symlink(None);
127 self.entry(py).get().set_fallback_symlink(None);
128 } else {
128 } else {
129 self.entry(py).get().set_fallback_symlink(
129 self.entry(py).get().set_fallback_symlink(
130 Some(value.is_true(py)?)
130 Some(value.is_true(py)?)
131 );
131 );
132 }},
132 }},
133 }
133 }
134 Ok(())
134 Ok(())
135 }
135 }
136
136
137 @property
137 @property
138 def tracked(&self) -> PyResult<bool> {
138 def tracked(&self) -> PyResult<bool> {
139 Ok(self.entry(py).get().tracked())
139 Ok(self.entry(py).get().tracked())
140 }
140 }
141
141
142 @property
142 @property
143 def p1_tracked(&self) -> PyResult<bool> {
143 def p1_tracked(&self) -> PyResult<bool> {
144 Ok(self.entry(py).get().p1_tracked())
144 Ok(self.entry(py).get().p1_tracked())
145 }
145 }
146
146
147 @property
147 @property
148 def added(&self) -> PyResult<bool> {
148 def added(&self) -> PyResult<bool> {
149 Ok(self.entry(py).get().added())
149 Ok(self.entry(py).get().added())
150 }
150 }
151
151
152
152
153 @property
153 @property
154 def p2_info(&self) -> PyResult<bool> {
154 def p2_info(&self) -> PyResult<bool> {
155 Ok(self.entry(py).get().p2_info())
155 Ok(self.entry(py).get().p2_info())
156 }
156 }
157
157
158 @property
158 @property
159 def removed(&self) -> PyResult<bool> {
159 def removed(&self) -> PyResult<bool> {
160 Ok(self.entry(py).get().removed())
160 Ok(self.entry(py).get().removed())
161 }
161 }
162
162
163 @property
163 @property
164 def maybe_clean(&self) -> PyResult<bool> {
164 def maybe_clean(&self) -> PyResult<bool> {
165 Ok(self.entry(py).get().maybe_clean())
165 Ok(self.entry(py).get().maybe_clean())
166 }
166 }
167
167
168 @property
168 @property
169 def any_tracked(&self) -> PyResult<bool> {
169 def any_tracked(&self) -> PyResult<bool> {
170 Ok(self.entry(py).get().any_tracked())
170 Ok(self.entry(py).get().any_tracked())
171 }
171 }
172
172
173 def v1_state(&self) -> PyResult<PyBytes> {
173 def v1_state(&self) -> PyResult<PyBytes> {
174 let (state, _mode, _size, _mtime) = self.entry(py).get().v1_data();
174 let (state, _mode, _size, _mtime) = self.entry(py).get().v1_data();
175 let state_byte: u8 = state.into();
175 let state_byte: u8 = state.into();
176 Ok(PyBytes::new(py, &[state_byte]))
176 Ok(PyBytes::new(py, &[state_byte]))
177 }
177 }
178
178
179 def v1_mode(&self) -> PyResult<i32> {
179 def v1_mode(&self) -> PyResult<i32> {
180 let (_state, mode, _size, _mtime) = self.entry(py).get().v1_data();
180 let (_state, mode, _size, _mtime) = self.entry(py).get().v1_data();
181 Ok(mode)
181 Ok(mode)
182 }
182 }
183
183
184 def v1_size(&self) -> PyResult<i32> {
184 def v1_size(&self) -> PyResult<i32> {
185 let (_state, _mode, size, _mtime) = self.entry(py).get().v1_data();
185 let (_state, _mode, size, _mtime) = self.entry(py).get().v1_data();
186 Ok(size)
186 Ok(size)
187 }
187 }
188
188
189 def v1_mtime(&self) -> PyResult<i32> {
189 def v1_mtime(&self) -> PyResult<i32> {
190 let (_state, _mode, _size, mtime) = self.entry(py).get().v1_data();
190 let (_state, _mode, _size, mtime) = self.entry(py).get().v1_data();
191 Ok(mtime)
191 Ok(mtime)
192 }
192 }
193
193
194 def need_delay(&self, now: i32) -> PyResult<bool> {
194 def need_delay(&self, now: i32) -> PyResult<bool> {
195 Ok(self.entry(py).get().mtime_is_ambiguous(now))
195 Ok(self.entry(py).get().need_delay(now))
196 }
196 }
197
197
198 @classmethod
198 @classmethod
199 def from_v1_data(
199 def from_v1_data(
200 _cls,
200 _cls,
201 state: PyBytes,
201 state: PyBytes,
202 mode: i32,
202 mode: i32,
203 size: i32,
203 size: i32,
204 mtime: i32,
204 mtime: i32,
205 ) -> PyResult<Self> {
205 ) -> PyResult<Self> {
206 let state = <[u8; 1]>::try_from(state.data(py))
206 let state = <[u8; 1]>::try_from(state.data(py))
207 .ok()
207 .ok()
208 .and_then(|state| EntryState::try_from(state[0]).ok())
208 .and_then(|state| EntryState::try_from(state[0]).ok())
209 .ok_or_else(|| PyErr::new::<exc::ValueError, _>(py, "invalid state"))?;
209 .ok_or_else(|| PyErr::new::<exc::ValueError, _>(py, "invalid state"))?;
210 let entry = DirstateEntry::from_v1_data(state, mode, size, mtime);
210 let entry = DirstateEntry::from_v1_data(state, mode, size, mtime);
211 DirstateItem::create_instance(py, Cell::new(entry))
211 DirstateItem::create_instance(py, Cell::new(entry))
212 }
212 }
213
213
214 def drop_merge_data(&self) -> PyResult<PyNone> {
214 def drop_merge_data(&self) -> PyResult<PyNone> {
215 self.update(py, |entry| entry.drop_merge_data());
215 self.update(py, |entry| entry.drop_merge_data());
216 Ok(PyNone)
216 Ok(PyNone)
217 }
217 }
218
218
219 def set_clean(
219 def set_clean(
220 &self,
220 &self,
221 mode: u32,
221 mode: u32,
222 size: u32,
222 size: u32,
223 mtime: u32,
223 mtime: u32,
224 ) -> PyResult<PyNone> {
224 ) -> PyResult<PyNone> {
225 self.update(py, |entry| entry.set_clean(mode, size, mtime));
225 self.update(py, |entry| entry.set_clean(mode, size, mtime));
226 Ok(PyNone)
226 Ok(PyNone)
227 }
227 }
228
228
229 def set_possibly_dirty(&self) -> PyResult<PyNone> {
229 def set_possibly_dirty(&self) -> PyResult<PyNone> {
230 self.update(py, |entry| entry.set_possibly_dirty());
230 self.update(py, |entry| entry.set_possibly_dirty());
231 Ok(PyNone)
231 Ok(PyNone)
232 }
232 }
233
233
234 def set_tracked(&self) -> PyResult<PyNone> {
234 def set_tracked(&self) -> PyResult<PyNone> {
235 self.update(py, |entry| entry.set_tracked());
235 self.update(py, |entry| entry.set_tracked());
236 Ok(PyNone)
236 Ok(PyNone)
237 }
237 }
238
238
239 def set_untracked(&self) -> PyResult<PyNone> {
239 def set_untracked(&self) -> PyResult<PyNone> {
240 self.update(py, |entry| entry.set_untracked());
240 self.update(py, |entry| entry.set_untracked());
241 Ok(PyNone)
241 Ok(PyNone)
242 }
242 }
243 });
243 });
244
244
245 impl DirstateItem {
245 impl DirstateItem {
246 pub fn new_as_pyobject(
246 pub fn new_as_pyobject(
247 py: Python<'_>,
247 py: Python<'_>,
248 entry: DirstateEntry,
248 entry: DirstateEntry,
249 ) -> PyResult<PyObject> {
249 ) -> PyResult<PyObject> {
250 Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
250 Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
251 }
251 }
252
252
253 pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
253 pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
254 self.entry(py).get()
254 self.entry(py).get()
255 }
255 }
256
256
257 // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
257 // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
258 pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
258 pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
259 let mut entry = self.entry(py).get();
259 let mut entry = self.entry(py).get();
260 f(&mut entry);
260 f(&mut entry);
261 self.entry(py).set(entry)
261 self.entry(py).set(entry)
262 }
262 }
263 }
263 }
General Comments 0
You need to be logged in to leave comments. Login now