Mercurial > public > mercurial-scm > hg-stable
comparison rust/rhg/src/commands/status.rs @ 50252:a6b8b1ab9116
branching: merge stable into default
The clippy god had to be appeased on some aspect.
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Thu, 02 Mar 2023 19:02:52 +0100 |
parents | 1cffc156f7cd 8fcd5302243a |
children | 668a871454e8 98fc949bec14 |
comparison
equal
deleted
inserted
replaced
50251:2fbc109fd58a | 50252:a6b8b1ab9116 |
---|---|
19 use hg::errors::{HgError, IoResultExt}; | 19 use hg::errors::{HgError, IoResultExt}; |
20 use hg::lock::LockError; | 20 use hg::lock::LockError; |
21 use hg::manifest::Manifest; | 21 use hg::manifest::Manifest; |
22 use hg::matchers::{AlwaysMatcher, IntersectionMatcher}; | 22 use hg::matchers::{AlwaysMatcher, IntersectionMatcher}; |
23 use hg::repo::Repo; | 23 use hg::repo::Repo; |
24 use hg::utils::debug::debug_wait_for_file; | |
24 use hg::utils::files::get_bytes_from_os_string; | 25 use hg::utils::files::get_bytes_from_os_string; |
25 use hg::utils::files::get_path_from_bytes; | 26 use hg::utils::files::get_path_from_bytes; |
26 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; | 27 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; |
27 use hg::DirstateStatus; | 28 use hg::DirstateStatus; |
28 use hg::PatternFileWarning; | 29 use hg::PatternFileWarning; |
306 let store_vfs = repo.store_vfs(); | 307 let store_vfs = repo.store_vfs(); |
307 let res: Vec<_> = ds_status | 308 let res: Vec<_> = ds_status |
308 .unsure | 309 .unsure |
309 .into_par_iter() | 310 .into_par_iter() |
310 .map(|to_check| { | 311 .map(|to_check| { |
311 unsure_is_modified( | 312 // The compiler seems to get a bit confused with complex |
313 // inference when using a parallel iterator + map | |
314 // + map_err + collect, so let's just inline some of the | |
315 // logic. | |
316 match unsure_is_modified( | |
312 working_directory_vfs, | 317 working_directory_vfs, |
313 store_vfs, | 318 store_vfs, |
314 check_exec, | 319 check_exec, |
315 &manifest, | 320 &manifest, |
316 &to_check.path, | 321 &to_check.path, |
317 ) | 322 ) { |
318 .map(|modified| (to_check, modified)) | 323 Err(HgError::IoError { .. }) => { |
324 // IO errors most likely stem from the file being | |
325 // deleted even though we know it's in the | |
326 // dirstate. | |
327 Ok((to_check, UnsureOutcome::Deleted)) | |
328 } | |
329 Ok(outcome) => Ok((to_check, outcome)), | |
330 Err(e) => Err(e), | |
331 } | |
319 }) | 332 }) |
320 .collect::<Result<_, _>>()?; | 333 .collect::<Result<_, _>>()?; |
321 for (status_path, is_modified) in res.into_iter() { | 334 for (status_path, outcome) in res.into_iter() { |
322 if is_modified { | 335 match outcome { |
323 if display_states.modified { | 336 UnsureOutcome::Clean => { |
324 ds_status.modified.push(status_path); | 337 if display_states.clean { |
338 ds_status.clean.push(status_path.clone()); | |
339 } | |
340 fixup.push(status_path.path.into_owned()) | |
325 } | 341 } |
326 } else { | 342 UnsureOutcome::Modified => { |
327 if display_states.clean { | 343 if display_states.modified { |
328 ds_status.clean.push(status_path.clone()); | 344 ds_status.modified.push(status_path); |
345 } | |
329 } | 346 } |
330 fixup.push(status_path.path.into_owned()) | 347 UnsureOutcome::Deleted => { |
348 if display_states.deleted { | |
349 ds_status.deleted.push(status_path); | |
350 } | |
351 } | |
331 } | 352 } |
332 } | 353 } |
333 } | 354 } |
334 let relative_paths = config | 355 let relative_paths = config |
335 .get_option(b"commands", b"status.relative")? | 356 .get_option(b"commands", b"status.relative")? |
399 ignore_files(repo, config), | 420 ignore_files(repo, config), |
400 options, | 421 options, |
401 after_status, | 422 after_status, |
402 )?; | 423 )?; |
403 | 424 |
425 // Development config option to test write races | |
426 if let Err(e) = | |
427 debug_wait_for_file(config, "status.pre-dirstate-write-file") | |
428 { | |
429 ui.write_stderr(e.as_bytes()).ok(); | |
430 } | |
431 | |
404 if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) | 432 if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) |
405 && !dirstate_write_needed | 433 && !dirstate_write_needed |
406 { | 434 { |
407 // Nothing to update | 435 // Nothing to update |
408 return Ok(()); | 436 return Ok(()); |
418 .expect("HgPath conversion"); | 446 .expect("HgPath conversion"); |
419 // Specifically do not reuse `fs_metadata` from | 447 // Specifically do not reuse `fs_metadata` from |
420 // `unsure_is_clean` which was needed before reading | 448 // `unsure_is_clean` which was needed before reading |
421 // contents. Here we access metadata again after reading | 449 // contents. Here we access metadata again after reading |
422 // content, in case it changed in the meantime. | 450 // content, in case it changed in the meantime. |
423 let fs_metadata = repo | 451 let metadata_res = repo |
424 .working_directory_vfs() | 452 .working_directory_vfs() |
425 .symlink_metadata(&fs_path)?; | 453 .symlink_metadata(&fs_path); |
454 let fs_metadata = match metadata_res { | |
455 Ok(meta) => meta, | |
456 Err(err) => match err { | |
457 HgError::IoError { .. } => { | |
458 // The file has probably been deleted. In any | |
459 // case, it was in the dirstate before, so | |
460 // let's ignore the error. | |
461 continue; | |
462 } | |
463 _ => return Err(err.into()), | |
464 }, | |
465 }; | |
426 if let Some(mtime) = | 466 if let Some(mtime) = |
427 TruncatedTimestamp::for_reliable_mtime_of( | 467 TruncatedTimestamp::for_reliable_mtime_of( |
428 &fs_metadata, | 468 &fs_metadata, |
429 &mtime_boundary, | 469 &mtime_boundary, |
430 ) | 470 ) |
447 Ok(closure_result) => closure_result?, | 487 Ok(closure_result) => closure_result?, |
448 Err(LockError::AlreadyHeld) => { | 488 Err(LockError::AlreadyHeld) => { |
449 // Not updating the dirstate is not ideal but not critical: | 489 // Not updating the dirstate is not ideal but not critical: |
450 // don’t keep our caller waiting until some other Mercurial | 490 // don’t keep our caller waiting until some other Mercurial |
451 // process releases the lock. | 491 // process releases the lock. |
492 log::info!("not writing dirstate from `status`: lock is held") | |
452 } | 493 } |
453 Err(LockError::Other(HgError::IoError { error, .. })) | 494 Err(LockError::Other(HgError::IoError { error, .. })) |
454 if error.kind() == io::ErrorKind::PermissionDenied => | 495 if error.kind() == io::ErrorKind::PermissionDenied => |
455 { | 496 { |
456 // `hg status` on a read-only repository is fine | 497 // `hg status` on a read-only repository is fine |
526 } | 567 } |
527 Ok(()) | 568 Ok(()) |
528 } | 569 } |
529 } | 570 } |
530 | 571 |
572 /// Outcome of the additional check for an ambiguous tracked file | |
573 enum UnsureOutcome { | |
574 /// The file is actually clean | |
575 Clean, | |
576 /// The file has been modified | |
577 Modified, | |
578 /// The file was deleted on disk (or became another type of fs entry) | |
579 Deleted, | |
580 } | |
581 | |
531 /// Check if a file is modified by comparing actual repo store and file system. | 582 /// Check if a file is modified by comparing actual repo store and file system. |
532 /// | 583 /// |
533 /// This meant to be used for those that the dirstate cannot resolve, due | 584 /// This meant to be used for those that the dirstate cannot resolve, due |
534 /// to time resolution limits. | 585 /// to time resolution limits. |
535 fn unsure_is_modified( | 586 fn unsure_is_modified( |
536 working_directory_vfs: hg::vfs::Vfs, | 587 working_directory_vfs: hg::vfs::Vfs, |
537 store_vfs: hg::vfs::Vfs, | 588 store_vfs: hg::vfs::Vfs, |
538 check_exec: bool, | 589 check_exec: bool, |
539 manifest: &Manifest, | 590 manifest: &Manifest, |
540 hg_path: &HgPath, | 591 hg_path: &HgPath, |
541 ) -> Result<bool, HgError> { | 592 ) -> Result<UnsureOutcome, HgError> { |
542 let vfs = working_directory_vfs; | 593 let vfs = working_directory_vfs; |
543 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion"); | 594 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion"); |
544 let fs_metadata = vfs.symlink_metadata(&fs_path)?; | 595 let fs_metadata = vfs.symlink_metadata(&fs_path)?; |
545 let is_symlink = fs_metadata.file_type().is_symlink(); | 596 let is_symlink = fs_metadata.file_type().is_symlink(); |
546 | 597 |
565 } else { | 616 } else { |
566 entry.flags | 617 entry.flags |
567 }; | 618 }; |
568 | 619 |
569 if entry_flags != fs_flags { | 620 if entry_flags != fs_flags { |
570 return Ok(true); | 621 return Ok(UnsureOutcome::Modified); |
571 } | 622 } |
572 let filelog = hg::filelog::Filelog::open_vfs(&store_vfs, hg_path)?; | 623 let filelog = hg::filelog::Filelog::open_vfs(&store_vfs, hg_path)?; |
573 let fs_len = fs_metadata.len(); | 624 let fs_len = fs_metadata.len(); |
574 let file_node = entry.node_id()?; | 625 let file_node = entry.node_id()?; |
575 let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| { | 626 let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| { |
579 )) | 630 )) |
580 })?; | 631 })?; |
581 if filelog_entry.file_data_len_not_equal_to(fs_len) { | 632 if filelog_entry.file_data_len_not_equal_to(fs_len) { |
582 // No need to read file contents: | 633 // No need to read file contents: |
583 // it cannot be equal if it has a different length. | 634 // it cannot be equal if it has a different length. |
584 return Ok(true); | 635 return Ok(UnsureOutcome::Modified); |
585 } | 636 } |
586 | 637 |
587 let p1_filelog_data = filelog_entry.data()?; | 638 let p1_filelog_data = filelog_entry.data()?; |
588 let p1_contents = p1_filelog_data.file_data()?; | 639 let p1_contents = p1_filelog_data.file_data()?; |
589 if p1_contents.len() as u64 != fs_len { | 640 if p1_contents.len() as u64 != fs_len { |
590 // No need to read file contents: | 641 // No need to read file contents: |
591 // it cannot be equal if it has a different length. | 642 // it cannot be equal if it has a different length. |
592 return Ok(true); | 643 return Ok(UnsureOutcome::Modified); |
593 } | 644 } |
594 | 645 |
595 let fs_contents = if is_symlink { | 646 let fs_contents = if is_symlink { |
596 get_bytes_from_os_string(vfs.read_link(fs_path)?.into_os_string()) | 647 get_bytes_from_os_string(vfs.read_link(fs_path)?.into_os_string()) |
597 } else { | 648 } else { |
598 vfs.read(fs_path)? | 649 vfs.read(fs_path)? |
599 }; | 650 }; |
600 Ok(p1_contents != &*fs_contents) | 651 |
601 } | 652 Ok(if p1_contents != &*fs_contents { |
653 UnsureOutcome::Modified | |
654 } else { | |
655 UnsureOutcome::Clean | |
656 }) | |
657 } |