Skip to content

Commit

Permalink
next/prev: update error message when no movement targets are found.
Browse files Browse the repository at this point in the history
If movement commands don't find a target commit, they fail. However,
it's usually not intuitive why they fail because in non-edit mode the
start commit is the parent of the working commit.

Adding the start commit change hash to the error message makes it easier
for the user to figure out what is going on.

Also, specifying 'No **other** descendant...' helps make it clear what
`jj` is really looking for.

Part of #3947
  • Loading branch information
essiene committed Aug 23, 2024
1 parent b05593f commit 9c9da31
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 40 deletions.
86 changes: 68 additions & 18 deletions cli/src/movement_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,55 @@ impl Direction {
}
}

fn target_not_found_message(&self, change_offset: u64) -> String {
match self {
Direction::Next => format!("No descendant found {} commit(s) forward", change_offset),
Direction::Prev => format!("No ancestor found {} commit(s) back", change_offset),
}
fn target_not_found_error(
&self,
workspace_command: &WorkspaceCommandHelper,
edit: bool,
change_offset: u64,
commits: &[Commit],
) -> CommandError {
let mut cmd_err = match self {
Direction::Next => {
if edit {
// in edit mode, start_revset is the WC, so
// we only look for direct descendants.
user_error(format!(
"No descendant found {} commit(s) forward from:",
change_offset
))
} else {
// in non-edit mode, start_revset is the parent
// of WC, so we look for other descendants of
// start_revset.
user_error(format!(
"No other descendant found {} commit(s) forward from:",
change_offset
))
}
}
// The WC can never be an ancestor of the start_revset since
// start_revset is either itself or it's parent.
Direction::Prev => user_error(format!(
"No ancestor found {} commit(s) back from:",
change_offset
)),
};

let template = workspace_command.commit_summary_template();

commits.iter().for_each(|commit| {
cmd_err.add_formatted_hint_with(|formatter| template.format(commit, formatter))
});

cmd_err
}

fn get_target_revset(
&self,
working_commit_id: &CommitId,
working_revset: &Rc<RevsetExpression>,
start_revset: &Rc<RevsetExpression>,
args: &MovementArgsInternal,
) -> Result<Rc<RevsetExpression>, CommandError> {
let wc_revset = RevsetExpression::commit(working_commit_id.clone());
// If we're editing, start at the working-copy commit. Otherwise, start from
// its direct parent(s).
let start_revset = if args.should_edit {
wc_revset.clone()
} else {
wc_revset.parents()
};

let target_revset = match self {
Direction::Next => if args.conflict {
start_revset
Expand All @@ -90,7 +118,7 @@ impl Direction {
} else {
start_revset.descendants_at(args.offset)
}
.minus(&wc_revset),
.minus(working_revset),

Direction::Prev => {
if args.conflict {
Expand Down Expand Up @@ -118,7 +146,24 @@ fn get_target_commit(
working_commit_id: &CommitId,
args: &MovementArgsInternal,
) -> Result<Commit, CommandError> {
let target_revset = direction.get_target_revset(working_commit_id, args)?;
let wc_revset = RevsetExpression::commit(working_commit_id.clone());
// If we're editing, start at the working-copy commit. Otherwise, start from
// its direct parent(s).
let start_revset = if args.should_edit {
wc_revset.clone()
} else {
wc_revset.parents()
};

let start_commits: Vec<Commit> = start_revset
.clone()
.evaluate_programmatic(workspace_command.repo().as_ref())?
.iter()
.commits(workspace_command.repo().store())
.try_collect()?;

let target_revset = direction.get_target_revset(&wc_revset, &start_revset, args)?;

let targets: Vec<Commit> = target_revset
.evaluate_programmatic(workspace_command.repo().as_ref())?
.iter()
Expand All @@ -129,7 +174,12 @@ fn get_target_commit(
[target] => target,
[] => {
// We found no ancestor/descendant.
return Err(user_error(direction.target_not_found_message(args.offset)));
return Err(direction.target_not_found_error(
workspace_command,
args.should_edit,
args.offset,
&start_commits,
));
}
commits => choose_commit(ui, workspace_command, direction, commits)?,
};
Expand Down
96 changes: 74 additions & 22 deletions cli/tests/test_next_prev_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ fn test_next_exceeding_history() {
let stderr = test_env.jj_cmd_failure(&repo_path, &["next", "3"]);
// `jj next` beyond existing history fails.
insta::assert_snapshot!(stderr, @r###"
Error: No descendant found 3 commit(s) forward
Error: No other descendant found 3 commit(s) forward from:
Hint: qpvuntsm fa15625b (empty) first
"###);
}

Expand Down Expand Up @@ -544,10 +545,12 @@ fn test_prev_prompts_on_multiple_parents() {
// Create a merge commit, which has two parents.
test_env.jj_cmd_ok(&repo_path, &["new", "all:@--+"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "merge"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "merge+1"]);

// Check that the graph looks the way we expect.
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ vruxwmqvtpmx
@ yostqsxwqrlt
○ vruxwmqvtpmx merge+1
○ yqosqzytrlsw merge
├─┬─╮
│ │ ○ qpvuntsmwlqt first
Expand All @@ -559,7 +562,7 @@ fn test_prev_prompts_on_multiple_parents() {
"###);

// Move @ backwards.
let (stdout, stderr) = test_env.jj_cmd_stdin_ok(&repo_path, &["prev"], "3\n");
let (stdout, stderr) = test_env.jj_cmd_stdin_ok(&repo_path, &["prev", "2"], "3\n");
insta::assert_snapshot!(stdout,@r###"
ambiguous prev commit, choose one to target:
1: mzvwutvl bc4f4fe3 (empty) third
Expand All @@ -569,9 +572,45 @@ fn test_prev_prompts_on_multiple_parents() {
enter the index of the commit you want to target:
"###);
insta::assert_snapshot!(stderr,@r###"
Working copy now at: znkkpsqq 07b409e8 (empty) (no description set)
Working copy now at: kpqxywon ddac00b0 (empty) (no description set)
Parent commit : qpvuntsm fa15625b (empty) first
"###);

insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ kpqxywonksrl
│ ○ vruxwmqvtpmx merge+1
│ ○ yqosqzytrlsw merge
╭─┼─╮
○ │ │ qpvuntsmwlqt first
│ │ ○ kkmpptxzrspx second
├───╯
│ ○ mzvwutvlkqwt third
├─╯
◆ zzzzzzzzzzzz
"###);

_ = test_env.jj_cmd_ok(&repo_path, &["next"]);
_ = test_env.jj_cmd_ok(&repo_path, &["edit", "@-"]);

insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
○ vruxwmqvtpmx merge+1
@ yqosqzytrlsw merge
├─┬─╮
│ │ ○ qpvuntsmwlqt first
│ ○ │ kkmpptxzrspx second
│ ├─╯
○ │ mzvwutvlkqwt third
├─╯
◆ zzzzzzzzzzzz
"###);

let stderr = test_env.jj_cmd_failure(&repo_path, &["next", "--no-edit"]);
insta::assert_snapshot!(stderr,@r###"
Error: No other descendant found 1 commit(s) forward from:
Hint: mzvwutvl bc4f4fe3 (empty) third
Hint: kkmpptxz b0d21db3 (empty) second
Hint: qpvuntsm fa15625b (empty) first
"###)
}

#[test]
Expand All @@ -594,7 +633,8 @@ fn test_prev_beyond_root_fails() {
// @- is at "fourth", and there is no parent 5 commits behind it.
let stderr = test_env.jj_cmd_failure(&repo_path, &["prev", "5"]);
insta::assert_snapshot!(stderr,@r###"
Error: No ancestor found 5 commit(s) back
Error: No ancestor found 5 commit(s) back from:
Hint: zsuskuln 9d7e5e99 (empty) fourth
"###);
}

Expand Down Expand Up @@ -826,14 +866,17 @@ fn test_next_conflict_head() {
@ rlvkpnrzqnoo conflict
◆ zzzzzzzzzzzz
"###);

let stderr = test_env.jj_cmd_failure(&repo_path, &["next", "--conflict"]);
insta::assert_snapshot!(stderr, @r###"
Error: No descendant found 1 commit(s) forward
Error: No other descendant found 1 commit(s) forward from:
Hint: zzzzzzzz 00000000 (empty) (no description set)
"###);

let stderr = test_env.jj_cmd_failure(&repo_path, &["next", "--conflict", "--edit"]);
insta::assert_snapshot!(stderr, @r###"
Error: No descendant found 1 commit(s) forward
Error: No descendant found 1 commit(s) forward from:
Hint: rlvkpnrz da992bf2 (conflict) (no description set)
"###);
}

Expand Down Expand Up @@ -893,6 +936,11 @@ fn test_movement_edit_mode_true() {
◆ zzzzzzzzzzzz
"###);

let stderr = test_env.jj_cmd_failure(&repo_path, &["prev"]);
insta::assert_snapshot!(stderr, @r###"
Error: The root commit 000000000000 is immutable
"###);

let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["next"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Expand Down Expand Up @@ -924,12 +972,12 @@ fn test_movement_edit_mode_true() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["prev", "--no-edit"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Working copy now at: nkmrtpmo 67a001a6 (empty) (no description set)
Working copy now at: uyznsvlq 7ad57fb8 (empty) (no description set)
Parent commit : qpvuntsm fa15625b (empty) first
"###);

insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ nkmrtpmomlro
@ uyznsvlquzzm
│ ○ kkmpptxzrspx third
│ ○ rlvkpnrzqnoo second
├─╯
Expand All @@ -940,18 +988,24 @@ fn test_movement_edit_mode_true() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["next", "--no-edit"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Working copy now at: xznxytkn 22287813 (empty) (no description set)
Working copy now at: xtnwkqum 7ac7a1c4 (empty) (no description set)
Parent commit : rlvkpnrz 9ed53a4a (empty) second
"###);

insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ xznxytknoqwo
@ xtnwkqumpolk
│ ○ kkmpptxzrspx third
├─╯
○ rlvkpnrzqnoo second
○ qpvuntsmwlqt first
◆ zzzzzzzzzzzz
"###);

let stderr = test_env.jj_cmd_failure(&repo_path, &["next"]);
insta::assert_snapshot!(stderr, @r###"
Error: No descendant found 1 commit(s) forward from:
Hint: xtnwkqum 7ac7a1c4 (empty) (no description set)
"###);
}

#[test]
Expand Down Expand Up @@ -1000,15 +1054,21 @@ fn test_movement_edit_mode_false() {
◆ zzzzzzzzzzzz
"###);

let stderr = test_env.jj_cmd_failure(&repo_path, &["prev", "3"]);
insta::assert_snapshot!(stderr, @r###"
Error: No ancestor found 3 commit(s) back from:
Hint: qpvuntsm fa15625b (empty) first
"###);

let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["next"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Working copy now at: znkkpsqq a8419fd6 (empty) (no description set)
Working copy now at: kpqxywon d06750fb (empty) (no description set)
Parent commit : rlvkpnrz 9ed53a4a (empty) second
"###);

insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ znkkpsqqskkl
@ kpqxywonksrl
│ ○ kkmpptxzrspx third
├─╯
○ rlvkpnrzqnoo second
Expand All @@ -1019,18 +1079,10 @@ fn test_movement_edit_mode_false() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["next", "--no-edit"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Working copy now at: kmkuslsw 8c4d85ef (empty) (no description set)
Working copy now at: wqnwkozp 10fa181f (empty) (no description set)
Parent commit : kkmpptxz 30056b0c (empty) third
"###);

insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ kmkuslswpqwq
○ kkmpptxzrspx third
○ rlvkpnrzqnoo second
○ qpvuntsmwlqt first
◆ zzzzzzzzzzzz
"###);

let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["prev", "--edit", "2"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Expand Down

0 comments on commit 9c9da31

Please sign in to comment.