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; |
363 } else { |
364 } else { |
364 exit_codes::ABORT |
365 exit_codes::ABORT |
365 } |
366 } |
366 } |
367 } |
367 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL, |
368 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL, |
368 |
|
369 // Exit with a specific code and no error message to let a potential |
369 // Exit with a specific code and no error message to let a potential |
370 // wrapper script fallback to Python-based Mercurial. |
370 // wrapper script fallback to Python-based Mercurial. |
371 Err(CommandError::UnsupportedFeature { .. }) => { |
371 Err(CommandError::UnsupportedFeature { .. }) => { |
372 exit_codes::UNIMPLEMENTED |
372 exit_codes::UNIMPLEMENTED |
|
373 } |
|
374 Err(CommandError::InvalidFallback { .. }) => { |
|
375 exit_codes::INVALID_FALLBACK |
373 } |
376 } |
374 } |
377 } |
375 } |
378 } |
376 |
379 |
377 fn exit( |
380 fn exit( |
413 )); |
416 )); |
414 on_unsupported = OnUnsupported::Abort |
417 on_unsupported = OnUnsupported::Abort |
415 } else { |
418 } else { |
416 log::debug!("falling back (see trace-level log)"); |
419 log::debug!("falling back (see trace-level log)"); |
417 log::trace!("{}", local_to_utf8(message)); |
420 log::trace!("{}", local_to_utf8(message)); |
|
421 if let Err(err) = which::which(executable_path) { |
|
422 exit_no_fallback( |
|
423 ui, |
|
424 OnUnsupported::Abort, |
|
425 Err(CommandError::InvalidFallback { |
|
426 path: executable.to_owned(), |
|
427 err: err.to_string(), |
|
428 }), |
|
429 use_detailed_exit_code, |
|
430 ) |
|
431 } |
418 // `args` is now `argv[1..]` since we’ve already consumed |
432 // `args` is now `argv[1..]` since we’ve already consumed |
419 // `argv[0]` |
433 // `argv[0]` |
420 let mut command = Command::new(executable_path); |
434 let mut command = Command::new(executable_path); |
421 command.args(args); |
435 command.args(args); |
422 if let Some(initial) = initial_current_dir { |
436 if let Some(initial) = initial_current_dir { |
423 command.current_dir(initial); |
437 command.current_dir(initial); |
424 } |
438 } |
425 let result = command.status(); |
439 // We don't use subprocess because proper signal handling is harder |
426 match result { |
440 // and we don't want to keep `rhg` around after a fallback anyway. |
427 Ok(status) => std::process::exit( |
441 // For example, if `rhg` is run in the background and falls back to |
428 status.code().unwrap_or(exit_codes::ABORT), |
442 // `hg` which, in turn, waits for a signal, we'll get stuck if |
429 ), |
443 // we're doing plain subprocess. |
430 Err(error) => { |
444 // |
431 let _ = ui.write_stderr(&format_bytes!( |
445 // If `exec` returns, we can only assume our process is very broken |
432 b"tried to fall back to a '{}' sub-process but got error {}\n", |
446 // (see its documentation), so only try to forward the error code |
433 executable, format_bytes::Utf8(error) |
447 // when exiting. |
434 )); |
448 let err = command.exec(); |
435 on_unsupported = OnUnsupported::Abort |
449 std::process::exit( |
436 } |
450 err.raw_os_error().unwrap_or(exit_codes::ABORT), |
437 } |
451 ); |
438 } |
452 } |
439 } |
453 } |
440 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code) |
454 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code) |
441 } |
455 } |
442 |
456 |
469 } |
483 } |
470 OnUnsupported::AbortSilent => {} |
484 OnUnsupported::AbortSilent => {} |
471 OnUnsupported::Fallback { .. } => unreachable!(), |
485 OnUnsupported::Fallback { .. } => unreachable!(), |
472 } |
486 } |
473 } |
487 } |
|
488 Err(CommandError::InvalidFallback { path, err }) => { |
|
489 let _ = ui.write_stderr(&format_bytes!( |
|
490 b"abort: invalid fallback '{}': {}\n", |
|
491 path, |
|
492 err.as_bytes(), |
|
493 )); |
|
494 } |
474 } |
495 } |
475 std::process::exit(exit_code(&result, use_detailed_exit_code)) |
496 std::process::exit(exit_code(&result, use_detailed_exit_code)) |
476 } |
497 } |
477 |
498 |
478 macro_rules! subcommands { |
499 macro_rules! subcommands { |