rust/rhg/src/commands/status.rs
changeset 50252 a6b8b1ab9116
parent 50094 1cffc156f7cd
parent 50226 8fcd5302243a
child 50361 668a871454e8
child 50427 98fc949bec14
equal deleted inserted replaced
50248: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 }