--- 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<bool, HgError> {
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)
}