diff -r 2097f63575a5 -r 000130cfafb6 rust/rhg/src/commands/status.rs --- a/rust/rhg/src/commands/status.rs Thu Dec 02 15:10:03 2021 +0100 +++ b/rust/rhg/src/commands/status.rs Thu Nov 25 18:33:51 2021 +0100 @@ -13,14 +13,18 @@ use hg; use hg::config::Config; use hg::dirstate::has_exec_bit; -use hg::errors::HgError; +use hg::dirstate::TruncatedTimestamp; +use hg::dirstate::RANGE_MASK_31BIT; +use hg::errors::{HgError, IoResultExt}; +use hg::lock::LockError; use hg::manifest::Manifest; use hg::matchers::AlwaysMatcher; use hg::repo::Repo; use hg::utils::files::get_bytes_from_os_string; -use hg::utils::hg_path::{hg_path_to_os_string, HgPath}; +use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; use hg::{HgPathCow, StatusOptions}; use log::{info, warn}; +use std::io; pub const HELP_TEXT: &str = " Show changed files in the working directory @@ -229,6 +233,7 @@ &ds_status.unsure ); } + let mut fixup = Vec::new(); if !ds_status.unsure.is_empty() && (display_states.modified || display_states.clean) { @@ -243,8 +248,9 @@ } } else { if display_states.clean { - ds_status.clean.push(to_check); + ds_status.clean.push(to_check.clone()); } + fixup.push(to_check.into_owned()) } } } @@ -318,6 +324,71 @@ b"C", )?; } + + let mut dirstate_write_needed = ds_status.dirty; + let filesystem_time_at_status_start = ds_status + .filesystem_time_at_status_start + .map(TruncatedTimestamp::from); + + if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) + && !dirstate_write_needed + { + // Nothing to update + return Ok(()); + } + + // Update the dirstate on disk if we can + let with_lock_result = + repo.try_with_wlock_no_wait(|| -> Result<(), CommandError> { + if let Some(mtime_boundary) = filesystem_time_at_status_start { + for hg_path in fixup { + use std::os::unix::fs::MetadataExt; + let fs_path = hg_path_to_path_buf(&hg_path) + .expect("HgPath conversion"); + // Specifically do not reuse `fs_metadata` from + // `unsure_is_clean` which was needed before reading + // contents. Here we access metadata again after reading + // content, in case it changed in the meantime. + let fs_metadata = repo + .working_directory_vfs() + .symlink_metadata(&fs_path)?; + let mtime = TruncatedTimestamp::for_mtime_of(&fs_metadata) + .when_reading_file(&fs_path)?; + if mtime.is_reliable_mtime(&mtime_boundary) { + let mode = fs_metadata.mode(); + let size = fs_metadata.len() as u32 & RANGE_MASK_31BIT; + let mut entry = dmap + .get(&hg_path)? + .expect("ambiguous file not in dirstate"); + entry.set_clean(mode, size, mtime); + dmap.add_file(&hg_path, entry)?; + dirstate_write_needed = true + } + } + } + drop(dmap); // Avoid "already mutably borrowed" RefCell panics + if dirstate_write_needed { + repo.write_dirstate()? + } + Ok(()) + }); + match with_lock_result { + Ok(closure_result) => closure_result?, + Err(LockError::AlreadyHeld) => { + // Not updating the dirstate is not ideal but not critical: + // don’t keep our caller waiting until some other Mercurial + // process releases the lock. + } + Err(LockError::Other(HgError::IoError { error, .. })) + if error.kind() == io::ErrorKind::PermissionDenied => + { + // `hg status` on a read-only repository is fine + } + Err(LockError::Other(error)) => { + // Report other I/O errors + Err(error)? + } + } Ok(()) } @@ -368,7 +439,7 @@ hg_path: &HgPath, ) -> Result { let vfs = repo.working_directory_vfs(); - let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion"); + let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion"); let fs_metadata = vfs.symlink_metadata(&fs_path)?; let is_symlink = fs_metadata.file_type().is_symlink(); // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the @@ -399,5 +470,5 @@ } else { vfs.read(fs_path)? }; - return Ok(contents_in_p1 != &*fs_contents); + Ok(contents_in_p1 != &*fs_contents) }