From 409b1058628b4db3edaf4531505d9c146dbd16e6 Mon Sep 17 00:00:00 2001 From: Philip Metzger Date: Sun, 18 Feb 2024 18:57:55 +0100 Subject: [PATCH] next/prev: Implement `next/prev --conflict` This allows users to jump to the next conflict in the ancestors or children of the start commit. Continues work on #2126 --- CHANGELOG.md | 3 ++ cli/src/commands/next.rs | 11 +++++- cli/src/commands/prev.rs | 11 +++++- cli/tests/cli-reference@.md.snap | 8 +++++ cli/tests/test_next_prev_commands.rs | 52 ++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16792f19e2..c292fe1053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,9 @@ No code changes (fixing Rust `Cargo.toml` stuff). * `ui.default-command` now accepts multiple string arguments, for more complex default `jj` commands. +* `jj prev` and `jj next` have gained a `--conflict` flag which moves you + to the next conflict in a child commit. + ### Fixed bugs * On Windows, symlinks in the repo are now supported when Developer Mode is enabled. diff --git a/cli/src/commands/next.rs b/cli/src/commands/next.rs index 520f2fc697..c8703dfaa7 100644 --- a/cli/src/commands/next.rs +++ b/cli/src/commands/next.rs @@ -63,6 +63,9 @@ pub(crate) struct NextArgs { /// edit`). #[arg(long)] edit: bool, + /// Jump to the next conflicted descendant. + #[arg(long, conflicts_with = "amount")] + conflict: bool, } pub fn choose_commit<'a>( @@ -113,6 +116,7 @@ pub(crate) fn cmd_next( .view() .heads() .contains(current_wc_id); + let conflict = args.conflict; let current_wc = workspace_command.repo().store().get_commit(current_wc_id)?; // If we're editing, start at the working-copy commit. // Otherwise start from our direct parent. @@ -124,7 +128,12 @@ pub(crate) fn cmd_next( _ => return Err(user_error("Cannot run `jj next` on a merge commit")), } }; - let descendant_expression = RevsetExpression::commit(start_id.clone()).descendants_at(amount); + let base_expr = RevsetExpression::commit(start_id.clone()); + let descendant_expression = if conflict { + base_expr.descendants().conflicts() + } else { + base_expr.descendants_at(amount) + }; let target_expression = if edit { descendant_expression } else { diff --git a/cli/src/commands/prev.rs b/cli/src/commands/prev.rs index 58af0e1ff9..bbfaa43089 100644 --- a/cli/src/commands/prev.rs +++ b/cli/src/commands/prev.rs @@ -59,6 +59,9 @@ pub(crate) struct PrevArgs { /// Edit the parent directly, instead of moving the working-copy commit. #[arg(long)] edit: bool, + /// Jump to nearest the conflicted ancestor. + #[arg(long, conflicts_with = "amount")] + conflict: bool, } pub(crate) fn cmd_prev( @@ -68,6 +71,7 @@ pub(crate) fn cmd_prev( ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; let amount = args.amount; + let conflict = args.conflict; let current_wc_id = workspace_command .get_wc_commit_id() .ok_or_else(|| user_error("This command requires a working copy"))?; @@ -86,7 +90,12 @@ pub(crate) fn cmd_prev( _ => return Err(user_error("Cannot run `jj prev` on a merge commit")), } }; - let ancestor_expression = RevsetExpression::commit(start_id.clone()).ancestors_at(amount); + let base_expr = RevsetExpression::commit(start_id.clone()); + let ancestor_expression = if conflict { + base_expr.ancestors().conflicts() + } else { + base_expr.ancestors_at(amount) + }; let target_revset = if edit { ancestor_expression } else { diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index b825dc6e09..77bc24300e 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -1181,6 +1181,10 @@ implied. Possible values: `true`, `false` +* `--conflict` — Jump to the next conflicted descendant + + Possible values: `true`, `false` + @@ -1385,6 +1389,10 @@ implied. Possible values: `true`, `false` +* `--conflict` — Jump to nearest the conflicted ancestor + + Possible values: `true`, `false` + diff --git a/cli/tests/test_next_prev_commands.rs b/cli/tests/test_next_prev_commands.rs index b75a0447aa..e6f81c1cf3 100644 --- a/cli/tests/test_next_prev_commands.rs +++ b/cli/tests/test_next_prev_commands.rs @@ -318,3 +318,55 @@ fn test_next_editing() { Parent commit : kkmpptxz 3fa8931e (empty) third "###); } + +// #[test] +// fn test_prev_conflict() { +// // Edit the third commit. +// let test_env = TestEnvironment::default(); +// test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); +// let repo_path = test_env.env_root().join("repo"); +// test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "first"]); +// test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "second"]); +// test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "third"]); +// test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "fourth"]); +// let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["prev", "--conflict"]); +// insta::assert_snapshot!(stdout, @""); +// insta::assert_snapshot!(stderr, @r###" +// Working copy now at: zsuskuln 009f88bf (empty) fourth +// Parent commit : kkmpptxz 3fa8931e (empty) third +// "###); +// // --edit is implied when already editing a non-head commit +// let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["prev"]); +// insta::assert_snapshot!(stdout, @""); +// insta::assert_snapshot!(stderr, @r###" +// Working copy now at: yqosqzyt d2edc95b (empty) (no description set) +// Parent commit : rlvkpnrz 5c52832c (empty) second +// "###); +// } + +// #[test] +// fn test_next_conflict() { +// // There is a conflict in the fourth commit, so after next we should create a +// // new revision on top of it. +// let test_env = TestEnvironment::default(); +// test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); +// let repo_path = test_env.env_root().join("repo"); +// test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "first"]); +// test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "second"]); +// test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "third"]); +// test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "fourth"]); +// test_env.jj_cmd_ok(&repo_path, &["edit", "@---"]); +// let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["next", "--conflict"]); +// insta::assert_snapshot!(stdout, @""); +// insta::assert_snapshot!(stderr, @r###" +// Working copy now at: kkmpptxz 3fa8931e (empty) third +// Parent commit : rlvkpnrz 5c52832c (empty) second +// "###); +// // --edit is implied when already editing a non-head commit +// let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["next"]); +// insta::assert_snapshot!(stdout, @""); +// insta::assert_snapshot!(stderr, @r###" +// Working copy now at: zsuskuln 009f88bf (empty) fourth +// Parent commit : kkmpptxz 3fa8931e (empty) third +// "###); +// }