changeset 46736:2255e7eb39e5

rhg: Add support for --cwd This affect the meaning of relative paths in `--repository`, which are resolved "early" by rhg in order to load config which is needed before fallback to Python is considered. An incorrect path could cause errors when loading a non-existent repo, leading to failing tests even when fallback is enabled. Differential Revision: https://phab.mercurial-scm.org/D10134
author Simon Sapin <simon.sapin@octobus.net>
date Fri, 05 Mar 2021 11:21:15 +0100
parents 12d59eec7f1d
children e96a0a53de20
files rust/rhg/src/main.rs
diffstat 1 files changed, 58 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/rust/rhg/src/main.rs	Thu Mar 04 13:30:20 2021 +0100
+++ b/rust/rhg/src/main.rs	Fri Mar 05 11:21:15 2021 +0100
@@ -53,6 +53,14 @@
                 // Not ok: `--config section.key1=val section.key2=val2`
                 .number_of_values(1),
         )
+        .arg(
+            Arg::with_name("cwd")
+                .help("change working directory")
+                .long("--cwd")
+                .value_name("DIR")
+                .takes_value(true)
+                .global(true),
+        )
         .version("0.0.1");
     let app = add_subcommand_args(app);
 
@@ -87,6 +95,28 @@
     let ui = ui::Ui::new();
 
     let early_args = EarlyArgs::parse(std::env::args_os());
+
+    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(
+                    &None,
+                    &ui,
+                    OnUnsupported::Abort,
+                    Err(CommandError::abort(format!(
+                        "abort: {}: '{}'",
+                        error,
+                        cwd.display()
+                    ))),
+                )
+            })
+    });
+
     let non_repo_config =
         Config::load(early_args.config).unwrap_or_else(|error| {
             // Normally this is decided based on config, but we don’t have that
@@ -94,7 +124,7 @@
             // "unsupported" error but that is not enforced by the type system.
             let on_unsupported = OnUnsupported::Abort;
 
-            exit(&ui, on_unsupported, Err(error.into()))
+            exit(&initial_current_dir, &ui, on_unsupported, Err(error.into()))
         });
 
     if let Some(repo_path_bytes) = &early_args.repo {
@@ -105,6 +135,7 @@
         }
         if SCHEME_RE.is_match(&repo_path_bytes) {
             exit(
+                &initial_current_dir,
                 &ui,
                 OnUnsupported::from_config(&non_repo_config),
                 Err(CommandError::UnsupportedFeature {
@@ -124,6 +155,7 @@
             Err(NoRepoInCwdError { cwd: at })
         }
         Err(error) => exit(
+            &initial_current_dir,
             &ui,
             OnUnsupported::from_config(&non_repo_config),
             Err(error.into()),
@@ -142,7 +174,12 @@
         repo_result.as_ref(),
         config,
     );
-    exit(&ui, OnUnsupported::from_config(config), result)
+    exit(
+        &initial_current_dir,
+        &ui,
+        OnUnsupported::from_config(config),
+        result,
+    )
 }
 
 fn exit_code(result: &Result<(), CommandError>) -> i32 {
@@ -159,6 +196,7 @@
 }
 
 fn exit(
+    initial_current_dir: &Option<PathBuf>,
     ui: &Ui,
     mut on_unsupported: OnUnsupported,
     result: Result<(), CommandError>,
@@ -182,7 +220,12 @@
             on_unsupported = OnUnsupported::Abort
         } else {
             // `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
-            let result = Command::new(executable_path).args(args).status();
+            let mut command = Command::new(executable_path);
+            command.args(args);
+            if let Some(initial) = initial_current_dir {
+                command.current_dir(initial);
+            }
+            let result = command.status();
             match result {
                 Ok(status) => std::process::exit(
                     status.code().unwrap_or(exitcode::ABORT),
@@ -283,6 +326,8 @@
     config: Vec<Vec<u8>>,
     /// Value of the `-R` or `--repository` argument, if any.
     repo: Option<Vec<u8>>,
+    /// Value of the `--cwd` argument, if any.
+    cwd: Option<Vec<u8>>,
 }
 
 impl EarlyArgs {
@@ -290,6 +335,7 @@
         let mut args = args.into_iter().map(get_bytes_from_os_str);
         let mut config = Vec::new();
         let mut repo = None;
+        let mut cwd = None;
         // Use `while let` instead of `for` so that we can also call
         // `args.next()` inside the loop.
         while let Some(arg) = args.next() {
@@ -301,6 +347,14 @@
                 config.push(value.to_owned())
             }
 
+            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())
+            }
+
             if arg == b"--repository" || arg == b"-R" {
                 if let Some(value) = args.next() {
                     repo = Some(value)
@@ -311,7 +365,7 @@
                 repo = Some(value.to_owned())
             }
         }
-        Self { config, repo }
+        Self { config, repo, cwd }
     }
 }