##// END OF EJS Templates
check-code: drop the check for whitespace around named parameters...
check-code: drop the check for whitespace around named parameters This check flags py3 annotations of named parameters, because `black` adds spaces around the assignment in this case. Since the chosen formatter has opinions (and pylint also wants the space in the case of annotations), drop the check so we can use py3 annotations.

File last commit:

r50464:5318ac25 stable
r50465:3a2b6158 default
Show More
main.rs
818 lines | 27.3 KiB | application/rls-services+xml | RustLexer
Antoine Cezar
rhg: Add debug timing...
r46101 extern crate log;
Simon Sapin
rhg: $HG_PENDING is not supported...
r49159 use crate::error::CommandError;
Raphaël Gomès
rhg: signal when falling back in logs...
r49622 use crate::ui::{local_to_utf8, Ui};
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593 use clap::App;
use clap::AppSettings;
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253 use clap::Arg;
Antoine Cezar
rhg: add a limited `rhg debugdata` subcommand...
r46100 use clap::ArgMatches;
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467 use format_bytes::{format_bytes, join};
Arseniy Alekseyev
rhg: central treatment of PLAIN and PLAINEXCEPT
r50407 use hg::config::{Config, ConfigSource, PlainInfo};
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335 use hg::repo::{Repo, RepoError};
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
use hg::utils::SliceExt;
auto-upgrade: introduce a way to auto-upgrade to/from share-safe...
r50087 use hg::{exit_codes, requirements};
Arseniy Alekseyev
rhg: central treatment of PLAIN and PLAINEXCEPT
r50407 use std::borrow::Cow;
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 use std::collections::HashSet;
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 use std::ffi::OsString;
Raphaël Gomès
rhg: use `Command::exec` instead of `Command::status`...
r50043 use std::os::unix::prelude::CommandExt;
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 use std::path::PathBuf;
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 use std::process::Command;
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 mod blackbox;
Simon Sapin
rhg: Add support for colored output...
r49584 mod color;
Antoine Cezar
rhg: add Command trait for subcommands implemented by rhg...
r45515 mod error;
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592 mod ui;
Pulkit Goyal
rhg: refactor function to relativize paths in utils...
r48988 pub mod utils {
pub mod path_utils;
}
Antoine Cezar
rhg: add rhg crate...
r45503
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 fn main_with_result(
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 argv: Vec<OsString>,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 process_start_time: &blackbox::ProcessStartTime,
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 ui: &ui::Ui,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 repo: Result<&Repo, &NoRepoInCwdError>,
config: &Config,
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 ) -> Result<(), CommandError> {
Simon Sapin
rhg: Colorize `rhg status` output when appropriate...
r49585 check_unsupported(config, repo)?;
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467
Antoine Cezar
rhg: add a limited `rhg debugdata` subcommand...
r46100 let app = App::new("rhg")
Simon Sapin
rhg: Use clap’s support for global CLI arguments...
r47351 .global_setting(AppSettings::AllowInvalidUtf8)
Simon Sapin
rhg: Fall back to Python for --version...
r47480 .global_setting(AppSettings::DisableVersion)
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593 .setting(AppSettings::SubcommandRequired)
.setting(AppSettings::VersionlessSubcommands)
Simon Sapin
rhg: Use clap’s support for global CLI arguments...
r47351 .arg(
Arg::with_name("repository")
.help("repository root directory")
.short("-R")
.long("--repository")
.value_name("REPO")
.takes_value(true)
// Both ok: `hg -R ./foo log` or `hg log -R ./foo`
.global(true),
)
.arg(
Arg::with_name("config")
.help("set/override config option (use 'section.name=value')")
.long("--config")
.value_name("CONFIG")
.takes_value(true)
.global(true)
// Ok: `--config section.key1=val --config section.key2=val2`
.multiple(true)
// Not ok: `--config section.key1=val section.key2=val2`
.number_of_values(1),
)
Simon Sapin
rhg: Add support for --cwd...
r47470 .arg(
Arg::with_name("cwd")
.help("change working directory")
.long("--cwd")
.value_name("DIR")
.takes_value(true)
.global(true),
)
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 .arg(
Arg::with_name("color")
.help("when to colorize (boolean, always, auto, never, or debug)")
.long("--color")
.value_name("TYPE")
.takes_value(true)
.global(true),
)
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 .version("0.0.1");
let app = add_subcommand_args(app);
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 let matches = app.clone().get_matches_from_safe(argv.iter())?;
Simon Sapin
rhg: Add support for -R and --repository command-line arguments...
r47253
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 let (subcommand_name, subcommand_matches) = matches.subcommand();
Raphaël Gomès
rhg: fallback if the current command has any generic hook defined...
r48889
Raphaël Gomès
rhg: fallback if `defaults` config is set for the current command...
r48890 // Mercurial allows users to define "defaults" for commands, fallback
// if a default is detected for the current command
let defaults = config.get_str(b"defaults", subcommand_name.as_bytes());
if defaults?.is_some() {
let msg = "`defaults` config set";
return Err(CommandError::unsupported(msg));
}
Raphaël Gomès
rhg: fallback if the current command has any generic hook defined...
r48889 for prefix in ["pre", "post", "fail"].iter() {
// Mercurial allows users to define generic hooks for commands,
// fallback if any are detected
let item = format!("{}-{}", prefix, subcommand_name);
let hook_for_command = config.get_str(b"hooks", item.as_bytes())?;
if hook_for_command.is_some() {
let msg = format!("{}-{} hook defined", prefix, subcommand_name);
return Err(CommandError::unsupported(msg));
}
}
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 let run = subcommand_run_fn(subcommand_name)
.expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 let subcommand_args = subcommand_matches
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
Antoine Cezar
rhg: add a limited `rhg root` subcommand...
r45593
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 let invocation = CliInvocation {
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 ui,
subcommand_args,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 config,
repo,
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 };
Raphaël Gomès
rhg: fall back if subrepos are detected...
r48891
if let Ok(repo) = repo {
// We don't support subrepos, fallback if the subrepos file is present
if repo.working_directory_vfs().join(".hgsub").exists() {
let msg = "subrepos (.hgsub is present)";
return Err(CommandError::unsupported(msg));
}
}
Raphaël Gomès
rhg: don't run `blackbox` if not activated...
r49243 if config.is_extension_enabled(b"blackbox") {
let blackbox =
blackbox::Blackbox::new(&invocation, process_start_time)?;
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 blackbox.log_command_start(argv.iter());
Raphaël Gomès
rhg: don't run `blackbox` if not activated...
r49243 let result = run(&invocation);
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 blackbox.log_command_end(
argv.iter(),
exit_code(
&result,
// TODO: show a warning or combine with original error if
// `get_bool` returns an error
config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
),
);
Raphaël Gomès
rhg: don't run `blackbox` if not activated...
r49243 result
} else {
run(&invocation)
}
Simon Sapin
rhg: Remove error message on unsupported CLI arguments...
r47333 }
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 fn rhg_main(argv: Vec<OsString>) -> ! {
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 // Run this first, before we find out if the blackbox extension is even
// enabled, in order to include everything in-between in the duration
// measurements. Reading config files can be slow if they’re on NFS.
let process_start_time = blackbox::ProcessStartTime::now();
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 env_logger::init();
Simon Sapin
rhg: Remove error message on unsupported CLI arguments...
r47333
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 let early_args = EarlyArgs::parse(&argv);
Simon Sapin
rhg: Add support for --cwd...
r47470
let initial_current_dir = early_args.cwd.map(|cwd| {
let cwd = get_path_from_bytes(&cwd);
std::env::current_dir()
.and_then(|initial| {
std::env::set_current_dir(cwd)?;
Ok(initial)
})
.unwrap_or_else(|error| {
exit(
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 &argv,
Simon Sapin
rhg: Add support for --cwd...
r47470 &None,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&Config::empty()),
Simon Sapin
rhg: Add support for --cwd...
r47470 OnUnsupported::Abort,
Err(CommandError::abort(format!(
"abort: {}: '{}'",
error,
cwd.display()
))),
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 false,
Simon Sapin
rhg: Add support for --cwd...
r47470 )
})
});
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 let mut non_repo_config =
Config::load_non_repo().unwrap_or_else(|error| {
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 // Normally this is decided based on config, but we don’t have that
// available. As of this writing config loading never returns an
// "unsupported" error but that is not enforced by the type system.
let on_unsupported = OnUnsupported::Abort;
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 exit(
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 &argv,
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 &initial_current_dir,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&Config::empty()),
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 on_unsupported,
Err(error.into()),
false,
)
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 });
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 non_repo_config
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 .load_cli_args(early_args.config, early_args.color)
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 .unwrap_or_else(|error| {
exit(
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 &argv,
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 &initial_current_dir,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&non_repo_config),
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 OnUnsupported::from_config(&non_repo_config),
Pulkit Goyal
rhg: split non_repo_config and `--config` loading in different functions...
r48198 Err(error.into()),
non_repo_config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
)
});
Simon Sapin
rhg: Fall back to Python on --repository with an URL...
r47463 if let Some(repo_path_bytes) = &early_args.repo {
lazy_static::lazy_static! {
static ref SCHEME_RE: regex::bytes::Regex =
// Same as `_matchscheme` in `mercurial/util.py`
regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
}
if SCHEME_RE.is_match(&repo_path_bytes) {
exit(
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 &argv,
Simon Sapin
rhg: Add support for --cwd...
r47470 &initial_current_dir,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 &Ui::new_infallible(&non_repo_config),
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 OnUnsupported::from_config(&non_repo_config),
Simon Sapin
rhg: Fall back to Python on --repository with an URL...
r47463 Err(CommandError::UnsupportedFeature {
message: format_bytes!(
b"URL-like --repository {}",
repo_path_bytes
),
}),
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 // TODO: show a warning or combine with original error if
// `get_bool` returns an error
non_repo_config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
Simon Sapin
rhg: Fall back to Python on --repository with an URL...
r47463 )
}
}
Pulkit Goyal
rhg: read [paths] for `--repository` value...
r48196 let repo_arg = early_args.repo.unwrap_or(Vec::new());
let repo_path: Option<PathBuf> = {
if repo_arg.is_empty() {
None
} else {
let local_config = {
if std::env::var_os("HGRCSKIPREPO").is_none() {
Pulkit Goyal
rhg: look for repository in ancestors also instead of cwd only...
r48197 // TODO: handle errors from find_repo_root
if let Ok(current_dir_path) = Repo::find_repo_root() {
Pulkit Goyal
rhg: read [paths] for `--repository` value...
r48196 let config_files = vec![
ConfigSource::AbsPath(
current_dir_path.join(".hg/hgrc"),
),
ConfigSource::AbsPath(
current_dir_path.join(".hg/hgrc-not-shared"),
),
];
// TODO: handle errors from
// `load_from_explicit_sources`
Config::load_from_explicit_sources(config_files).ok()
} else {
None
}
} else {
None
}
};
let non_repo_config_val = {
let non_repo_val = non_repo_config.get(b"paths", &repo_arg);
match &non_repo_val {
Some(val) if val.len() > 0 => home::home_dir()
.unwrap_or_else(|| PathBuf::from("~"))
.join(get_path_from_bytes(val))
.canonicalize()
// TODO: handle error and make it similar to python
// implementation maybe?
.ok(),
_ => None,
}
};
let config_val = match &local_config {
None => non_repo_config_val,
Some(val) => {
let local_config_val = val.get(b"paths", &repo_arg);
match &local_config_val {
Some(val) if val.len() > 0 => {
// presence of a local_config assures that
// current_dir
// wont result in an Error
let canpath = hg::utils::current_dir()
.unwrap()
.join(get_path_from_bytes(val))
.canonicalize();
canpath.ok().or(non_repo_config_val)
}
_ => non_repo_config_val,
}
}
};
config_val.or(Some(get_path_from_bytes(&repo_arg).to_path_buf()))
}
};
Arseniy Alekseyev
rhg: share some code
r50410 let exit =
|ui: &Ui, config: &Config, result: Result<(), CommandError>| -> ! {
exit(
&argv,
&initial_current_dir,
ui,
OnUnsupported::from_config(config),
result,
// TODO: show a warning or combine with original error if
// `get_bool` returns an error
non_repo_config
.get_bool(b"ui", b"detailed-exit-code")
.unwrap_or(false),
)
};
let early_exit = |config: &Config, error: CommandError| -> ! {
exit(&Ui::new_infallible(config), &config, Err(error))
};
Pulkit Goyal
rhg: read [paths] for `--repository` value...
r48196 let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned())
{
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 Ok(repo) => Ok(repo),
Err(RepoError::NotFound { at }) if repo_path.is_none() => {
// Not finding a repo is not fatal yet, if `-R` was not given
Err(NoRepoInCwdError { cwd: at })
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 }
Arseniy Alekseyev
rhg: share some code
r50410 Err(error) => early_exit(&non_repo_config, error.into()),
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 };
let config = if let Ok(repo) = &repo_result {
repo.config()
} else {
&non_repo_config
};
Arseniy Alekseyev
rhg: central treatment of PLAIN and PLAINEXCEPT
r50407
let mut config_cow = Cow::Borrowed(config);
Arseniy Alekseyev
rhg: centralize PlainInfo
r50408 config_cow.to_mut().apply_plain(PlainInfo::from_env());
Arseniy Alekseyev
rhg: support tweakdefaults
r50409 if !ui::plain(Some("tweakdefaults"))
&& config_cow
.as_ref()
.get_bool(b"ui", b"tweakdefaults")
Arseniy Alekseyev
rhg: share some code
r50410 .unwrap_or_else(|error| early_exit(&config, error.into()))
Arseniy Alekseyev
rhg: support tweakdefaults
r50409 {
config_cow.to_mut().tweakdefaults()
};
Arseniy Alekseyev
rhg: central treatment of PLAIN and PLAINEXCEPT
r50407 let config = config_cow.as_ref();
Arseniy Alekseyev
rhg: share some code
r50410 let ui = Ui::new(&config)
.unwrap_or_else(|error| early_exit(&config, error.into()));
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 let result = main_with_result(
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 argv.iter().map(|s| s.to_owned()).collect(),
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 &process_start_time,
&ui,
repo_result.as_ref(),
config,
);
Arseniy Alekseyev
rhg: share some code
r50410 exit(&ui, &config, result)
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 }
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 fn main() -> ! {
rhg_main(std::env::args_os().collect())
}
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 fn exit_code(
result: &Result<(), CommandError>,
use_detailed_exit_code: bool,
) -> i32 {
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 match result {
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 Ok(()) => exit_codes::OK,
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 Err(CommandError::Abort {
Raphaël Gomès
rust: add support for hints in error messages...
r50382 detailed_exit_code, ..
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 }) => {
if use_detailed_exit_code {
*detailed_exit_code
} else {
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 exit_codes::ABORT
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 }
}
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
Simon Sapin
rhg: Simplify CommandError based on its use...
r47174 // Exit with a specific code and no error message to let a potential
// wrapper script fallback to Python-based Mercurial.
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 Err(CommandError::UnsupportedFeature { .. }) => {
Pulkit Goyal
rhg: add exit code to HgError::Abort()...
r48199 exit_codes::UNIMPLEMENTED
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
Raphaël Gomès
rhg: use `Command::exec` instead of `Command::status`...
r50043 Err(CommandError::InvalidFallback { .. }) => {
exit_codes::INVALID_FALLBACK
}
Simon Sapin
rhg: Add support for the blackbox extension...
r47343 }
Antoine Cezar
rhg: add rhg crate...
r45503 }
Antoine Cezar
rhg: add a limited `rhg debugdata` subcommand...
r46100
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 fn exit<'a>(
original_args: &'a [OsString],
Simon Sapin
rhg: Add support for --cwd...
r47470 initial_current_dir: &Option<PathBuf>,
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 ui: &Ui,
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 mut on_unsupported: OnUnsupported,
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 result: Result<(), CommandError>,
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 use_detailed_exit_code: bool,
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 ) -> ! {
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 if let (
OnUnsupported::Fallback { executable },
Raphaël Gomès
rhg: signal when falling back in logs...
r49622 Err(CommandError::UnsupportedFeature { message }),
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 ) = (&on_unsupported, &result)
{
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 let mut args = original_args.iter();
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 let executable = match executable {
None => {
exit_no_fallback(
ui,
OnUnsupported::Abort,
Err(CommandError::abort(
"abort: 'rhg.on-unsupported=fallback' without \
'rhg.fallback-executable' set.",
)),
false,
);
}
Some(executable) => executable,
};
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 let executable_path = get_path_from_bytes(&executable);
let this_executable = args.next().expect("exepcted argv[0] to exist");
if executable_path == &PathBuf::from(this_executable) {
// Avoid spawning infinitely many processes until resource
// exhaustion.
let _ = ui.write_stderr(&format_bytes!(
b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
points to `rhg` itself.\n",
executable
));
on_unsupported = OnUnsupported::Abort
} else {
Raphaël Gomès
rhg: signal when falling back in logs...
r49622 log::debug!("falling back (see trace-level log)");
log::trace!("{}", local_to_utf8(message));
Raphaël Gomès
rhg: use `Command::exec` instead of `Command::status`...
r50043 if let Err(err) = which::which(executable_path) {
exit_no_fallback(
ui,
OnUnsupported::Abort,
Err(CommandError::InvalidFallback {
path: executable.to_owned(),
err: err.to_string(),
}),
use_detailed_exit_code,
)
}
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 // `args` is now `argv[1..]` since we’ve already consumed
// `argv[0]`
Simon Sapin
rhg: Add support for --cwd...
r47470 let mut command = Command::new(executable_path);
command.args(args);
if let Some(initial) = initial_current_dir {
command.current_dir(initial);
}
Raphaël Gomès
rhg: use `Command::exec` instead of `Command::status`...
r50043 // We don't use subprocess because proper signal handling is harder
// and we don't want to keep `rhg` around after a fallback anyway.
// For example, if `rhg` is run in the background and falls back to
// `hg` which, in turn, waits for a signal, we'll get stuck if
// we're doing plain subprocess.
//
// If `exec` returns, we can only assume our process is very broken
// (see its documentation), so only try to forward the error code
// when exiting.
let err = command.exec();
std::process::exit(
err.raw_os_error().unwrap_or(exit_codes::ABORT),
);
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 }
}
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
Simon Sapin
rhg: Remove `rhg.fallback-executable=hg` default configuration...
r47482 }
fn exit_no_fallback(
ui: &Ui,
on_unsupported: OnUnsupported,
result: Result<(), CommandError>,
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 use_detailed_exit_code: bool,
Simon Sapin
rhg: Remove `rhg.fallback-executable=hg` default configuration...
r47482 ) -> ! {
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 match &result {
Ok(_) => {}
Simon Sapin
rhg: `cat` command: print error messages for missing files...
r47478 Err(CommandError::Unsuccessful) => {}
Raphaël Gomès
rust: add support for hints in error messages...
r50382 Err(CommandError::Abort { message, hint, .. }) => {
// Ignore errors when writing to stderr, we’re already exiting
// with failure code so there’s not much more we can do.
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 if !message.is_empty() {
Simon Sapin
rhg: Align config file parse error formatting with Python...
r47465 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
Raphaël Gomès
rust: add support for hints in error messages...
r50382 if let Some(hint) = hint {
let _ = ui.write_stderr(&format_bytes!(b"({})\n", hint));
}
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
Err(CommandError::UnsupportedFeature { message }) => {
match on_unsupported {
OnUnsupported::Abort => {
let _ = ui.write_stderr(&format_bytes!(
b"unsupported feature: {}\n",
message
));
}
OnUnsupported::AbortSilent => {}
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 OnUnsupported::Fallback { .. } => unreachable!(),
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 }
Raphaël Gomès
rhg: use `Command::exec` instead of `Command::status`...
r50043 Err(CommandError::InvalidFallback { path, err }) => {
let _ = ui.write_stderr(&format_bytes!(
b"abort: invalid fallback '{}': {}\n",
path,
err.as_bytes(),
));
}
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 }
Pulkit Goyal
rhg: add support for detailed exit code for ConfigParseError...
r47576 std::process::exit(exit_code(&result, use_detailed_exit_code))
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 }
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 macro_rules! subcommands {
($( $command: ident )+) => {
mod commands {
$(
pub mod $command;
)+
}
fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
app
$(
Simon Sapin
rhg: Use clap’s support for global CLI arguments...
r47351 .subcommand(commands::$command::args())
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 )+
}
Simon Sapin
rhg: Parse system and user configuration at program start...
r47213
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
fn subcommand_run_fn(name: &str) -> Option<RunFn> {
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 match name {
$(
stringify!($command) => Some(commands::$command::run),
)+
_ => None,
}
Antoine Cezar
rhg: add a limited `rhg cat -r` subcommand...
r46113 }
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 };
Antoine Cezar
rhg: add a limited `rhg debugdata` subcommand...
r46100 }
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252
subcommands! {
cat
debugdata
debugrequirements
Arseniy Alekseyev
rhg: implement the debugignorerhg subcommand...
r49178 debugignorerhg
Raphaël Gomès
rhg: add debugrhgsparse command to help figure out bugs in rhg
r50379 debugrhgsparse
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 files
root
Simon Sapin
rhg: add limited support for the `config` sub-command...
r47255 config
Georges Racinet
rhg: Initial support for the 'status' command...
r47578 status
Simon Sapin
rhg: Replace subcommand boilerplate with a macro...
r47252 }
Georges Racinet
rhg: Initial support for the 'status' command...
r47578
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 pub struct CliInvocation<'a> {
ui: &'a Ui,
subcommand_args: &'a ArgMatches<'a>,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 config: &'a Config,
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335 /// References inside `Result` is a bit peculiar but allow
/// `invocation.repo?` to work out with `&CliInvocation` since this
/// `Result` type is `Copy`.
repo: Result<&'a Repo, &'a NoRepoInCwdError>,
}
struct NoRepoInCwdError {
cwd: PathBuf,
Simon Sapin
rhg: Group values passed to every sub-command into a struct...
r47334 }
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 /// CLI arguments to be parsed "early" in order to be able to read
/// configuration before using Clap. Ideally we would also use Clap for this,
/// see <https://github.com/clap-rs/clap/discussions/2366>.
///
/// These arguments are still declared when we do use Clap later, so that Clap
/// does not return an error for their presence.
struct EarlyArgs {
/// Values of all `--config` arguments. (Possibly none)
config: Vec<Vec<u8>>,
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 /// Value of all the `--color` argument, if any.
color: Option<Vec<u8>>,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 /// Value of the `-R` or `--repository` argument, if any.
repo: Option<Vec<u8>>,
Simon Sapin
rhg: Add support for --cwd...
r47470 /// Value of the `--cwd` argument, if any.
cwd: Option<Vec<u8>>,
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 }
impl EarlyArgs {
Arseniy Alekseyev
rhg: refactor to pass argv down, instead of caling args_os()...
r49961 fn parse<'a>(args: impl IntoIterator<Item = &'a OsString>) -> Self {
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 let mut args = args.into_iter().map(get_bytes_from_os_str);
let mut config = Vec::new();
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 let mut color = None;
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 let mut repo = None;
Simon Sapin
rhg: Add support for --cwd...
r47470 let mut cwd = None;
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 // Use `while let` instead of `for` so that we can also call
// `args.next()` inside the loop.
while let Some(arg) = args.next() {
if arg == b"--config" {
if let Some(value) = args.next() {
config.push(value)
}
} else if let Some(value) = arg.drop_prefix(b"--config=") {
config.push(value.to_owned())
}
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 if arg == b"--color" {
if let Some(value) = args.next() {
color = Some(value)
}
} else if let Some(value) = arg.drop_prefix(b"--color=") {
color = Some(value.to_owned())
}
Simon Sapin
rhg: Add support for --cwd...
r47470 if arg == b"--cwd" {
if let Some(value) = args.next() {
cwd = Some(value)
}
} else if let Some(value) = arg.drop_prefix(b"--cwd=") {
cwd = Some(value.to_owned())
}
Simon Sapin
rhg: Make configuration available as early as possible in main()...
r47423 if arg == b"--repository" || arg == b"-R" {
if let Some(value) = args.next() {
repo = Some(value)
}
} else if let Some(value) = arg.drop_prefix(b"--repository=") {
repo = Some(value.to_owned())
} else if let Some(value) = arg.drop_prefix(b"-R") {
repo = Some(value.to_owned())
}
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335 }
Simon Sapin
rhg: Add parsing for the --color global CLI argument...
r49583 Self {
config,
color,
repo,
cwd,
}
Simon Sapin
rhg: Move `Repo` object creation into `main()`...
r47335 }
}
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424
/// What to do when encountering some unsupported feature.
///
/// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
enum OnUnsupported {
/// Print an error message describing what feature is not supported,
/// and exit with code 252.
Abort,
/// Silently exit with code 252.
AbortSilent,
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 /// Try running a Python implementation
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 Fallback { executable: Option<Vec<u8>> },
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
impl OnUnsupported {
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 const DEFAULT: Self = OnUnsupported::Abort;
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 fn from_config(config: &Config) -> Self {
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 match config
.get(b"rhg", b"on-unsupported")
.map(|value| value.to_ascii_lowercase())
.as_deref()
{
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 Some(b"abort") => OnUnsupported::Abort,
Some(b"abort-silent") => OnUnsupported::AbortSilent,
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 Some(b"fallback") => OnUnsupported::Fallback {
executable: config
.get(b"rhg", b"fallback-executable")
Arseniy Alekseyev
rhg: only complain about poorly configured fallback when falling back...
r49176 .map(|x| x.to_owned()),
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 },
None => Self::DEFAULT,
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 Some(_) => {
// TODO: warn about unknown config value
Simon Sapin
rhg: Add support for automatic fallback to Python...
r47425 Self::DEFAULT
Simon Sapin
rhg: Add a `rhg.on-unsupported` configuration key...
r47424 }
}
}
}
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 /// The `*` extension is an edge-case for config sub-options that apply to all
/// extensions. For now, only `:required` exists, but that may change in the
/// future.
Raphaël Gomès
rhg: don't fallback if `strip` or `rebase` are activated...
r50376 const SUPPORTED_EXTENSIONS: &[&[u8]] = &[
b"blackbox",
b"share",
b"sparse",
b"narrow",
b"*",
b"strip",
b"rebase",
];
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467
fn check_extensions(config: &Config) -> Result<(), CommandError> {
Raphaël Gomès
rhg: add support for ignoring all extensions...
r49829 if let Some(b"*") = config.get(b"rhg", b"ignored-extensions") {
// All extensions are to be ignored, nothing to do here
return Ok(());
}
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 let enabled: HashSet<&[u8]> = config
Raphaël Gomès
rhg: support "!" syntax for disabling extensions...
r50372 .iter_section(b"extensions")
.filter_map(|(extension, value)| {
if value == b"!" {
// Filter out disabled extensions
return None;
}
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 // Ignore extension suboptions. Only `required` exists for now.
// `rhg` either supports an extension or doesn't, so it doesn't
// make sense to consider the loading of an extension.
Raphaël Gomès
rhg: support "!" syntax for disabling extensions...
r50372 let actual_extension =
extension.split_2(b':').unwrap_or((extension, b"")).0;
Some(actual_extension)
Raphaël Gomès
rhg: support the new extension suboptions syntax...
r49270 })
.collect();
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467
let mut unsupported = enabled;
for supported in SUPPORTED_EXTENSIONS {
unsupported.remove(supported);
}
Simon Sapin
rhg: Switch rhg.ignored-extensions config to Python-compatible list syntax...
r48763 if let Some(ignored_list) = config.get_list(b"rhg", b"ignored-extensions")
Simon Sapin
rhg: Add an allow-list of ignored extensions...
r47468 {
for ignored in ignored_list {
Simon Sapin
rhg: Switch rhg.ignored-extensions config to Python-compatible list syntax...
r48763 unsupported.remove(ignored.as_slice());
Simon Sapin
rhg: Add an allow-list of ignored extensions...
r47468 }
}
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467 if unsupported.is_empty() {
Ok(())
} else {
Raphaël Gomès
rhg: sort unsupported extensions in error message...
r49842 let mut unsupported: Vec<_> = unsupported.into_iter().collect();
// Sort the extensions to get a stable output
unsupported.sort();
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467 Err(CommandError::UnsupportedFeature {
message: format_bytes!(
Simon Sapin
rhg: Add an allow-list of ignored extensions...
r47468 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
Simon Sapin
rhg: Fall back to Python if unsupported extensions are enabled...
r47467 join(unsupported, b", ")
),
})
}
}
Simon Sapin
rhg: $HG_PENDING is not supported...
r49159
auto-upgrade: introduce a way to auto-upgrade to/from share-safe...
r50087 /// Array of tuples of (auto upgrade conf, feature conf, local requirement)
const AUTO_UPGRADES: &[((&str, &str), (&str, &str), &str)] = &[
(
("format", "use-share-safe.automatic-upgrade-of-mismatching-repositories"),
("format", "use-share-safe"),
requirements::SHARESAFE_REQUIREMENT,
),
auto-upgrade: introduce a way to auto-upgrade to/from tracked-hint...
r50089 (
("format", "use-dirstate-tracked-hint.automatic-upgrade-of-mismatching-repositories"),
("format", "use-dirstate-tracked-hint"),
requirements::DIRSTATE_TRACKED_HINT_V1,
),
auto-upgrade: introduce a way to auto-upgrade to/from dirstate-v2...
r50090 (
Arseniy Alekseyev
rhg: fix bugs around [use-dirstate-tracked-hint] and repo auto-upgrade...
r50395 ("format", "use-dirstate-v2.automatic-upgrade-of-mismatching-repositories"),
auto-upgrade: introduce a way to auto-upgrade to/from dirstate-v2...
r50090 ("format", "use-dirstate-v2"),
requirements::DIRSTATE_V2_REQUIREMENT,
),
auto-upgrade: introduce a way to auto-upgrade to/from share-safe...
r50087 ];
/// Mercurial allows users to automatically upgrade their repository.
/// `rhg` does not have the ability to upgrade yet, so fallback if an upgrade
/// is needed.
fn check_auto_upgrade(
config: &Config,
reqs: &HashSet<String>,
) -> Result<(), CommandError> {
for (upgrade_conf, feature_conf, local_req) in AUTO_UPGRADES.iter() {
let auto_upgrade = config
.get_bool(upgrade_conf.0.as_bytes(), upgrade_conf.1.as_bytes())?;
if auto_upgrade {
let want_it = config.get_bool(
feature_conf.0.as_bytes(),
feature_conf.1.as_bytes(),
)?;
let have_it = reqs.contains(*local_req);
let action = match (want_it, have_it) {
(true, false) => Some("upgrade"),
(false, true) => Some("downgrade"),
_ => None,
};
if let Some(action) = action {
let message = format!(
"automatic {} {}.{}",
action, upgrade_conf.0, upgrade_conf.1
);
return Err(CommandError::unsupported(message));
}
}
}
Ok(())
}
Simon Sapin
rhg: Colored output is not supported...
r49163 fn check_unsupported(
config: &Config,
Simon Sapin
rhg: Sub-repositories are not supported...
r49341 repo: Result<&Repo, &NoRepoInCwdError>,
Simon Sapin
rhg: Colored output is not supported...
r49163 ) -> Result<(), CommandError> {
Simon Sapin
rhg: $HG_PENDING is not supported...
r49159 check_extensions(config)?;
if std::env::var_os("HG_PENDING").is_some() {
// TODO: only if the value is `== repo.working_directory`?
// What about relative v.s. absolute paths?
Err(CommandError::unsupported("$HG_PENDING"))?
}
Simon Sapin
rhg: Sub-repositories are not supported...
r49341 if let Ok(repo) = repo {
if repo.has_subrepos()? {
Err(CommandError::unsupported("sub-repositories"))?
}
auto-upgrade: introduce a way to auto-upgrade to/from share-safe...
r50087 check_auto_upgrade(config, repo.requirements())?;
Simon Sapin
rhg: Sub-repositories are not supported...
r49341 }
Simon Sapin
rhg: [encode] and [decode] config sections are not supported...
r49162 if config.has_non_empty_section(b"encode") {
Err(CommandError::unsupported("[encode] config"))?
}
if config.has_non_empty_section(b"decode") {
Err(CommandError::unsupported("[decode] config"))?
}
Simon Sapin
rhg: $HG_PENDING is not supported...
r49159 Ok(())
}