##// END OF EJS Templates
dirstate-v2: add test that shows a collision in ignore patterns hash...
dirstate-v2: add test that shows a collision in ignore patterns hash This hash is used for optimizing dirstate `status`. We demonstrate that the hash is incorrectly ignoring the changes to the semantics of the ignore files just because the contents (but not their source) haven't changed. This is fixed in the next changeset.

File last commit:

r50412:52464a20 default
r50452:ca19335e stable
Show More
status.rs
625 lines | 20.1 KiB | application/rls-services+xml | RustLexer
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 // status.rs
//
// Copyright 2020, Georges Racinet <georges.racinets@octobus.net>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
use crate::error::CommandError;
Simon Sapin
rhg: Add support for `rhg status -n`...
r49171 use crate::ui::Ui;
Simon Sapin
rhg: refactor relativize_path into a struct + method...
r49284 use crate::utils::path_utils::RelativizePaths;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 use clap::{Arg, SubCommand};
Simon Sapin
rhg: Add support for `rhg status -n`...
r49171 use format_bytes::format_bytes;
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 use hg::config::Config;
dirstate: remove `lastnormaltime` mechanism...
r49220 use hg::dirstate::has_exec_bit;
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 use hg::dirstate::status::StatusPath;
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 use hg::dirstate::TruncatedTimestamp;
use hg::errors::{HgError, IoResultExt};
use hg::lock::LockError;
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 use hg::manifest::Manifest;
Raphaël Gomès
rhg-status: add support for narrow clones
r50383 use hg::matchers::{AlwaysMatcher, IntersectionMatcher};
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 use hg::repo::Repo;
Simon Sapin
rhg: Fix status desambiguation of symlinks and executable files...
r49168 use hg::utils::files::get_bytes_from_os_string;
Simon Sapin
rhg: Properly format warnings related to ignore patterns...
r49342 use hg::utils::files::get_bytes_from_path;
Simon Sapin
rhg: Add support for ui.ignore and ui.ignore.* config...
r49282 use hg::utils::files::get_path_from_bytes;
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath};
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 use hg::DirstateStatus;
use hg::PatternFileWarning;
use hg::StatusError;
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 use hg::StatusOptions;
Raphaël Gomès
rhg-status: add support for narrow clones
r50383 use hg::{self, narrow, sparse};
Simon Sapin
rhg: Properly format warnings related to ignore patterns...
r49342 use log::info;
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 use rayon::prelude::*;
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 use std::io;
Simon Sapin
rhg: Add support for ui.ignore and ui.ignore.* config...
r49282 use std::path::PathBuf;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578
pub const HELP_TEXT: &str = "
Show changed files in the working directory
This is a pure Rust version of `hg status`.
Some options might be missing, check the list below.
";
pub fn args() -> clap::App<'static, 'static> {
SubCommand::with_name("status")
.alias("st")
.about(HELP_TEXT)
.arg(
Arg::with_name("all")
.help("show status of all files")
.short("-A")
.long("--all"),
)
.arg(
Arg::with_name("modified")
.help("show only modified files")
.short("-m")
.long("--modified"),
)
.arg(
Arg::with_name("added")
.help("show only added files")
.short("-a")
.long("--added"),
)
.arg(
Arg::with_name("removed")
.help("show only removed files")
.short("-r")
.long("--removed"),
)
.arg(
Arg::with_name("clean")
.help("show only clean files")
.short("-c")
.long("--clean"),
)
.arg(
Arg::with_name("deleted")
.help("show only deleted files")
.short("-d")
.long("--deleted"),
)
.arg(
Arg::with_name("unknown")
.help("show only unknown (not tracked) files")
.short("-u")
.long("--unknown"),
)
.arg(
Arg::with_name("ignored")
.help("show only ignored files")
.short("-i")
.long("--ignored"),
)
Simon Sapin
rhg: Add support for `rhg status -n`...
r49171 .arg(
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 Arg::with_name("copies")
.help("show source of copied files (DEFAULT: ui.statuscopies)")
.short("-C")
.long("--copies"),
)
.arg(
Simon Sapin
rhg: Add support for `rhg status -n`...
r49171 Arg::with_name("no-status")
.help("hide status prefix")
.short("-n")
.long("--no-status"),
)
Arseniy Alekseyev
rhg: make [rhg status -v] work when it needs no extra output...
r50334 .arg(
Arg::with_name("verbose")
.help("enable additional output")
.short("-v")
.long("--verbose"),
)
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
/// Pure data type allowing the caller to specify file states to display
#[derive(Copy, Clone, Debug)]
pub struct DisplayStates {
pub modified: bool,
pub added: bool,
pub removed: bool,
pub clean: bool,
pub deleted: bool,
pub unknown: bool,
pub ignored: bool,
}
pub const DEFAULT_DISPLAY_STATES: DisplayStates = DisplayStates {
modified: true,
added: true,
removed: true,
clean: false,
deleted: true,
unknown: true,
ignored: false,
};
pub const ALL_DISPLAY_STATES: DisplayStates = DisplayStates {
modified: true,
added: true,
removed: true,
clean: true,
deleted: true,
unknown: true,
ignored: true,
};
impl DisplayStates {
pub fn is_empty(&self) -> bool {
!(self.modified
|| self.added
|| self.removed
|| self.clean
|| self.deleted
|| self.unknown
|| self.ignored)
}
}
Arseniy Alekseyev
rhg: make [rhg status -v] work when it needs no extra output...
r50334 fn has_unfinished_merge(repo: &Repo) -> Result<bool, CommandError> {
return Ok(repo.dirstate_parents()?.is_merge());
}
fn has_unfinished_state(repo: &Repo) -> Result<bool, CommandError> {
// These are all the known values for the [fname] argument of
// [addunfinished] function in [state.py]
let known_state_files: &[&str] = &[
"bisect.state",
"graftstate",
"histedit-state",
"rebasestate",
"shelvedstate",
"transplant/journal",
"updatestate",
];
if has_unfinished_merge(repo)? {
return Ok(true);
};
for f in known_state_files {
if repo.hg_vfs().join(f).exists() {
return Ok(true);
}
}
return Ok(false);
}
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
Pulkit Goyal
rhg: fallback if tweakdefaults or statuscopies is enabled with status...
r48985 // TODO: lift these limitations
Simon Sapin
rhg: Config commands.status.terse is not supported...
r49161 if invocation
.config
.get(b"commands", b"status.terse")
.is_some()
{
return Err(CommandError::unsupported(
"status.terse is not yet supported with rhg status",
));
}
Pulkit Goyal
rhg: fallback if tweakdefaults or statuscopies is enabled with status...
r48985
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 let ui = invocation.ui;
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 let config = invocation.config;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 let args = invocation.subcommand_args;
Simon Sapin
rhg: Fall back to Python if verbose status is requested by config...
r49344
Arseniy Alekseyev
rhg: central treatment of PLAIN and PLAINEXCEPT
r50407 let verbose = !args.is_present("print0")
Arseniy Alekseyev
rhg: make [rhg status -v] work when it needs no extra output...
r50334 && (args.is_present("verbose")
|| config.get_bool(b"ui", b"verbose")?
Simon Sapin
rhg: Fall back to Python if verbose status is requested by config...
r49344 || config.get_bool(b"commands", b"status.verbose")?);
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 let all = args.is_present("all");
let display_states = if all {
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 // TODO when implementing `--quiet`: it excludes clean files
// from `--all`
ALL_DISPLAY_STATES
} else {
let requested = DisplayStates {
modified: args.is_present("modified"),
added: args.is_present("added"),
removed: args.is_present("removed"),
clean: args.is_present("clean"),
deleted: args.is_present("deleted"),
unknown: args.is_present("unknown"),
ignored: args.is_present("ignored"),
};
if requested.is_empty() {
DEFAULT_DISPLAY_STATES
} else {
requested
}
};
Simon Sapin
rhg: Add support for `rhg status -n`...
r49171 let no_status = args.is_present("no-status");
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 let list_copies = all
|| args.is_present("copies")
|| config.get_bool(b"ui", b"statuscopies")?;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578
let repo = invocation.repo?;
Arseniy Alekseyev
rhg: add support for narrow clones and sparse checkouts...
r49238
Arseniy Alekseyev
rhg: make [rhg status -v] work when it needs no extra output...
r50334 if verbose {
if has_unfinished_state(repo)? {
return Err(CommandError::unsupported(
"verbose status output is not supported by rhg (and is needed because we're in an unfinished operation)",
));
};
}
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 let mut dmap = repo.dirstate_map_mut()?;
Simon Sapin
dirstate-v2: Introduce a docket file...
r48474
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 let options = StatusOptions {
// we're currently supporting file systems with exec flags only
// anyway
check_exec: true,
list_clean: display_states.clean,
list_unknown: display_states.unknown,
list_ignored: display_states.ignored,
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 list_copies,
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 collect_traversed_dirs: false,
};
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864
type StatusResult<'a> =
Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>;
let after_status = |res: StatusResult| -> Result<_, CommandError> {
let (mut ds_status, pattern_warnings) = res?;
for warning in pattern_warnings {
Raphaël Gomès
rhg-status: extract a function for printing pattern file warnings...
r50378 ui.write_stderr(&print_pattern_file_warning(&warning, &repo))?;
Simon Sapin
rhg: Properly format warnings related to ignore patterns...
r49342 }
Georges Racinet
rhg: Initial support for the 'status' command...
r47578
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 for (path, error) in ds_status.bad {
let error = match error {
hg::BadMatch::OsError(code) => {
std::io::Error::from_raw_os_error(code).to_string()
}
hg::BadMatch::BadType(ty) => {
format!("unsupported file type (type is {})", ty)
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 }
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 };
ui.write_stderr(&format_bytes!(
b"{}: {}\n",
path.as_bytes(),
error.as_bytes()
))?
}
if !ds_status.unsure.is_empty() {
info!(
"Files to be rechecked by retrieval from filelog: {:?}",
ds_status.unsure.iter().map(|s| &s.path).collect::<Vec<_>>()
);
}
let mut fixup = Vec::new();
if !ds_status.unsure.is_empty()
&& (display_states.modified || display_states.clean)
{
let p1 = repo.dirstate_parents()?.p1;
let manifest = repo.manifest_for_node(p1).map_err(|e| {
CommandError::from((e, &*format!("{:x}", p1.short())))
})?;
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 let working_directory_vfs = repo.working_directory_vfs();
let store_vfs = repo.store_vfs();
let res: Vec<_> = ds_status
.unsure
.into_par_iter()
.map(|to_check| {
unsure_is_modified(
working_directory_vfs,
store_vfs,
&manifest,
&to_check.path,
)
.map(|modified| (to_check, modified))
})
.collect::<Result<_, _>>()?;
for (status_path, is_modified) in res.into_iter() {
if is_modified {
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 if display_states.modified {
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 ds_status.modified.push(status_path);
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 }
} else {
if display_states.clean {
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 ds_status.clean.push(status_path.clone());
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 }
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 fixup.push(status_path.path.into_owned())
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 }
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
}
Arseniy Alekseyev
rhg: central treatment of PLAIN and PLAINEXCEPT
r50407 let relative_paths = config
.get_option(b"commands", b"status.relative")?
.unwrap_or(config.get_bool(b"ui", b"relative-paths")?);
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 let output = DisplayStatusPaths {
ui,
no_status,
relativize: if relative_paths {
Some(RelativizePaths::new(repo)?)
} else {
None
},
};
if display_states.modified {
output.display(b"M ", "status.modified", ds_status.modified)?;
}
if display_states.added {
output.display(b"A ", "status.added", ds_status.added)?;
}
if display_states.removed {
output.display(b"R ", "status.removed", ds_status.removed)?;
}
if display_states.deleted {
output.display(b"! ", "status.deleted", ds_status.deleted)?;
}
if display_states.unknown {
output.display(b"? ", "status.unknown", ds_status.unknown)?;
}
if display_states.ignored {
output.display(b"I ", "status.ignored", ds_status.ignored)?;
}
if display_states.clean {
output.display(b"C ", "status.clean", ds_status.clean)?;
}
let dirstate_write_needed = ds_status.dirty;
let filesystem_time_at_status_start =
ds_status.filesystem_time_at_status_start;
Ok((
fixup,
dirstate_write_needed,
filesystem_time_at_status_start,
))
Simon Sapin
rhg: refactor display_status_paths with a struct for common arguments...
r49283 };
Raphaël Gomès
rhg-status: add support for narrow clones
r50383 let (narrow_matcher, narrow_warnings) = narrow::matcher(repo)?;
let (sparse_matcher, sparse_warnings) = sparse::matcher(repo)?;
let matcher = match (repo.has_narrow(), repo.has_sparse()) {
(true, true) => {
Box::new(IntersectionMatcher::new(narrow_matcher, sparse_matcher))
}
(true, false) => narrow_matcher,
(false, true) => sparse_matcher,
(false, false) => Box::new(AlwaysMatcher),
};
Raphaël Gomès
rhg: add sparse support
r50380
Raphaël Gomès
rhg-status: add support for narrow clones
r50383 for warning in narrow_warnings.into_iter().chain(sparse_warnings) {
Raphaël Gomès
rhg: add sparse support
r50380 match &warning {
Raphaël Gomès
rhg-status: add support for narrow clones
r50383 sparse::SparseWarning::RootWarning { context, line } => {
Raphaël Gomès
rhg: add sparse support
r50380 let msg = format_bytes!(
b"warning: {} profile cannot use paths \"
starting with /, ignoring {}\n",
context,
line
);
ui.write_stderr(&msg)?;
}
Raphaël Gomès
rhg-status: add support for narrow clones
r50383 sparse::SparseWarning::ProfileNotFound { profile, rev } => {
Raphaël Gomès
rhg: add sparse support
r50380 let msg = format_bytes!(
b"warning: sparse profile '{}' not found \"
in rev {} - ignoring it\n",
profile,
rev
);
ui.write_stderr(&msg)?;
}
Raphaël Gomès
rhg-status: add support for narrow clones
r50383 sparse::SparseWarning::Pattern(e) => {
Raphaël Gomès
rhg: add sparse support
r50380 ui.write_stderr(&print_pattern_file_warning(e, &repo))?;
}
}
}
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 let (fixup, mut dirstate_write_needed, filesystem_time_at_status_start) =
dmap.with_status(
Raphaël Gomès
rhg: add sparse support
r50380 matcher.as_ref(),
Raphaël Gomès
rust: fix unsound `OwningDirstateMap`...
r49864 repo.working_directory_path().to_owned(),
ignore_files(repo, config),
options,
after_status,
)?;
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250
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)?;
Simon Sapin
rhg: Set second_ambiguous as needed in post-status fixup...
r49272 if let Some(mtime) =
TruncatedTimestamp::for_reliable_mtime_of(
&fs_metadata,
&mtime_boundary,
)
.when_reading_file(&fs_path)?
{
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 let mode = fs_metadata.mode();
Raphaël Gomès
rhg: use the new `set_clean` API...
r50001 let size = fs_metadata.len();
dmap.set_clean(&hg_path, mode, size as u32, mtime)?;
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 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)?
}
}
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 Ok(())
}
Simon Sapin
rhg: Add support for ui.ignore and ui.ignore.* config...
r49282 fn ignore_files(repo: &Repo, config: &Config) -> Vec<PathBuf> {
let mut ignore_files = Vec::new();
let repo_ignore = repo.working_directory_vfs().join(".hgignore");
if repo_ignore.exists() {
ignore_files.push(repo_ignore)
}
for (key, value) in config.iter_section(b"ui") {
if key == b"ignore" || key.starts_with(b"ignore.") {
let path = get_path_from_bytes(value);
// TODO: expand "~/" and environment variable here, like Python
// does with `os.path.expanduser` and `os.path.expandvars`
let joined = repo.working_directory_path().join(path);
ignore_files.push(joined);
}
}
ignore_files
}
Simon Sapin
rhg: refactor display_status_paths with a struct for common arguments...
r49283 struct DisplayStatusPaths<'a> {
ui: &'a Ui,
Simon Sapin
rhg: Add support for `rhg status -n`...
r49171 no_status: bool,
Simon Sapin
rhg: refactor relativize_path into a struct + method...
r49284 relativize: Option<RelativizePaths>,
Simon Sapin
rhg: refactor display_status_paths with a struct for common arguments...
r49283 }
impl DisplayStatusPaths<'_> {
// Probably more elegant to use a Deref or Borrow trait rather than
// harcode HgPathBuf, but probably not really useful at this point
fn display(
&self,
status_prefix: &[u8],
Simon Sapin
rhg: Colorize `rhg status` output when appropriate...
r49585 label: &'static str,
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 mut paths: Vec<StatusPath<'_>>,
Simon Sapin
rhg: refactor display_status_paths with a struct for common arguments...
r49283 ) -> Result<(), CommandError> {
paths.sort_unstable();
Simon Sapin
rust: fix code formatting...
r49589 // TODO: get the stdout lock once for the whole loop
// instead of in each write
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 for StatusPath { path, copy_source } in paths {
Simon Sapin
rhg: refactor relativize_path into a struct + method...
r49284 let relative;
let path = if let Some(relativize) = &self.relativize {
relative = relativize.relativize(&path);
&*relative
} else {
path.as_bytes()
};
Simon Sapin
rhg: Colorize `rhg status` output when appropriate...
r49585 // TODO: Add a way to use `write_bytes!` instead of `format_bytes!`
// in order to stream to stdout instead of allocating an
// itermediate `Vec<u8>`.
if !self.no_status {
self.ui.write_stdout_labelled(status_prefix, label)?
Simon Sapin
rhg: refactor display_status_paths with a struct for common arguments...
r49283 }
Simon Sapin
rhg: Colorize `rhg status` output when appropriate...
r49585 self.ui
.write_stdout_labelled(&format_bytes!(b"{}\n", path), label)?;
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 if let Some(source) = copy_source {
Simon Sapin
rhg: Colorize `rhg status` output when appropriate...
r49585 let label = "status.copied";
self.ui.write_stdout_labelled(
&format_bytes!(b" {}\n", source.as_bytes()),
label,
)?
Simon Sapin
rhg: Add support for `rhg status --copies`...
r49285 }
Simon Sapin
rhg: Add support for `rhg status -n`...
r49171 }
Simon Sapin
rhg: refactor display_status_paths with a struct for common arguments...
r49283 Ok(())
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
}
/// Check if a file is modified by comparing actual repo store and file system.
///
/// This meant to be used for those that the dirstate cannot resolve, due
/// to time resolution limits.
Simon Sapin
rhg: Rename cat_file_is_modified...
r49167 fn unsure_is_modified(
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 working_directory_vfs: hg::vfs::Vfs,
store_vfs: hg::vfs::Vfs,
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 manifest: &Manifest,
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 hg_path: &HgPath,
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 ) -> Result<bool, HgError> {
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 let vfs = working_directory_vfs;
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion");
Simon Sapin
rhg: Fix status desambiguation of symlinks and executable files...
r49168 let fs_metadata = vfs.symlink_metadata(&fs_path)?;
let is_symlink = fs_metadata.file_type().is_symlink();
dirstate: remove `lastnormaltime` mechanism...
r49220 // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the
// dirstate
Simon Sapin
rhg: Fix status desambiguation of symlinks and executable files...
r49168 let fs_flags = if is_symlink {
Some(b'l')
} else if has_exec_bit(&fs_metadata) {
Some(b'x')
} else {
None
};
Simon Sapin
rhg: Also parse flags in the manifest parser...
r49166 let entry = manifest
Simon Sapin
rhg: Use binary search in manifest lookup...
r49324 .find_by_path(hg_path)?
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 .expect("ambgious file not in p1");
Simon Sapin
rhg: Fix status desambiguation of symlinks and executable files...
r49168 if entry.flags != fs_flags {
return Ok(true);
}
Arseniy Alekseyev
rhg: parallellize computation of [unsure_is_modified]...
r50412 let filelog = hg::filelog::Filelog::open_vfs(&store_vfs, hg_path)?;
Simon Sapin
rhg: Skip reading the contents of ambiguous files in some cases...
r49302 let fs_len = fs_metadata.len();
Raphaël Gomès
rust: don't swallow valuable error information...
r50269 let file_node = entry.node_id()?;
let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| {
HgError::corrupted(format!(
"filelog missing node {:?} from manifest",
file_node
))
})?;
Simon Sapin
rhg: desambiguate status without decompressing filelog if possible...
r49378 if filelog_entry.file_data_len_not_equal_to(fs_len) {
// No need to read file contents:
// it cannot be equal if it has a different length.
return Ok(true);
}
let p1_filelog_data = filelog_entry.data()?;
let p1_contents = p1_filelog_data.file_data()?;
if p1_contents.len() as u64 != fs_len {
// No need to read file contents:
Simon Sapin
rhg: Skip reading the contents of ambiguous files in some cases...
r49302 // it cannot be equal if it has a different length.
return Ok(true);
}
Georges Racinet
rhg: Initial support for the 'status' command...
r47578
Simon Sapin
rhg: Fix status desambiguation of symlinks and executable files...
r49168 let fs_contents = if is_symlink {
get_bytes_from_os_string(vfs.read_link(fs_path)?.into_os_string())
} else {
vfs.read(fs_path)?
};
Simon Sapin
rhg: desambiguate status without decompressing filelog if possible...
r49378 Ok(p1_contents != &*fs_contents)
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
Raphaël Gomès
rhg-status: extract a function for printing pattern file warnings...
r50378
fn print_pattern_file_warning(
warning: &PatternFileWarning,
repo: &Repo,
) -> Vec<u8> {
match warning {
PatternFileWarning::InvalidSyntax(path, syntax) => format_bytes!(
b"{}: ignoring invalid syntax '{}'\n",
get_bytes_from_path(path),
&*syntax
),
PatternFileWarning::NoSuchFile(path) => {
let path = if let Ok(relative) =
path.strip_prefix(repo.working_directory_path())
{
relative
} else {
&*path
};
format_bytes!(
b"skipping unreadable pattern file '{}': \
No such file or directory\n",
get_bytes_from_path(path),
)
}
}
}