11 use hg::repo::{Repo, RepoError}; |
11 use hg::repo::{Repo, RepoError}; |
12 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes}; |
12 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes}; |
13 use hg::utils::SliceExt; |
13 use hg::utils::SliceExt; |
14 use std::collections::HashSet; |
14 use std::collections::HashSet; |
15 use std::ffi::OsString; |
15 use std::ffi::OsString; |
|
16 use std::os::unix::prelude::CommandExt; |
16 use std::path::PathBuf; |
17 use std::path::PathBuf; |
17 use std::process::Command; |
18 use std::process::Command; |
18 |
19 |
19 mod blackbox; |
20 mod blackbox; |
20 mod color; |
21 mod color; |
379 } else { |
380 } else { |
380 exit_codes::ABORT |
381 exit_codes::ABORT |
381 } |
382 } |
382 } |
383 } |
383 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL, |
384 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL, |
384 |
|
385 // Exit with a specific code and no error message to let a potential |
385 // Exit with a specific code and no error message to let a potential |
386 // wrapper script fallback to Python-based Mercurial. |
386 // wrapper script fallback to Python-based Mercurial. |
387 Err(CommandError::UnsupportedFeature { .. }) => { |
387 Err(CommandError::UnsupportedFeature { .. }) => { |
388 exit_codes::UNIMPLEMENTED |
388 exit_codes::UNIMPLEMENTED |
|
389 } |
|
390 Err(CommandError::InvalidFallback { .. }) => { |
|
391 exit_codes::INVALID_FALLBACK |
389 } |
392 } |
390 } |
393 } |
391 } |
394 } |
392 |
395 |
393 fn exit<'a>( |
396 fn exit<'a>( |
430 )); |
433 )); |
431 on_unsupported = OnUnsupported::Abort |
434 on_unsupported = OnUnsupported::Abort |
432 } else { |
435 } else { |
433 log::debug!("falling back (see trace-level log)"); |
436 log::debug!("falling back (see trace-level log)"); |
434 log::trace!("{}", local_to_utf8(message)); |
437 log::trace!("{}", local_to_utf8(message)); |
|
438 if let Err(err) = which::which(executable_path) { |
|
439 exit_no_fallback( |
|
440 ui, |
|
441 OnUnsupported::Abort, |
|
442 Err(CommandError::InvalidFallback { |
|
443 path: executable.to_owned(), |
|
444 err: err.to_string(), |
|
445 }), |
|
446 use_detailed_exit_code, |
|
447 ) |
|
448 } |
435 // `args` is now `argv[1..]` since we’ve already consumed |
449 // `args` is now `argv[1..]` since we’ve already consumed |
436 // `argv[0]` |
450 // `argv[0]` |
437 let mut command = Command::new(executable_path); |
451 let mut command = Command::new(executable_path); |
438 command.args(args); |
452 command.args(args); |
439 if let Some(initial) = initial_current_dir { |
453 if let Some(initial) = initial_current_dir { |
440 command.current_dir(initial); |
454 command.current_dir(initial); |
441 } |
455 } |
442 let result = command.status(); |
456 // We don't use subprocess because proper signal handling is harder |
443 match result { |
457 // and we don't want to keep `rhg` around after a fallback anyway. |
444 Ok(status) => std::process::exit( |
458 // For example, if `rhg` is run in the background and falls back to |
445 status.code().unwrap_or(exit_codes::ABORT), |
459 // `hg` which, in turn, waits for a signal, we'll get stuck if |
446 ), |
460 // we're doing plain subprocess. |
447 Err(error) => { |
461 // |
448 let _ = ui.write_stderr(&format_bytes!( |
462 // If `exec` returns, we can only assume our process is very broken |
449 b"tried to fall back to a '{}' sub-process but got error {}\n", |
463 // (see its documentation), so only try to forward the error code |
450 executable, format_bytes::Utf8(error) |
464 // when exiting. |
451 )); |
465 let err = command.exec(); |
452 on_unsupported = OnUnsupported::Abort |
466 std::process::exit( |
453 } |
467 err.raw_os_error().unwrap_or(exit_codes::ABORT), |
454 } |
468 ); |
455 } |
469 } |
456 } |
470 } |
457 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code) |
471 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code) |
458 } |
472 } |
459 |
473 |
486 } |
500 } |
487 OnUnsupported::AbortSilent => {} |
501 OnUnsupported::AbortSilent => {} |
488 OnUnsupported::Fallback { .. } => unreachable!(), |
502 OnUnsupported::Fallback { .. } => unreachable!(), |
489 } |
503 } |
490 } |
504 } |
|
505 Err(CommandError::InvalidFallback { path, err }) => { |
|
506 let _ = ui.write_stderr(&format_bytes!( |
|
507 b"abort: invalid fallback '{}': {}\n", |
|
508 path, |
|
509 err.as_bytes(), |
|
510 )); |
|
511 } |
491 } |
512 } |
492 std::process::exit(exit_code(&result, use_detailed_exit_code)) |
513 std::process::exit(exit_code(&result, use_detailed_exit_code)) |
493 } |
514 } |
494 |
515 |
495 macro_rules! subcommands { |
516 macro_rules! subcommands { |