##// END OF EJS Templates
rust-dirstatemap: stop using `.state` in `is_from_other_parent`...
Raphaël Gomès -
r50030:7241b372 default
parent child Browse files
Show More
@@ -1,724 +1,723 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 #[derive(Debug, Default, Copy, Clone)]
252 252 pub struct DirstateV2Data {
253 253 pub wc_tracked: bool,
254 254 pub p1_tracked: bool,
255 255 pub p2_info: bool,
256 256 pub mode_size: Option<(u32, u32)>,
257 257 pub mtime: Option<TruncatedTimestamp>,
258 258 pub fallback_exec: Option<bool>,
259 259 pub fallback_symlink: Option<bool>,
260 260 }
261 261
262 262 #[derive(Debug, Default, Copy, Clone)]
263 263 pub struct ParentFileData {
264 264 pub mode_size: Option<(u32, u32)>,
265 265 pub mtime: Option<TruncatedTimestamp>,
266 266 }
267 267
268 268 impl DirstateEntry {
269 269 pub fn from_v2_data(v2_data: DirstateV2Data) -> Self {
270 270 let DirstateV2Data {
271 271 wc_tracked,
272 272 p1_tracked,
273 273 p2_info,
274 274 mode_size,
275 275 mtime,
276 276 fallback_exec,
277 277 fallback_symlink,
278 278 } = v2_data;
279 279 if let Some((mode, size)) = mode_size {
280 280 // TODO: return an error for out of range values?
281 281 assert!(mode & !RANGE_MASK_31BIT == 0);
282 282 assert!(size & !RANGE_MASK_31BIT == 0);
283 283 }
284 284 let mut flags = Flags::empty();
285 285 flags.set(Flags::WDIR_TRACKED, wc_tracked);
286 286 flags.set(Flags::P1_TRACKED, p1_tracked);
287 287 flags.set(Flags::P2_INFO, p2_info);
288 288 if let Some(exec) = fallback_exec {
289 289 flags.insert(Flags::HAS_FALLBACK_EXEC);
290 290 if exec {
291 291 flags.insert(Flags::FALLBACK_EXEC);
292 292 }
293 293 }
294 294 if let Some(exec) = fallback_symlink {
295 295 flags.insert(Flags::HAS_FALLBACK_SYMLINK);
296 296 if exec {
297 297 flags.insert(Flags::FALLBACK_SYMLINK);
298 298 }
299 299 }
300 300 Self {
301 301 flags,
302 302 mode_size,
303 303 mtime,
304 304 }
305 305 }
306 306
307 307 pub fn from_v1_data(
308 308 state: EntryState,
309 309 mode: i32,
310 310 size: i32,
311 311 mtime: i32,
312 312 ) -> Self {
313 313 match state {
314 314 EntryState::Normal => {
315 315 if size == SIZE_FROM_OTHER_PARENT {
316 316 Self {
317 317 // might be missing P1_TRACKED
318 318 flags: Flags::WDIR_TRACKED | Flags::P2_INFO,
319 319 mode_size: None,
320 320 mtime: None,
321 321 }
322 322 } else if size == SIZE_NON_NORMAL {
323 323 Self {
324 324 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
325 325 mode_size: None,
326 326 mtime: None,
327 327 }
328 328 } else if mtime == MTIME_UNSET {
329 329 // TODO: return an error for negative values?
330 330 let mode = u32::try_from(mode).unwrap();
331 331 let size = u32::try_from(size).unwrap();
332 332 Self {
333 333 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
334 334 mode_size: Some((mode, size)),
335 335 mtime: None,
336 336 }
337 337 } else {
338 338 // TODO: return an error for negative values?
339 339 let mode = u32::try_from(mode).unwrap();
340 340 let size = u32::try_from(size).unwrap();
341 341 let mtime = u32::try_from(mtime).unwrap();
342 342 let mtime = TruncatedTimestamp::from_already_truncated(
343 343 mtime, 0, false,
344 344 )
345 345 .unwrap();
346 346 Self {
347 347 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
348 348 mode_size: Some((mode, size)),
349 349 mtime: Some(mtime),
350 350 }
351 351 }
352 352 }
353 353 EntryState::Added => Self {
354 354 flags: Flags::WDIR_TRACKED,
355 355 mode_size: None,
356 356 mtime: None,
357 357 },
358 358 EntryState::Removed => Self {
359 359 flags: if size == SIZE_NON_NORMAL {
360 360 Flags::P1_TRACKED | Flags::P2_INFO
361 361 } else if size == SIZE_FROM_OTHER_PARENT {
362 362 // We don’t know if P1_TRACKED should be set (file history)
363 363 Flags::P2_INFO
364 364 } else {
365 365 Flags::P1_TRACKED
366 366 },
367 367 mode_size: None,
368 368 mtime: None,
369 369 },
370 370 EntryState::Merged => Self {
371 371 flags: Flags::WDIR_TRACKED
372 372 | Flags::P1_TRACKED // might not be true because of rename ?
373 373 | Flags::P2_INFO, // might not be true because of rename ?
374 374 mode_size: None,
375 375 mtime: None,
376 376 },
377 377 }
378 378 }
379 379
380 380 /// Creates a new entry in "removed" state.
381 381 ///
382 382 /// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
383 383 /// `SIZE_FROM_OTHER_PARENT`
384 384 pub fn new_removed(size: i32) -> Self {
385 385 Self::from_v1_data(EntryState::Removed, 0, size, 0)
386 386 }
387 387
388 388 pub fn new_tracked() -> Self {
389 389 let data = DirstateV2Data {
390 390 wc_tracked: true,
391 391 ..Default::default()
392 392 };
393 393 Self::from_v2_data(data)
394 394 }
395 395
396 396 pub fn tracked(&self) -> bool {
397 397 self.flags.contains(Flags::WDIR_TRACKED)
398 398 }
399 399
400 400 pub fn p1_tracked(&self) -> bool {
401 401 self.flags.contains(Flags::P1_TRACKED)
402 402 }
403 403
404 404 fn in_either_parent(&self) -> bool {
405 405 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO)
406 406 }
407 407
408 408 pub fn removed(&self) -> bool {
409 409 self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED)
410 410 }
411 411
412 412 pub fn p2_info(&self) -> bool {
413 413 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
414 414 }
415 415
416 416 pub fn added(&self) -> bool {
417 417 self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent()
418 418 }
419 419
420 420 pub fn modified(&self) -> bool {
421 421 self.flags
422 422 .contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO)
423 423 }
424 424
425 425 pub fn maybe_clean(&self) -> bool {
426 426 if !self.flags.contains(Flags::WDIR_TRACKED) {
427 427 false
428 428 } else if !self.flags.contains(Flags::P1_TRACKED) {
429 429 false
430 430 } else if self.flags.contains(Flags::P2_INFO) {
431 431 false
432 432 } else {
433 433 true
434 434 }
435 435 }
436 436
437 437 pub fn any_tracked(&self) -> bool {
438 438 self.flags.intersects(
439 439 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
440 440 )
441 441 }
442 442
443 443 pub(crate) fn v2_data(&self) -> DirstateV2Data {
444 444 if !self.any_tracked() {
445 445 // TODO: return an Option instead?
446 446 panic!("Accessing v2_data of an untracked DirstateEntry")
447 447 }
448 448 let wc_tracked = self.flags.contains(Flags::WDIR_TRACKED);
449 449 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
450 450 let p2_info = self.flags.contains(Flags::P2_INFO);
451 451 let mode_size = self.mode_size;
452 452 let mtime = self.mtime;
453 453 DirstateV2Data {
454 454 wc_tracked,
455 455 p1_tracked,
456 456 p2_info,
457 457 mode_size,
458 458 mtime,
459 459 fallback_exec: self.get_fallback_exec(),
460 460 fallback_symlink: self.get_fallback_symlink(),
461 461 }
462 462 }
463 463
464 464 fn v1_state(&self) -> EntryState {
465 465 if !self.any_tracked() {
466 466 // TODO: return an Option instead?
467 467 panic!("Accessing v1_state of an untracked DirstateEntry")
468 468 }
469 469 if self.removed() {
470 470 EntryState::Removed
471 471 } else if self.modified() {
472 472 EntryState::Merged
473 473 } else if self.added() {
474 474 EntryState::Added
475 475 } else {
476 476 EntryState::Normal
477 477 }
478 478 }
479 479
480 480 fn v1_mode(&self) -> i32 {
481 481 if let Some((mode, _size)) = self.mode_size {
482 482 i32::try_from(mode).unwrap()
483 483 } else {
484 484 0
485 485 }
486 486 }
487 487
488 488 fn v1_size(&self) -> i32 {
489 489 if !self.any_tracked() {
490 490 // TODO: return an Option instead?
491 491 panic!("Accessing v1_size of an untracked DirstateEntry")
492 492 }
493 493 if self.removed()
494 494 && self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO)
495 495 {
496 496 SIZE_NON_NORMAL
497 497 } else if self.flags.contains(Flags::P2_INFO) {
498 498 SIZE_FROM_OTHER_PARENT
499 499 } else if self.removed() {
500 500 0
501 501 } else if self.added() {
502 502 SIZE_NON_NORMAL
503 503 } else if let Some((_mode, size)) = self.mode_size {
504 504 i32::try_from(size).unwrap()
505 505 } else {
506 506 SIZE_NON_NORMAL
507 507 }
508 508 }
509 509
510 510 fn v1_mtime(&self) -> i32 {
511 511 if !self.any_tracked() {
512 512 // TODO: return an Option instead?
513 513 panic!("Accessing v1_mtime of an untracked DirstateEntry")
514 514 }
515 515 if self.removed() {
516 516 0
517 517 } else if self.flags.contains(Flags::P2_INFO) {
518 518 MTIME_UNSET
519 519 } else if !self.flags.contains(Flags::P1_TRACKED) {
520 520 MTIME_UNSET
521 521 } else if let Some(mtime) = self.mtime {
522 522 if mtime.second_ambiguous {
523 523 MTIME_UNSET
524 524 } else {
525 525 i32::try_from(mtime.truncated_seconds()).unwrap()
526 526 }
527 527 } else {
528 528 MTIME_UNSET
529 529 }
530 530 }
531 531
532 532 // TODO: return `Option<EntryState>`? None when `!self.any_tracked`
533 533 pub fn state(&self) -> EntryState {
534 534 self.v1_state()
535 535 }
536 536
537 537 // TODO: return Option?
538 538 pub fn mode(&self) -> i32 {
539 539 self.v1_mode()
540 540 }
541 541
542 542 // TODO: return Option?
543 543 pub fn size(&self) -> i32 {
544 544 self.v1_size()
545 545 }
546 546
547 547 // TODO: return Option?
548 548 pub fn mtime(&self) -> i32 {
549 549 self.v1_mtime()
550 550 }
551 551
552 552 pub fn get_fallback_exec(&self) -> Option<bool> {
553 553 if self.flags.contains(Flags::HAS_FALLBACK_EXEC) {
554 554 Some(self.flags.contains(Flags::FALLBACK_EXEC))
555 555 } else {
556 556 None
557 557 }
558 558 }
559 559
560 560 pub fn set_fallback_exec(&mut self, value: Option<bool>) {
561 561 match value {
562 562 None => {
563 563 self.flags.remove(Flags::HAS_FALLBACK_EXEC);
564 564 self.flags.remove(Flags::FALLBACK_EXEC);
565 565 }
566 566 Some(exec) => {
567 567 self.flags.insert(Flags::HAS_FALLBACK_EXEC);
568 568 if exec {
569 569 self.flags.insert(Flags::FALLBACK_EXEC);
570 570 }
571 571 }
572 572 }
573 573 }
574 574
575 575 pub fn get_fallback_symlink(&self) -> Option<bool> {
576 576 if self.flags.contains(Flags::HAS_FALLBACK_SYMLINK) {
577 577 Some(self.flags.contains(Flags::FALLBACK_SYMLINK))
578 578 } else {
579 579 None
580 580 }
581 581 }
582 582
583 583 pub fn set_fallback_symlink(&mut self, value: Option<bool>) {
584 584 match value {
585 585 None => {
586 586 self.flags.remove(Flags::HAS_FALLBACK_SYMLINK);
587 587 self.flags.remove(Flags::FALLBACK_SYMLINK);
588 588 }
589 589 Some(symlink) => {
590 590 self.flags.insert(Flags::HAS_FALLBACK_SYMLINK);
591 591 if symlink {
592 592 self.flags.insert(Flags::FALLBACK_SYMLINK);
593 593 }
594 594 }
595 595 }
596 596 }
597 597
598 598 pub fn truncated_mtime(&self) -> Option<TruncatedTimestamp> {
599 599 self.mtime
600 600 }
601 601
602 602 pub fn drop_merge_data(&mut self) {
603 603 if self.flags.contains(Flags::P2_INFO) {
604 604 self.flags.remove(Flags::P2_INFO);
605 605 self.mode_size = None;
606 606 self.mtime = None;
607 607 }
608 608 }
609 609
610 610 pub fn set_possibly_dirty(&mut self) {
611 611 self.mtime = None
612 612 }
613 613
614 614 pub fn set_clean(
615 615 &mut self,
616 616 mode: u32,
617 617 size: u32,
618 618 mtime: TruncatedTimestamp,
619 619 ) {
620 620 let size = size & RANGE_MASK_31BIT;
621 621 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
622 622 self.mode_size = Some((mode, size));
623 623 self.mtime = Some(mtime);
624 624 }
625 625
626 626 pub fn set_tracked(&mut self) {
627 627 self.flags.insert(Flags::WDIR_TRACKED);
628 628 // `set_tracked` is replacing various `normallookup` call. So we mark
629 629 // the files as needing lookup
630 630 //
631 631 // Consider dropping this in the future in favor of something less
632 632 // broad.
633 633 self.mtime = None;
634 634 }
635 635
636 636 pub fn set_untracked(&mut self) {
637 637 self.flags.remove(Flags::WDIR_TRACKED);
638 638 self.mode_size = None;
639 639 self.mtime = None;
640 640 }
641 641
642 642 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
643 643 /// in the dirstate-v1 format.
644 644 ///
645 645 /// This includes marker values such as `mtime == -1`. In the future we may
646 646 /// want to not represent these cases that way in memory, but serialization
647 647 /// will need to keep the same format.
648 648 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
649 649 (
650 650 self.v1_state().into(),
651 651 self.v1_mode(),
652 652 self.v1_size(),
653 653 self.v1_mtime(),
654 654 )
655 655 }
656 656
657 657 pub(crate) fn is_from_other_parent(&self) -> bool {
658 self.state() == EntryState::Normal
659 && self.size() == SIZE_FROM_OTHER_PARENT
658 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
660 659 }
661 660
662 661 // TODO: other platforms
663 662 #[cfg(unix)]
664 663 pub fn mode_changed(
665 664 &self,
666 665 filesystem_metadata: &std::fs::Metadata,
667 666 ) -> bool {
668 667 let dirstate_exec_bit = (self.mode() as u32 & EXEC_BIT_MASK) != 0;
669 668 let fs_exec_bit = has_exec_bit(filesystem_metadata);
670 669 dirstate_exec_bit != fs_exec_bit
671 670 }
672 671
673 672 /// Returns a `(state, mode, size, mtime)` tuple as for
674 673 /// `DirstateMapMethods::debug_iter`.
675 674 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
676 675 (self.state().into(), self.mode(), self.size(), self.mtime())
677 676 }
678 677 }
679 678
680 679 impl EntryState {
681 680 pub fn is_tracked(self) -> bool {
682 681 use EntryState::*;
683 682 match self {
684 683 Normal | Added | Merged => true,
685 684 Removed => false,
686 685 }
687 686 }
688 687 }
689 688
690 689 impl TryFrom<u8> for EntryState {
691 690 type Error = HgError;
692 691
693 692 fn try_from(value: u8) -> Result<Self, Self::Error> {
694 693 match value {
695 694 b'n' => Ok(EntryState::Normal),
696 695 b'a' => Ok(EntryState::Added),
697 696 b'r' => Ok(EntryState::Removed),
698 697 b'm' => Ok(EntryState::Merged),
699 698 _ => Err(HgError::CorruptedRepository(format!(
700 699 "Incorrect dirstate entry state {}",
701 700 value
702 701 ))),
703 702 }
704 703 }
705 704 }
706 705
707 706 impl Into<u8> for EntryState {
708 707 fn into(self) -> u8 {
709 708 match self {
710 709 EntryState::Normal => b'n',
711 710 EntryState::Added => b'a',
712 711 EntryState::Removed => b'r',
713 712 EntryState::Merged => b'm',
714 713 }
715 714 }
716 715 }
717 716
718 717 const EXEC_BIT_MASK: u32 = 0o100;
719 718
720 719 pub fn has_exec_bit(metadata: &std::fs::Metadata) -> bool {
721 720 // TODO: How to handle executable permissions on Windows?
722 721 use std::os::unix::fs::MetadataExt;
723 722 (metadata.mode() & EXEC_BIT_MASK) != 0
724 723 }
General Comments 0
You need to be logged in to leave comments. Login now