9 use crate::ui::{Ui, UiError}; |
9 use crate::ui::{Ui, UiError}; |
10 use crate::utils::path_utils::relativize_paths; |
10 use crate::utils::path_utils::relativize_paths; |
11 use clap::{Arg, SubCommand}; |
11 use clap::{Arg, SubCommand}; |
12 use hg; |
12 use hg; |
13 use hg::config::Config; |
13 use hg::config::Config; |
14 use hg::dirstate::TruncatedTimestamp; |
14 use hg::dirstate::{has_exec_bit, TruncatedTimestamp}; |
15 use hg::errors::HgError; |
15 use hg::errors::HgError; |
16 use hg::manifest::Manifest; |
16 use hg::manifest::Manifest; |
17 use hg::matchers::AlwaysMatcher; |
17 use hg::matchers::AlwaysMatcher; |
18 use hg::repo::Repo; |
18 use hg::repo::Repo; |
|
19 use hg::utils::files::get_bytes_from_os_string; |
19 use hg::utils::hg_path::{hg_path_to_os_string, HgPath}; |
20 use hg::utils::hg_path::{hg_path_to_os_string, HgPath}; |
20 use hg::{HgPathCow, StatusOptions}; |
21 use hg::{HgPathCow, StatusOptions}; |
21 use log::{info, warn}; |
22 use log::{info, warn}; |
22 use std::borrow::Cow; |
23 use std::borrow::Cow; |
23 |
24 |
300 |
301 |
301 /// Check if a file is modified by comparing actual repo store and file system. |
302 /// Check if a file is modified by comparing actual repo store and file system. |
302 /// |
303 /// |
303 /// This meant to be used for those that the dirstate cannot resolve, due |
304 /// This meant to be used for those that the dirstate cannot resolve, due |
304 /// to time resolution limits. |
305 /// to time resolution limits. |
305 /// |
|
306 /// TODO: detect permission bits and similar metadata modifications |
|
307 fn unsure_is_modified( |
306 fn unsure_is_modified( |
308 repo: &Repo, |
307 repo: &Repo, |
309 manifest: &Manifest, |
308 manifest: &Manifest, |
310 hg_path: &HgPath, |
309 hg_path: &HgPath, |
311 ) -> Result<bool, HgError> { |
310 ) -> Result<bool, HgError> { |
|
311 let vfs = repo.working_directory_vfs(); |
|
312 let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion"); |
|
313 let fs_metadata = vfs.symlink_metadata(&fs_path)?; |
|
314 let is_symlink = fs_metadata.file_type().is_symlink(); |
|
315 // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the dirstate |
|
316 let fs_flags = if is_symlink { |
|
317 Some(b'l') |
|
318 } else if has_exec_bit(&fs_metadata) { |
|
319 Some(b'x') |
|
320 } else { |
|
321 None |
|
322 }; |
|
323 |
312 let entry = manifest |
324 let entry = manifest |
313 .find_file(hg_path)? |
325 .find_file(hg_path)? |
314 .expect("ambgious file not in p1"); |
326 .expect("ambgious file not in p1"); |
|
327 if entry.flags != fs_flags { |
|
328 return Ok(true); |
|
329 } |
315 let filelog = repo.filelog(hg_path)?; |
330 let filelog = repo.filelog(hg_path)?; |
316 let filelog_entry = |
331 let filelog_entry = |
317 filelog.data_for_node(entry.node_id()?).map_err(|_| { |
332 filelog.data_for_node(entry.node_id()?).map_err(|_| { |
318 HgError::corrupted("filelog missing node from manifest") |
333 HgError::corrupted("filelog missing node from manifest") |
319 })?; |
334 })?; |
320 let contents_in_p1 = filelog_entry.data()?; |
335 let contents_in_p1 = filelog_entry.data()?; |
321 |
336 |
322 let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion"); |
337 let fs_contents = if is_symlink { |
323 let fs_contents = repo.working_directory_vfs().read(fs_path)?; |
338 get_bytes_from_os_string(vfs.read_link(fs_path)?.into_os_string()) |
|
339 } else { |
|
340 vfs.read(fs_path)? |
|
341 }; |
324 return Ok(contents_in_p1 != &*fs_contents); |
342 return Ok(contents_in_p1 != &*fs_contents); |
325 } |
343 } |