##// END OF EJS Templates
rust-dirstate-entry: fix typo in panic message...
Raphaël Gomès -
r49919:362312d6 default
parent child Browse files
Show More
@@ -1,707 +1,707 b''
1 1 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
2 2 use crate::errors::HgError;
3 3 use bitflags::bitflags;
4 4 use std::convert::{TryFrom, TryInto};
5 5 use std::fs;
6 6 use std::io;
7 7 use std::time::{SystemTime, UNIX_EPOCH};
8 8
9 9 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
10 10 pub enum EntryState {
11 11 Normal,
12 12 Added,
13 13 Removed,
14 14 Merged,
15 15 }
16 16
17 17 /// `size` and `mtime.seconds` are truncated to 31 bits.
18 18 ///
19 19 /// TODO: double-check status algorithm correctness for files
20 20 /// larger than 2 GiB or modified after 2038.
21 21 #[derive(Debug, Copy, Clone)]
22 22 pub struct DirstateEntry {
23 23 pub(crate) flags: Flags,
24 24 mode_size: Option<(u32, u32)>,
25 25 mtime: Option<TruncatedTimestamp>,
26 26 }
27 27
28 28 bitflags! {
29 29 pub(crate) struct Flags: u8 {
30 30 const WDIR_TRACKED = 1 << 0;
31 31 const P1_TRACKED = 1 << 1;
32 32 const P2_INFO = 1 << 2;
33 33 const HAS_FALLBACK_EXEC = 1 << 3;
34 34 const FALLBACK_EXEC = 1 << 4;
35 35 const HAS_FALLBACK_SYMLINK = 1 << 5;
36 36 const FALLBACK_SYMLINK = 1 << 6;
37 37 }
38 38 }
39 39
40 40 /// A Unix timestamp with nanoseconds precision
41 41 #[derive(Debug, Copy, Clone)]
42 42 pub struct TruncatedTimestamp {
43 43 truncated_seconds: u32,
44 44 /// Always in the `0 .. 1_000_000_000` range.
45 45 nanoseconds: u32,
46 46 /// TODO this should be in DirstateEntry, but the current code needs
47 47 /// refactoring to use DirstateEntry instead of TruncatedTimestamp for
48 48 /// comparison.
49 49 pub second_ambiguous: bool,
50 50 }
51 51
52 52 impl TruncatedTimestamp {
53 53 /// Constructs from a timestamp potentially outside of the supported range,
54 54 /// and truncate the seconds components to its lower 31 bits.
55 55 ///
56 56 /// Panics if the nanoseconds components is not in the expected range.
57 57 pub fn new_truncate(
58 58 seconds: i64,
59 59 nanoseconds: u32,
60 60 second_ambiguous: bool,
61 61 ) -> Self {
62 62 assert!(nanoseconds < NSEC_PER_SEC);
63 63 Self {
64 64 truncated_seconds: seconds as u32 & RANGE_MASK_31BIT,
65 65 nanoseconds,
66 66 second_ambiguous,
67 67 }
68 68 }
69 69
70 70 /// Construct from components. Returns an error if they are not in the
71 71 /// expcted range.
72 72 pub fn from_already_truncated(
73 73 truncated_seconds: u32,
74 74 nanoseconds: u32,
75 75 second_ambiguous: bool,
76 76 ) -> Result<Self, DirstateV2ParseError> {
77 77 if truncated_seconds & !RANGE_MASK_31BIT == 0
78 78 && nanoseconds < NSEC_PER_SEC
79 79 {
80 80 Ok(Self {
81 81 truncated_seconds,
82 82 nanoseconds,
83 83 second_ambiguous,
84 84 })
85 85 } else {
86 86 Err(DirstateV2ParseError)
87 87 }
88 88 }
89 89
90 90 /// Returns a `TruncatedTimestamp` for the modification time of `metadata`.
91 91 ///
92 92 /// Propagates errors from `std` on platforms where modification time
93 93 /// is not available at all.
94 94 pub fn for_mtime_of(metadata: &fs::Metadata) -> io::Result<Self> {
95 95 #[cfg(unix)]
96 96 {
97 97 use std::os::unix::fs::MetadataExt;
98 98 let seconds = metadata.mtime();
99 99 // i64 -> u32 with value always in the `0 .. NSEC_PER_SEC` range
100 100 let nanoseconds = metadata.mtime_nsec().try_into().unwrap();
101 101 Ok(Self::new_truncate(seconds, nanoseconds, false))
102 102 }
103 103 #[cfg(not(unix))]
104 104 {
105 105 metadata.modified().map(Self::from)
106 106 }
107 107 }
108 108
109 109 /// Like `for_mtime_of`, but may return `None` or a value with
110 110 /// `second_ambiguous` set if the mtime is not "reliable".
111 111 ///
112 112 /// A modification time is reliable if it is older than `boundary` (or
113 113 /// sufficiently in the future).
114 114 ///
115 115 /// Otherwise a concurrent modification might happens with the same mtime.
116 116 pub fn for_reliable_mtime_of(
117 117 metadata: &fs::Metadata,
118 118 boundary: &Self,
119 119 ) -> io::Result<Option<Self>> {
120 120 let mut mtime = Self::for_mtime_of(metadata)?;
121 121 // If the mtime of the ambiguous file is younger (or equal) to the
122 122 // starting point of the `status` walk, we cannot garantee that
123 123 // another, racy, write will not happen right after with the same mtime
124 124 // and we cannot cache the information.
125 125 //
126 126 // However if the mtime is far away in the future, this is likely some
127 127 // mismatch between the current clock and previous file system
128 128 // operation. So mtime more than one days in the future are considered
129 129 // fine.
130 130 let reliable = if mtime.truncated_seconds == boundary.truncated_seconds
131 131 {
132 132 mtime.second_ambiguous = true;
133 133 mtime.nanoseconds != 0
134 134 && boundary.nanoseconds != 0
135 135 && mtime.nanoseconds < boundary.nanoseconds
136 136 } else {
137 137 // `truncated_seconds` is less than 2**31,
138 138 // so this does not overflow `u32`:
139 139 let one_day_later = boundary.truncated_seconds + 24 * 3600;
140 140 mtime.truncated_seconds < boundary.truncated_seconds
141 141 || mtime.truncated_seconds > one_day_later
142 142 };
143 143 if reliable {
144 144 Ok(Some(mtime))
145 145 } else {
146 146 Ok(None)
147 147 }
148 148 }
149 149
150 150 /// The lower 31 bits of the number of seconds since the epoch.
151 151 pub fn truncated_seconds(&self) -> u32 {
152 152 self.truncated_seconds
153 153 }
154 154
155 155 /// The sub-second component of this timestamp, in nanoseconds.
156 156 /// Always in the `0 .. 1_000_000_000` range.
157 157 ///
158 158 /// This timestamp is after `(seconds, 0)` by this many nanoseconds.
159 159 pub fn nanoseconds(&self) -> u32 {
160 160 self.nanoseconds
161 161 }
162 162
163 163 /// Returns whether two timestamps are equal modulo 2**31 seconds.
164 164 ///
165 165 /// If this returns `true`, the original values converted from `SystemTime`
166 166 /// or given to `new_truncate` were very likely equal. A false positive is
167 167 /// possible if they were exactly a multiple of 2**31 seconds apart (around
168 168 /// 68 years). This is deemed very unlikely to happen by chance, especially
169 169 /// on filesystems that support sub-second precision.
170 170 ///
171 171 /// If someone is manipulating the modification times of some files to
172 172 /// intentionally make `hg status` return incorrect results, not truncating
173 173 /// wouldn’t help much since they can set exactly the expected timestamp.
174 174 ///
175 175 /// Sub-second precision is ignored if it is zero in either value.
176 176 /// Some APIs simply return zero when more precision is not available.
177 177 /// When comparing values from different sources, if only one is truncated
178 178 /// in that way, doing a simple comparison would cause many false
179 179 /// negatives.
180 180 pub fn likely_equal(self, other: Self) -> bool {
181 181 if self.truncated_seconds != other.truncated_seconds {
182 182 false
183 183 } else if self.nanoseconds == 0 || other.nanoseconds == 0 {
184 184 if self.second_ambiguous {
185 185 false
186 186 } else {
187 187 true
188 188 }
189 189 } else {
190 190 self.nanoseconds == other.nanoseconds
191 191 }
192 192 }
193 193
194 194 pub fn likely_equal_to_mtime_of(
195 195 self,
196 196 metadata: &fs::Metadata,
197 197 ) -> io::Result<bool> {
198 198 Ok(self.likely_equal(Self::for_mtime_of(metadata)?))
199 199 }
200 200 }
201 201
202 202 impl From<SystemTime> for TruncatedTimestamp {
203 203 fn from(system_time: SystemTime) -> Self {
204 204 // On Unix, `SystemTime` is a wrapper for the `timespec` C struct:
205 205 // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec
206 206 // We want to effectively access its fields, but the Rust standard
207 207 // library does not expose them. The best we can do is:
208 208 let seconds;
209 209 let nanoseconds;
210 210 match system_time.duration_since(UNIX_EPOCH) {
211 211 Ok(duration) => {
212 212 seconds = duration.as_secs() as i64;
213 213 nanoseconds = duration.subsec_nanos();
214 214 }
215 215 Err(error) => {
216 216 // `system_time` is before `UNIX_EPOCH`.
217 217 // We need to undo this algorithm:
218 218 // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41
219 219 let negative = error.duration();
220 220 let negative_secs = negative.as_secs() as i64;
221 221 let negative_nanos = negative.subsec_nanos();
222 222 if negative_nanos == 0 {
223 223 seconds = -negative_secs;
224 224 nanoseconds = 0;
225 225 } else {
226 226 // For example if `system_time` was 4.3 seconds before
227 227 // the Unix epoch we get a Duration that represents
228 228 // `(-4, -0.3)` but we want `(-5, +0.7)`:
229 229 seconds = -1 - negative_secs;
230 230 nanoseconds = NSEC_PER_SEC - negative_nanos;
231 231 }
232 232 }
233 233 };
234 234 Self::new_truncate(seconds, nanoseconds, false)
235 235 }
236 236 }
237 237
238 238 const NSEC_PER_SEC: u32 = 1_000_000_000;
239 239 pub const RANGE_MASK_31BIT: u32 = 0x7FFF_FFFF;
240 240
241 241 pub const MTIME_UNSET: i32 = -1;
242 242
243 243 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
244 244 /// other parent. This allows revert to pick the right status back during a
245 245 /// merge.
246 246 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
247 247 /// A special value used for internal representation of special case in
248 248 /// dirstate v1 format.
249 249 pub const SIZE_NON_NORMAL: i32 = -1;
250 250
251 251 impl DirstateEntry {
252 252 pub fn from_v2_data(
253 253 wdir_tracked: bool,
254 254 p1_tracked: bool,
255 255 p2_info: bool,
256 256 mode_size: Option<(u32, u32)>,
257 257 mtime: Option<TruncatedTimestamp>,
258 258 fallback_exec: Option<bool>,
259 259 fallback_symlink: Option<bool>,
260 260 ) -> Self {
261 261 if let Some((mode, size)) = mode_size {
262 262 // TODO: return an error for out of range values?
263 263 assert!(mode & !RANGE_MASK_31BIT == 0);
264 264 assert!(size & !RANGE_MASK_31BIT == 0);
265 265 }
266 266 let mut flags = Flags::empty();
267 267 flags.set(Flags::WDIR_TRACKED, wdir_tracked);
268 268 flags.set(Flags::P1_TRACKED, p1_tracked);
269 269 flags.set(Flags::P2_INFO, p2_info);
270 270 if let Some(exec) = fallback_exec {
271 271 flags.insert(Flags::HAS_FALLBACK_EXEC);
272 272 if exec {
273 273 flags.insert(Flags::FALLBACK_EXEC);
274 274 }
275 275 }
276 276 if let Some(exec) = fallback_symlink {
277 277 flags.insert(Flags::HAS_FALLBACK_SYMLINK);
278 278 if exec {
279 279 flags.insert(Flags::FALLBACK_SYMLINK);
280 280 }
281 281 }
282 282 Self {
283 283 flags,
284 284 mode_size,
285 285 mtime,
286 286 }
287 287 }
288 288
289 289 pub fn from_v1_data(
290 290 state: EntryState,
291 291 mode: i32,
292 292 size: i32,
293 293 mtime: i32,
294 294 ) -> Self {
295 295 match state {
296 296 EntryState::Normal => {
297 297 if size == SIZE_FROM_OTHER_PARENT {
298 298 Self {
299 299 // might be missing P1_TRACKED
300 300 flags: Flags::WDIR_TRACKED | Flags::P2_INFO,
301 301 mode_size: None,
302 302 mtime: None,
303 303 }
304 304 } else if size == SIZE_NON_NORMAL {
305 305 Self {
306 306 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
307 307 mode_size: None,
308 308 mtime: None,
309 309 }
310 310 } else if mtime == MTIME_UNSET {
311 311 // TODO: return an error for negative values?
312 312 let mode = u32::try_from(mode).unwrap();
313 313 let size = u32::try_from(size).unwrap();
314 314 Self {
315 315 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
316 316 mode_size: Some((mode, size)),
317 317 mtime: None,
318 318 }
319 319 } else {
320 320 // TODO: return an error for negative values?
321 321 let mode = u32::try_from(mode).unwrap();
322 322 let size = u32::try_from(size).unwrap();
323 323 let mtime = u32::try_from(mtime).unwrap();
324 324 let mtime = TruncatedTimestamp::from_already_truncated(
325 325 mtime, 0, false,
326 326 )
327 327 .unwrap();
328 328 Self {
329 329 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
330 330 mode_size: Some((mode, size)),
331 331 mtime: Some(mtime),
332 332 }
333 333 }
334 334 }
335 335 EntryState::Added => Self {
336 336 flags: Flags::WDIR_TRACKED,
337 337 mode_size: None,
338 338 mtime: None,
339 339 },
340 340 EntryState::Removed => Self {
341 341 flags: if size == SIZE_NON_NORMAL {
342 342 Flags::P1_TRACKED | Flags::P2_INFO
343 343 } else if size == SIZE_FROM_OTHER_PARENT {
344 344 // We don’t know if P1_TRACKED should be set (file history)
345 345 Flags::P2_INFO
346 346 } else {
347 347 Flags::P1_TRACKED
348 348 },
349 349 mode_size: None,
350 350 mtime: None,
351 351 },
352 352 EntryState::Merged => Self {
353 353 flags: Flags::WDIR_TRACKED
354 354 | Flags::P1_TRACKED // might not be true because of rename ?
355 355 | Flags::P2_INFO, // might not be true because of rename ?
356 356 mode_size: None,
357 357 mtime: None,
358 358 },
359 359 }
360 360 }
361 361
362 362 /// Creates a new entry in "removed" state.
363 363 ///
364 364 /// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
365 365 /// `SIZE_FROM_OTHER_PARENT`
366 366 pub fn new_removed(size: i32) -> Self {
367 367 Self::from_v1_data(EntryState::Removed, 0, size, 0)
368 368 }
369 369
370 370 pub fn tracked(&self) -> bool {
371 371 self.flags.contains(Flags::WDIR_TRACKED)
372 372 }
373 373
374 374 pub fn p1_tracked(&self) -> bool {
375 375 self.flags.contains(Flags::P1_TRACKED)
376 376 }
377 377
378 378 fn in_either_parent(&self) -> bool {
379 379 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO)
380 380 }
381 381
382 382 pub fn removed(&self) -> bool {
383 383 self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED)
384 384 }
385 385
386 386 pub fn p2_info(&self) -> bool {
387 387 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
388 388 }
389 389
390 390 pub fn added(&self) -> bool {
391 391 self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent()
392 392 }
393 393
394 394 pub fn maybe_clean(&self) -> bool {
395 395 if !self.flags.contains(Flags::WDIR_TRACKED) {
396 396 false
397 397 } else if !self.flags.contains(Flags::P1_TRACKED) {
398 398 false
399 399 } else if self.flags.contains(Flags::P2_INFO) {
400 400 false
401 401 } else {
402 402 true
403 403 }
404 404 }
405 405
406 406 pub fn any_tracked(&self) -> bool {
407 407 self.flags.intersects(
408 408 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
409 409 )
410 410 }
411 411
412 412 /// Returns `(wdir_tracked, p1_tracked, p2_info, mode_size, mtime)`
413 413 pub(crate) fn v2_data(
414 414 &self,
415 415 ) -> (
416 416 bool,
417 417 bool,
418 418 bool,
419 419 Option<(u32, u32)>,
420 420 Option<TruncatedTimestamp>,
421 421 Option<bool>,
422 422 Option<bool>,
423 423 ) {
424 424 if !self.any_tracked() {
425 425 // TODO: return an Option instead?
426 panic!("Accessing v1_state of an untracked DirstateEntry")
426 panic!("Accessing v2_data of an untracked DirstateEntry")
427 427 }
428 428 let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED);
429 429 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
430 430 let p2_info = self.flags.contains(Flags::P2_INFO);
431 431 let mode_size = self.mode_size;
432 432 let mtime = self.mtime;
433 433 (
434 434 wdir_tracked,
435 435 p1_tracked,
436 436 p2_info,
437 437 mode_size,
438 438 mtime,
439 439 self.get_fallback_exec(),
440 440 self.get_fallback_symlink(),
441 441 )
442 442 }
443 443
444 444 fn v1_state(&self) -> EntryState {
445 445 if !self.any_tracked() {
446 446 // TODO: return an Option instead?
447 447 panic!("Accessing v1_state of an untracked DirstateEntry")
448 448 }
449 449 if self.removed() {
450 450 EntryState::Removed
451 451 } else if self
452 452 .flags
453 453 .contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO)
454 454 {
455 455 EntryState::Merged
456 456 } else if self.added() {
457 457 EntryState::Added
458 458 } else {
459 459 EntryState::Normal
460 460 }
461 461 }
462 462
463 463 fn v1_mode(&self) -> i32 {
464 464 if let Some((mode, _size)) = self.mode_size {
465 465 i32::try_from(mode).unwrap()
466 466 } else {
467 467 0
468 468 }
469 469 }
470 470
471 471 fn v1_size(&self) -> i32 {
472 472 if !self.any_tracked() {
473 473 // TODO: return an Option instead?
474 474 panic!("Accessing v1_size of an untracked DirstateEntry")
475 475 }
476 476 if self.removed()
477 477 && self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO)
478 478 {
479 479 SIZE_NON_NORMAL
480 480 } else if self.flags.contains(Flags::P2_INFO) {
481 481 SIZE_FROM_OTHER_PARENT
482 482 } else if self.removed() {
483 483 0
484 484 } else if self.added() {
485 485 SIZE_NON_NORMAL
486 486 } else if let Some((_mode, size)) = self.mode_size {
487 487 i32::try_from(size).unwrap()
488 488 } else {
489 489 SIZE_NON_NORMAL
490 490 }
491 491 }
492 492
493 493 fn v1_mtime(&self) -> i32 {
494 494 if !self.any_tracked() {
495 495 // TODO: return an Option instead?
496 496 panic!("Accessing v1_mtime of an untracked DirstateEntry")
497 497 }
498 498 if self.removed() {
499 499 0
500 500 } else if self.flags.contains(Flags::P2_INFO) {
501 501 MTIME_UNSET
502 502 } else if !self.flags.contains(Flags::P1_TRACKED) {
503 503 MTIME_UNSET
504 504 } else if let Some(mtime) = self.mtime {
505 505 if mtime.second_ambiguous {
506 506 MTIME_UNSET
507 507 } else {
508 508 i32::try_from(mtime.truncated_seconds()).unwrap()
509 509 }
510 510 } else {
511 511 MTIME_UNSET
512 512 }
513 513 }
514 514
515 515 // TODO: return `Option<EntryState>`? None when `!self.any_tracked`
516 516 pub fn state(&self) -> EntryState {
517 517 self.v1_state()
518 518 }
519 519
520 520 // TODO: return Option?
521 521 pub fn mode(&self) -> i32 {
522 522 self.v1_mode()
523 523 }
524 524
525 525 // TODO: return Option?
526 526 pub fn size(&self) -> i32 {
527 527 self.v1_size()
528 528 }
529 529
530 530 // TODO: return Option?
531 531 pub fn mtime(&self) -> i32 {
532 532 self.v1_mtime()
533 533 }
534 534
535 535 pub fn get_fallback_exec(&self) -> Option<bool> {
536 536 if self.flags.contains(Flags::HAS_FALLBACK_EXEC) {
537 537 Some(self.flags.contains(Flags::FALLBACK_EXEC))
538 538 } else {
539 539 None
540 540 }
541 541 }
542 542
543 543 pub fn set_fallback_exec(&mut self, value: Option<bool>) {
544 544 match value {
545 545 None => {
546 546 self.flags.remove(Flags::HAS_FALLBACK_EXEC);
547 547 self.flags.remove(Flags::FALLBACK_EXEC);
548 548 }
549 549 Some(exec) => {
550 550 self.flags.insert(Flags::HAS_FALLBACK_EXEC);
551 551 if exec {
552 552 self.flags.insert(Flags::FALLBACK_EXEC);
553 553 }
554 554 }
555 555 }
556 556 }
557 557
558 558 pub fn get_fallback_symlink(&self) -> Option<bool> {
559 559 if self.flags.contains(Flags::HAS_FALLBACK_SYMLINK) {
560 560 Some(self.flags.contains(Flags::FALLBACK_SYMLINK))
561 561 } else {
562 562 None
563 563 }
564 564 }
565 565
566 566 pub fn set_fallback_symlink(&mut self, value: Option<bool>) {
567 567 match value {
568 568 None => {
569 569 self.flags.remove(Flags::HAS_FALLBACK_SYMLINK);
570 570 self.flags.remove(Flags::FALLBACK_SYMLINK);
571 571 }
572 572 Some(symlink) => {
573 573 self.flags.insert(Flags::HAS_FALLBACK_SYMLINK);
574 574 if symlink {
575 575 self.flags.insert(Flags::FALLBACK_SYMLINK);
576 576 }
577 577 }
578 578 }
579 579 }
580 580
581 581 pub fn truncated_mtime(&self) -> Option<TruncatedTimestamp> {
582 582 self.mtime
583 583 }
584 584
585 585 pub fn drop_merge_data(&mut self) {
586 586 if self.flags.contains(Flags::P2_INFO) {
587 587 self.flags.remove(Flags::P2_INFO);
588 588 self.mode_size = None;
589 589 self.mtime = None;
590 590 }
591 591 }
592 592
593 593 pub fn set_possibly_dirty(&mut self) {
594 594 self.mtime = None
595 595 }
596 596
597 597 pub fn set_clean(
598 598 &mut self,
599 599 mode: u32,
600 600 size: u32,
601 601 mtime: TruncatedTimestamp,
602 602 ) {
603 603 let size = size & RANGE_MASK_31BIT;
604 604 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
605 605 self.mode_size = Some((mode, size));
606 606 self.mtime = Some(mtime);
607 607 }
608 608
609 609 pub fn set_tracked(&mut self) {
610 610 self.flags.insert(Flags::WDIR_TRACKED);
611 611 // `set_tracked` is replacing various `normallookup` call. So we mark
612 612 // the files as needing lookup
613 613 //
614 614 // Consider dropping this in the future in favor of something less
615 615 // broad.
616 616 self.mtime = None;
617 617 }
618 618
619 619 pub fn set_untracked(&mut self) {
620 620 self.flags.remove(Flags::WDIR_TRACKED);
621 621 self.mode_size = None;
622 622 self.mtime = None;
623 623 }
624 624
625 625 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
626 626 /// in the dirstate-v1 format.
627 627 ///
628 628 /// This includes marker values such as `mtime == -1`. In the future we may
629 629 /// want to not represent these cases that way in memory, but serialization
630 630 /// will need to keep the same format.
631 631 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
632 632 (
633 633 self.v1_state().into(),
634 634 self.v1_mode(),
635 635 self.v1_size(),
636 636 self.v1_mtime(),
637 637 )
638 638 }
639 639
640 640 pub(crate) fn is_from_other_parent(&self) -> bool {
641 641 self.state() == EntryState::Normal
642 642 && self.size() == SIZE_FROM_OTHER_PARENT
643 643 }
644 644
645 645 // TODO: other platforms
646 646 #[cfg(unix)]
647 647 pub fn mode_changed(
648 648 &self,
649 649 filesystem_metadata: &std::fs::Metadata,
650 650 ) -> bool {
651 651 let dirstate_exec_bit = (self.mode() as u32 & EXEC_BIT_MASK) != 0;
652 652 let fs_exec_bit = has_exec_bit(filesystem_metadata);
653 653 dirstate_exec_bit != fs_exec_bit
654 654 }
655 655
656 656 /// Returns a `(state, mode, size, mtime)` tuple as for
657 657 /// `DirstateMapMethods::debug_iter`.
658 658 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
659 659 (self.state().into(), self.mode(), self.size(), self.mtime())
660 660 }
661 661 }
662 662
663 663 impl EntryState {
664 664 pub fn is_tracked(self) -> bool {
665 665 use EntryState::*;
666 666 match self {
667 667 Normal | Added | Merged => true,
668 668 Removed => false,
669 669 }
670 670 }
671 671 }
672 672
673 673 impl TryFrom<u8> for EntryState {
674 674 type Error = HgError;
675 675
676 676 fn try_from(value: u8) -> Result<Self, Self::Error> {
677 677 match value {
678 678 b'n' => Ok(EntryState::Normal),
679 679 b'a' => Ok(EntryState::Added),
680 680 b'r' => Ok(EntryState::Removed),
681 681 b'm' => Ok(EntryState::Merged),
682 682 _ => Err(HgError::CorruptedRepository(format!(
683 683 "Incorrect dirstate entry state {}",
684 684 value
685 685 ))),
686 686 }
687 687 }
688 688 }
689 689
690 690 impl Into<u8> for EntryState {
691 691 fn into(self) -> u8 {
692 692 match self {
693 693 EntryState::Normal => b'n',
694 694 EntryState::Added => b'a',
695 695 EntryState::Removed => b'r',
696 696 EntryState::Merged => b'm',
697 697 }
698 698 }
699 699 }
700 700
701 701 const EXEC_BIT_MASK: u32 = 0o100;
702 702
703 703 pub fn has_exec_bit(metadata: &std::fs::Metadata) -> bool {
704 704 // TODO: How to handle executable permissions on Windows?
705 705 use std::os::unix::fs::MetadataExt;
706 706 (metadata.mode() & EXEC_BIT_MASK) != 0
707 707 }
General Comments 0
You need to be logged in to leave comments. Login now