##// END OF EJS Templates
pyoxidizer: use in-memory resources on non-Windows platforms...
pyoxidizer: use in-memory resources on non-Windows platforms In-memory resources were disabled for macOS in 7bc1beed, and for all platforms in c900d962. Unfortunately this made it so that we were no longer producing standalone binaries on these platforms, and would have to ship the .py and .pyc files alongside the pyoxidized binary. These changes are no longer necessary after f6b04591, which disabled pep517 and solved the issue we were encountering. Differential Revision: https://phab.mercurial-scm.org/D11734

File last commit:

r49079:269ff897 default
r49120:1a420a13 default
Show More
status.rs
316 lines | 9.8 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;
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 use crate::ui::{Ui, UiError};
use crate::utils::path_utils::relativize_paths;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 use clap::{Arg, SubCommand};
use hg;
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 use hg::config::Config;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 use hg::dirstate::TruncatedTimestamp;
Simon Sapin
rhg: Don’t compare ambiguous files one byte at a time...
r48779 use hg::errors::HgError;
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 use hg::manifest::Manifest;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 use hg::matchers::AlwaysMatcher;
use hg::repo::Repo;
use hg::utils::hg_path::{hg_path_to_os_string, HgPath};
use hg::{HgPathCow, StatusOptions};
use log::{info, warn};
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 use std::borrow::Cow;
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"),
)
}
/// 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)
}
}
pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
let status_enabled_default = false;
let status_enabled = invocation.config.get_option(b"rhg", b"status")?;
if !status_enabled.unwrap_or(status_enabled_default) {
return Err(CommandError::unsupported(
"status is experimental in rhg (enable it with 'rhg.status = true' \
or enable fallback with 'rhg.on-unsupported = fallback')"
));
}
Pulkit Goyal
rhg: fallback if tweakdefaults or statuscopies is enabled with status...
r48985 // TODO: lift these limitations
if invocation.config.get_bool(b"ui", b"tweakdefaults").ok() == Some(true) {
return Err(CommandError::unsupported(
"ui.tweakdefaults is not yet supported with rhg status",
));
}
if invocation.config.get_bool(b"ui", b"statuscopies").ok() == Some(true) {
return Err(CommandError::unsupported(
"ui.statuscopies is not yet supported with rhg status",
));
}
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;
let display_states = if args.is_present("all") {
// 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
}
};
let repo = invocation.repo?;
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 {
// TODO should be provided by the dirstate parsing and
// hence be stored on dmap. Using a value that assumes we aren't
// below the time resolution granularity of the FS and the
// dirstate.
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 last_normal_time: TruncatedTimestamp::new_truncate(0, 0),
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 // 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,
collect_traversed_dirs: false,
};
let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded
Simon Sapin
rust: Add Repo::dirstate_map and use it in `rhg status`...
r48768 let (mut ds_status, pattern_warnings) = dmap.status(
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 &AlwaysMatcher,
repo.working_directory_path().to_owned(),
vec![ignore_file],
options,
)?;
if !pattern_warnings.is_empty() {
warn!("Pattern warnings: {:?}", &pattern_warnings);
}
if !ds_status.bad.is_empty() {
warn!("Bad matches {:?}", &(ds_status.bad))
}
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 if !ds_status.unsure.is_empty() {
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 info!(
"Files to be rechecked by retrieval from filelog: {:?}",
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 &ds_status.unsure
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 );
}
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 if !ds_status.unsure.is_empty()
&& (display_states.modified || display_states.clean)
{
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 let p1 = repo.dirstate_parents()?.p1;
let manifest = repo.manifest_for_node(p1).map_err(|e| {
CommandError::from((e, &*format!("{:x}", p1.short())))
})?;
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 for to_check in ds_status.unsure {
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 if cat_file_is_modified(repo, &manifest, &to_check)? {
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 if display_states.modified {
ds_status.modified.push(to_check);
}
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 } else {
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 if display_states.clean {
ds_status.clean.push(to_check);
}
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
}
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 }
if display_states.modified {
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 display_status_paths(ui, repo, config, &mut ds_status.modified, b"M")?;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
if display_states.added {
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 display_status_paths(ui, repo, config, &mut ds_status.added, b"A")?;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
if display_states.removed {
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 display_status_paths(ui, repo, config, &mut ds_status.removed, b"R")?;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
if display_states.deleted {
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 display_status_paths(ui, repo, config, &mut ds_status.deleted, b"!")?;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
if display_states.unknown {
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 display_status_paths(ui, repo, config, &mut ds_status.unknown, b"?")?;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
if display_states.ignored {
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 display_status_paths(ui, repo, config, &mut ds_status.ignored, b"I")?;
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 }
if display_states.clean {
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 display_status_paths(ui, repo, config, &mut ds_status.clean, b"C")?;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
Ok(())
}
// Probably more elegant to use a Deref or Borrow trait rather than
// harcode HgPathBuf, but probably not really useful at this point
fn display_status_paths(
ui: &Ui,
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 repo: &Repo,
config: &Config,
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 paths: &mut [HgPathCow],
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 status_prefix: &[u8],
) -> Result<(), CommandError> {
Simon Sapin
rhg: Sort `rhg status` output correctly...
r48112 paths.sort_unstable();
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 let mut relative: bool =
config.get_bool(b"ui", b"relative-paths").unwrap_or(false);
relative = config
.get_bool(b"commands", b"status.relative")
.unwrap_or(relative);
Pulkit Goyal
rhg: add ui.plain() and check it before showing relative paths in status...
r48990 if relative && !ui.plain() {
Pulkit Goyal
rhg: add relative paths support in `rhg status`...
r48989 relativize_paths(
repo,
paths,
|path: Cow<[u8]>| -> Result<(), UiError> {
ui.write_stdout(
&[status_prefix, b" ", path.as_ref(), b"\n"].concat(),
)
},
)?;
} else {
for path in paths {
// Same TODO as in commands::root
let bytes: &[u8] = path.as_bytes();
// TODO optim, probably lots of unneeded copies here, especially
// if out stream is buffered
ui.write_stdout(&[status_prefix, b" ", bytes, b"\n"].concat())?;
}
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }
Ok(())
}
/// 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.
///
/// TODO: detect permission bits and similar metadata modifications
fn cat_file_is_modified(
repo: &Repo,
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> {
let file_node = manifest
.find_file(hg_path)?
.expect("ambgious file not in p1");
let filelog = repo.filelog(hg_path)?;
Simon Sapin
rust: Rename get_node methods to data_for_node, get_rev to data_for_rev...
r48783 let filelog_entry = filelog.data_for_node(file_node).map_err(|_| {
Simon Sapin
rhg: Reuse manifest when checking status of multiple ambiguous files...
r48778 HgError::corrupted("filelog missing node from manifest")
})?;
let contents_in_p1 = filelog_entry.data()?;
Georges Racinet
rhg: Initial support for the 'status' command...
r47578
Simon Sapin
rhg: Don’t compare ambiguous files one byte at a time...
r48779 let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion");
let fs_contents = repo.working_directory_vfs().read(fs_path)?;
return Ok(contents_in_p1 == &*fs_contents);
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 }