diff --git a/src/cli.rs b/src/cli.rs index e6fd2ed3f..8082225f9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -178,9 +178,9 @@ pub(crate) fn run(runtime: &tokio::runtime::Runtime) -> Result { } None => {} } - let streaming = args.format.is_some(); let metadata_or_quiet = args.quiet || args.metadata(); - if !streaming && !metadata_or_quiet { + let interactive = args.format.is_none() && !metadata_or_quiet; + if interactive && args.no_index { return launch_tui(&frontend_args, true); } if args.no_index { @@ -194,6 +194,11 @@ pub(crate) fn run(runtime: &tokio::runtime::Runtime) -> Result { let repo = gix::discover(&location)?; let workspace = repo.workdir().unwrap_or(repo.git_dir()); let (comparison, paths) = select(&repo, &location, &args, has_separator)?; + if interactive { + // Reject invalid revisions before the UI takes over the terminal. + comparison.resolve(&repo)?; + return launch_tui(&frontend_args, true); + } let files = FileParams { paths, // `-M` conflicts with `--no-renames`; renames are on by default. diff --git a/tests/git_comparison.rs b/tests/git_comparison.rs index c521acdc3..abd382307 100644 --- a/tests/git_comparison.rs +++ b/tests/git_comparison.rs @@ -42,6 +42,27 @@ fn statuses(text: &str) -> Vec { .collect() } +#[test] +fn invalid_revision_range_is_rejected_before_the_terminal_ui() { + let dir = tempfile::tempdir().unwrap(); + let repo = gix::init(dir.path()).unwrap(); + commit(&repo, "base"); + commit(&repo, "head"); + + let output = diffr(dir.path(), &["HEAD^2..HEAD"]); + assert_eq!(output.status.code(), Some(2)); + assert!(output.stdout.is_empty()); + let stderr = String::from_utf8(output.stderr).unwrap(); + assert!( + stderr.contains("unknown revision or path \"HEAD^2..HEAD\""), + "{stderr}" + ); + + let output = diffr(dir.path(), &["HEAD^..HEAD"]); + let stderr = String::from_utf8(output.stderr).unwrap(); + assert!(stderr.contains("terminal UI needs a terminal"), "{stderr}"); +} + #[test] fn revisions_index_worktree_reverse_paths_and_stats_match_git() { let dir = tempfile::tempdir().unwrap();