// Copyright 2026 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. use test_case::test_case;use testutils::TestResult; use crate::common::CommandOutput;use crate::common::TestEnvironment;use crate::common::TestWorkDir;use crate::common::create_commit_with_files;use crate::common::force_interactive; // `jj converge` must runs successfully when there are no divergent changes and// prints a message to stderr.#[test]fn test_converge_no_divergence() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up commit graph (without divergent changes) create_commit_with_files(&work_dir, "a", &[], &[("file1", "a")]); create_commit_with_files(&work_dir, "b", &["a"], &[("file2", "b")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "c")]); // Test the setup insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ c royxmykx 78dcec21 - description: c │ ○ b zsuskuln 056564da - description: b ├─╯ ○ a rlvkpnrz 3b93fc14 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Run `jj converge` command and check the output. let output = work_dir.run_jj(["converge"]).success(); insta::assert_snapshot!(output, @r" ------- stderr ------- No divergent changes found. [EOF] ");} // A simple `jj converge` scenario where there is a single divergent change with// two visible commits. In this setup no user input is required.#[test]fn test_converge_simple() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up commit graph with one divergent change (with two visible commits). create_commit_with_files(&work_dir, "a", &[], &[("file1", "1")]); create_commit_with_files(&work_dir, "b2", &["a"], &[("file2", "2")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "3")]); work_dir.run_jj(["rebase", "-r", "b2", "-o", "c"]).success(); work_dir .run_jj(["bookmark", "create", "b1", "-r", "at_operation(@-, b2)"]) .success(); create_commit_with_files(&work_dir, "d", &["b1"], &[("file4", "4")]); // Test the setup: look at the commit graph, commit B is duplicated insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ d znkkpsqq bf5126ef - description: d ○ b1 zsuskuln/1 59a77004 - description: b2 │ ○ b2 zsuskuln/0 2c2bd25d - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Test the setup: look at the evolog insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln/0 2c2bd25d (divergent) b2 ○ zsuskuln/1 59a77004 (divergent) b2 ○ zsuskuln/2 b2852eb2 (hidden) (empty) b2 [EOF] "); // Run `jj converge` command and check the output. In this case no user input is // needed. let output = work_dir.run_jj(["converge"]).success(); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 1 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 Attempting to converge change zsuskulnrvyr... Successfully converged change: created commit 6b1ce5bc4cbe. Rebased 1 descendants Working copy (@) now at: znkkpsqq 696cf5e0 d | d Parent commit (@-) : zsuskuln 6b1ce5bc b1 b2 | b2 Added 1 files, modified 0 files, removed 0 files [EOF] "); // Verify the commit graph after converge insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ d znkkpsqq 696cf5e0 - description: d ○ b1 b2 zsuskuln 6b1ce5bc - description: b2 ○ c royxmykx 4343fc61 - description: c ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Verify the evolution history after converge insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln 6b1ce5bc b2 ├─╮ ○ │ zsuskuln/1 2c2bd25d (hidden) b2 ├─╯ ○ zsuskuln/2 59a77004 (hidden) b2 ○ zsuskuln/3 b2852eb2 (hidden) (empty) b2 [EOF] ");} // When there are multiple divergent changes, the command must prompt the user// to select one of them. When running in non-interactive mode (jj converge// --no-interactive) this is not possible.#[test]fn test_converge_two_divergent_changes_in_non_interactive_mode() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up: first create a base commit create_commit_with_files(&work_dir, "a", &[], &[("file1", "1")]); // Set up: create commit graph with two divergent changes // First divergent change: create_commit_with_files(&work_dir, "b2", &["a"], &[("file2", "2")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "3")]); work_dir.run_jj(["rebase", "-r", "b2", "-o", "c"]).success(); work_dir .run_jj(["bookmark", "create", "b1", "-r", "at_operation(@-, b2)"]) .success(); create_commit_with_files(&work_dir, "d", &["b1"], &[("file4", "4")]); // Second divergent change: create_commit_with_files(&work_dir, "e2", &["a"], &[("file5", "5")]); create_commit_with_files(&work_dir, "f", &["a"], &[("file6", "6")]); work_dir.run_jj(["rebase", "-r", "e2", "-o", "f"]).success(); work_dir .run_jj(["bookmark", "create", "e1", "-r", "at_operation(@-, e2)"]) .success(); create_commit_with_files(&work_dir, "g", &["e1"], &[("file7", "7")]); // Test the setup: look at the commit graph (commit B is duplicated and commit E // is duplicated) insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ g xznxytkn 46658cae - description: g ○ e1 kmkuslsw/1 15962bae - description: e2 │ ○ e2 kmkuslsw/0 b54f15d8 - description: e2 │ ○ f lylxulpl d50e2761 - description: f ├─╯ │ ○ b2 zsuskuln/0 2c2bd25d - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ │ ○ d znkkpsqq bf5126ef - description: d │ ○ b1 zsuskuln/1 59a77004 - description: b2 ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Pass --non-interactive to jj converge command. let output = work_dir.run_jj_with(|cmd| force_interactive(cmd).args(["converge", "--no-interactive"])); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Error: Cannot automatically choose which change to converge Hint: Run `jj converge` in interactive mode, or specify a revset that resolves to only one change ID [EOF] [exit status: 1] "); // Note: in the test environment jj commands run in non-interactive (quiet) mode // by default, so the following also fails but for a different reason: it // cannot prompt the user let output = work_dir.run_jj(["converge"]); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Choose which change to converge (jj converge only converges one change at a time): 1: zsuskulnrvyr 2: kmkuslswpqwq q: abort Error: Cannot prompt for input since the output is not connected to a terminal [EOF] [exit status: 1] "); // Note: the invocation also fails if stdin is not connected to a terminal let output = work_dir.run_jj_with(|cmd| force_interactive(cmd).args(["converge"])); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Choose which change to converge (jj converge only converges one change at a time): 1: zsuskulnrvyr 2: kmkuslswpqwq q: abort Enter the index of the change to converge: Error: Prompt canceled by EOF [EOF] [exit status: 1] ");} // This tests scenarios where there are two divergent changes. The command// prompts the user to choose which change to converge.#[test]fn test_converge_two_divergent_changes() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up: first create a base commit create_commit_with_files(&work_dir, "a", &[], &[("file1", "1")]); // Set up: create commit graph with two divergent changes // First divergent change: create_commit_with_files(&work_dir, "b2", &["a"], &[("file2", "2")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "3")]); work_dir.run_jj(["rebase", "-r", "b2", "-o", "c"]).success(); work_dir .run_jj(["bookmark", "create", "b1", "-r", "at_operation(@-, b2)"]) .success(); create_commit_with_files(&work_dir, "d", &["b1"], &[("file4", "4")]); // Second divergent change: create_commit_with_files(&work_dir, "e2", &["a"], &[("file5", "5")]); create_commit_with_files(&work_dir, "f", &["a"], &[("file6", "6")]); work_dir.run_jj(["rebase", "-r", "e2", "-o", "f"]).success(); work_dir .run_jj(["bookmark", "create", "e1", "-r", "at_operation(@-, e2)"]) .success(); create_commit_with_files(&work_dir, "g", &["e1"], &[("file7", "7")]); // Test the setup: look at the commit graph (commit B is duplicated and commit E // is duplicated) insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ g xznxytkn 46658cae - description: g ○ e1 kmkuslsw/1 15962bae - description: e2 │ ○ e2 kmkuslsw/0 b54f15d8 - description: e2 │ ○ f lylxulpl d50e2761 - description: f ├─╯ │ ○ b2 zsuskuln/0 2c2bd25d - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ │ ○ d znkkpsqq bf5126ef - description: d │ ○ b1 zsuskuln/1 59a77004 - description: b2 ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Test the setup: look at the evolog insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln/0 2c2bd25d (divergent) b2 ○ zsuskuln/1 59a77004 (divergent) b2 ○ zsuskuln/2 b2852eb2 (hidden) (empty) b2 [EOF] "); // Test the setup: look at the evolog insta::assert_snapshot!(get_evolog(&work_dir, "e2"), @r" ○ kmkuslsw/0 b54f15d8 (divergent) e2 ○ kmkuslsw/1 15962bae (divergent) e2 ○ kmkuslsw/2 843de29d (hidden) (empty) e2 [EOF] "); // If the user chooses to abort the converge operation nothing changes. let output = work_dir .run_jj_with(|cmd| force_interactive(cmd).args(["converge"]).write_stdin("q\n")) .success(); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Choose which change to converge (jj converge only converges one change at a time): 1: zsuskulnrvyr 2: kmkuslswpqwq q: abort Enter the index of the change to converge: Aborting... nothing changed. [EOF] "); // Run the command again, this time the user chooses the first divergent change. // This invocation succeeds to automatically converge that change. A hint is // printed to inform the user that there is still one divergent change // remaining. let output = work_dir .run_jj_with(|cmd| force_interactive(cmd).args(["converge"]).write_stdin("1\n")) .success(); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Choose which change to converge (jj converge only converges one change at a time): 1: zsuskulnrvyr 2: kmkuslswpqwq q: abort Enter the index of the change to converge: Attempting to converge change zsuskulnrvyr... Successfully converged change: created commit ba447f020ee6. Rebased 1 descendants Hint: There are still 1 divergent changes remaining in the specified revset, you can run this command again to converge another one. [EOF] "); // Verify the commit graph after converging the first divergent change insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ g xznxytkn 46658cae - description: g ○ e1 kmkuslsw/1 15962bae - description: e2 │ ○ e2 kmkuslsw/0 b54f15d8 - description: e2 │ ○ f lylxulpl d50e2761 - description: f ├─╯ │ ○ d znkkpsqq b30d892b - description: d │ ○ b1 b2 zsuskuln ba447f02 - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Verify the evolution history after converging the first divergent change insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln ba447f02 b2 ├─╮ ○ │ zsuskuln/1 2c2bd25d (hidden) b2 ├─╯ ○ zsuskuln/2 59a77004 (hidden) b2 ○ zsuskuln/3 b2852eb2 (hidden) (empty) b2 [EOF] "); // Run converge a second time to converge the other divergent change let output = work_dir.run_jj(["converge"]).success(); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 1 divergent change(s) in the specified revset: - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Attempting to converge change kmkuslswpqwq... Successfully converged change: created commit 3f08d00a88c4. Rebased 1 descendants Working copy (@) now at: xznxytkn f9fd4e7e g | g Parent commit (@-) : kmkuslsw 3f08d00a e1 e2 | e2 Added 1 files, modified 0 files, removed 0 files [EOF] "); // Verify the commit graph after converging the second divergent change insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ g xznxytkn f9fd4e7e - description: g ○ e1 e2 kmkuslsw 3f08d00a - description: e2 ○ f lylxulpl d50e2761 - description: f │ ○ d znkkpsqq b30d892b - description: d │ ○ b1 b2 zsuskuln ba447f02 - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Verify the evolution history after converging the second divergent change insta::assert_snapshot!(get_evolog(&work_dir, "e2"), @r" ○ kmkuslsw 3f08d00a e2 ├─╮ ○ │ kmkuslsw/1 b54f15d8 (hidden) e2 ├─╯ ○ kmkuslsw/2 15962bae (hidden) e2 ○ kmkuslsw/3 843de29d (hidden) (empty) e2 [EOF] "); // There are no more divergent changes now let output = work_dir.run_jj(["converge"]).success(); insta::assert_snapshot!(output, @r" ------- stderr ------- No divergent changes found. [EOF] ");} // This tests scenarios where the user specifies revisions to converge. More// precisely, the user specifies a revset that is used as the search space for// divergent commits.#[test]fn test_converge_simple_with_revisions_arg() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up commit graph with divergent changes create_commit_with_files(&work_dir, "a", &[], &[("file1", "1")]); create_commit_with_files(&work_dir, "b2", &["a"], &[("file2", "2")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "3")]); work_dir.run_jj(["rebase", "-r", "b2", "-o", "c"]).success(); work_dir .run_jj(["bookmark", "create", "b1", "-r", "at_operation(@-, b2)"]) .success(); create_commit_with_files(&work_dir, "d", &["b1"], &[("file4", "4")]); // Test the setup (commit B is duplicated) insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ d znkkpsqq bf5126ef - description: d ○ b1 zsuskuln/1 59a77004 - description: b2 │ ○ b2 zsuskuln/0 2c2bd25d - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln/0 2c2bd25d (divergent) b2 ○ zsuskuln/1 59a77004 (divergent) b2 ○ zsuskuln/2 b2852eb2 (hidden) (empty) b2 [EOF] "); // `-r a::d` resolves to {a, b1, d}. b1 IS a divergent commit, but in that // revset there are no other commits with that change-id, so by design the // command does nothing (we could change that in the future). let output = work_dir.run_jj(["converge", "-r", "a::d"]).success(); insta::assert_snapshot!(output, @r" ------- stderr -------Lines 1–500 of 1631. Load more
// Copyright 2026 The Jujutsu Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use test_case::test_case; use testutils::TestResult; use crate::common::CommandOutput; use crate::common::TestEnvironment; use crate::common::TestWorkDir; use crate::common::create_commit_with_files; use crate::common::force_interactive; // `jj converge` must runs successfully when there are no divergent changes and // prints a message to stderr. #[test] fn test_converge_no_divergence() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up commit graph (without divergent changes) create_commit_with_files(&work_dir, "a", &[], &[("file1", "a")]); create_commit_with_files(&work_dir, "b", &["a"], &[("file2", "b")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "c")]); // Test the setup insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ c royxmykx 78dcec21 - description: c │ ○ b zsuskuln 056564da - description: b ├─╯ ○ a rlvkpnrz 3b93fc14 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Run `jj converge` command and check the output. let output = work_dir.run_jj(["converge"]).success(); insta::assert_snapshot!(output, @r" ------- stderr ------- No divergent changes found. [EOF] "); } // A simple `jj converge` scenario where there is a single divergent change with // two visible commits. In this setup no user input is required. #[test] fn test_converge_simple() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up commit graph with one divergent change (with two visible commits). create_commit_with_files(&work_dir, "a", &[], &[("file1", "1")]); create_commit_with_files(&work_dir, "b2", &["a"], &[("file2", "2")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "3")]); work_dir.run_jj(["rebase", "-r", "b2", "-o", "c"]).success(); work_dir .run_jj(["bookmark", "create", "b1", "-r", "at_operation(@-, b2)"]) .success(); create_commit_with_files(&work_dir, "d", &["b1"], &[("file4", "4")]); // Test the setup: look at the commit graph, commit B is duplicated insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ d znkkpsqq bf5126ef - description: d ○ b1 zsuskuln/1 59a77004 - description: b2 │ ○ b2 zsuskuln/0 2c2bd25d - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Test the setup: look at the evolog insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln/0 2c2bd25d (divergent) b2 ○ zsuskuln/1 59a77004 (divergent) b2 ○ zsuskuln/2 b2852eb2 (hidden) (empty) b2 [EOF] "); // Run `jj converge` command and check the output. In this case no user input is // needed. let output = work_dir.run_jj(["converge"]).success(); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 1 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 Attempting to converge change zsuskulnrvyr... Successfully converged change: created commit 6b1ce5bc4cbe. Rebased 1 descendants Working copy (@) now at: znkkpsqq 696cf5e0 d | d Parent commit (@-) : zsuskuln 6b1ce5bc b1 b2 | b2 Added 1 files, modified 0 files, removed 0 files [EOF] "); // Verify the commit graph after converge insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ d znkkpsqq 696cf5e0 - description: d ○ b1 b2 zsuskuln 6b1ce5bc - description: b2 ○ c royxmykx 4343fc61 - description: c ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Verify the evolution history after converge insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln 6b1ce5bc b2 ├─╮ ○ │ zsuskuln/1 2c2bd25d (hidden) b2 ├─╯ ○ zsuskuln/2 59a77004 (hidden) b2 ○ zsuskuln/3 b2852eb2 (hidden) (empty) b2 [EOF] "); } // When there are multiple divergent changes, the command must prompt the user // to select one of them. When running in non-interactive mode (jj converge // --no-interactive) this is not possible. #[test] fn test_converge_two_divergent_changes_in_non_interactive_mode() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up: first create a base commit create_commit_with_files(&work_dir, "a", &[], &[("file1", "1")]); // Set up: create commit graph with two divergent changes // First divergent change: create_commit_with_files(&work_dir, "b2", &["a"], &[("file2", "2")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "3")]); work_dir.run_jj(["rebase", "-r", "b2", "-o", "c"]).success(); work_dir .run_jj(["bookmark", "create", "b1", "-r", "at_operation(@-, b2)"]) .success(); create_commit_with_files(&work_dir, "d", &["b1"], &[("file4", "4")]); // Second divergent change: create_commit_with_files(&work_dir, "e2", &["a"], &[("file5", "5")]); create_commit_with_files(&work_dir, "f", &["a"], &[("file6", "6")]); work_dir.run_jj(["rebase", "-r", "e2", "-o", "f"]).success(); work_dir .run_jj(["bookmark", "create", "e1", "-r", "at_operation(@-, e2)"]) .success(); create_commit_with_files(&work_dir, "g", &["e1"], &[("file7", "7")]); // Test the setup: look at the commit graph (commit B is duplicated and commit E // is duplicated) insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ g xznxytkn 46658cae - description: g ○ e1 kmkuslsw/1 15962bae - description: e2 │ ○ e2 kmkuslsw/0 b54f15d8 - description: e2 │ ○ f lylxulpl d50e2761 - description: f ├─╯ │ ○ b2 zsuskuln/0 2c2bd25d - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ │ ○ d znkkpsqq bf5126ef - description: d │ ○ b1 zsuskuln/1 59a77004 - description: b2 ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Pass --non-interactive to jj converge command. let output = work_dir.run_jj_with(|cmd| force_interactive(cmd).args(["converge", "--no-interactive"])); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Error: Cannot automatically choose which change to converge Hint: Run `jj converge` in interactive mode, or specify a revset that resolves to only one change ID [EOF] [exit status: 1] "); // Note: in the test environment jj commands run in non-interactive (quiet) mode // by default, so the following also fails but for a different reason: it // cannot prompt the user let output = work_dir.run_jj(["converge"]); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Choose which change to converge (jj converge only converges one change at a time): 1: zsuskulnrvyr 2: kmkuslswpqwq q: abort Error: Cannot prompt for input since the output is not connected to a terminal [EOF] [exit status: 1] "); // Note: the invocation also fails if stdin is not connected to a terminal let output = work_dir.run_jj_with(|cmd| force_interactive(cmd).args(["converge"])); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Choose which change to converge (jj converge only converges one change at a time): 1: zsuskulnrvyr 2: kmkuslswpqwq q: abort Enter the index of the change to converge: Error: Prompt canceled by EOF [EOF] [exit status: 1] "); } // This tests scenarios where there are two divergent changes. The command // prompts the user to choose which change to converge. #[test] fn test_converge_two_divergent_changes() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up: first create a base commit create_commit_with_files(&work_dir, "a", &[], &[("file1", "1")]); // Set up: create commit graph with two divergent changes // First divergent change: create_commit_with_files(&work_dir, "b2", &["a"], &[("file2", "2")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "3")]); work_dir.run_jj(["rebase", "-r", "b2", "-o", "c"]).success(); work_dir .run_jj(["bookmark", "create", "b1", "-r", "at_operation(@-, b2)"]) .success(); create_commit_with_files(&work_dir, "d", &["b1"], &[("file4", "4")]); // Second divergent change: create_commit_with_files(&work_dir, "e2", &["a"], &[("file5", "5")]); create_commit_with_files(&work_dir, "f", &["a"], &[("file6", "6")]); work_dir.run_jj(["rebase", "-r", "e2", "-o", "f"]).success(); work_dir .run_jj(["bookmark", "create", "e1", "-r", "at_operation(@-, e2)"]) .success(); create_commit_with_files(&work_dir, "g", &["e1"], &[("file7", "7")]); // Test the setup: look at the commit graph (commit B is duplicated and commit E // is duplicated) insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ g xznxytkn 46658cae - description: g ○ e1 kmkuslsw/1 15962bae - description: e2 │ ○ e2 kmkuslsw/0 b54f15d8 - description: e2 │ ○ f lylxulpl d50e2761 - description: f ├─╯ │ ○ b2 zsuskuln/0 2c2bd25d - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ │ ○ d znkkpsqq bf5126ef - description: d │ ○ b1 zsuskuln/1 59a77004 - description: b2 ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Test the setup: look at the evolog insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln/0 2c2bd25d (divergent) b2 ○ zsuskuln/1 59a77004 (divergent) b2 ○ zsuskuln/2 b2852eb2 (hidden) (empty) b2 [EOF] "); // Test the setup: look at the evolog insta::assert_snapshot!(get_evolog(&work_dir, "e2"), @r" ○ kmkuslsw/0 b54f15d8 (divergent) e2 ○ kmkuslsw/1 15962bae (divergent) e2 ○ kmkuslsw/2 843de29d (hidden) (empty) e2 [EOF] "); // If the user chooses to abort the converge operation nothing changes. let output = work_dir .run_jj_with(|cmd| force_interactive(cmd).args(["converge"]).write_stdin("q\n")) .success(); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Choose which change to converge (jj converge only converges one change at a time): 1: zsuskulnrvyr 2: kmkuslswpqwq q: abort Enter the index of the change to converge: Aborting... nothing changed. [EOF] "); // Run the command again, this time the user chooses the first divergent change. // This invocation succeeds to automatically converge that change. A hint is // printed to inform the user that there is still one divergent change // remaining. let output = work_dir .run_jj_with(|cmd| force_interactive(cmd).args(["converge"]).write_stdin("1\n")) .success(); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 2 divergent change(s) in the specified revset: - Change: zsuskulnrvyr with 2 commits: zsuskuln/0 2c2bd25d b2 | (divergent) b2 zsuskuln/1 59a77004 b1 | (divergent) b2 - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Choose which change to converge (jj converge only converges one change at a time): 1: zsuskulnrvyr 2: kmkuslswpqwq q: abort Enter the index of the change to converge: Attempting to converge change zsuskulnrvyr... Successfully converged change: created commit ba447f020ee6. Rebased 1 descendants Hint: There are still 1 divergent changes remaining in the specified revset, you can run this command again to converge another one. [EOF] "); // Verify the commit graph after converging the first divergent change insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ g xznxytkn 46658cae - description: g ○ e1 kmkuslsw/1 15962bae - description: e2 │ ○ e2 kmkuslsw/0 b54f15d8 - description: e2 │ ○ f lylxulpl d50e2761 - description: f ├─╯ │ ○ d znkkpsqq b30d892b - description: d │ ○ b1 b2 zsuskuln ba447f02 - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Verify the evolution history after converging the first divergent change insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln ba447f02 b2 ├─╮ ○ │ zsuskuln/1 2c2bd25d (hidden) b2 ├─╯ ○ zsuskuln/2 59a77004 (hidden) b2 ○ zsuskuln/3 b2852eb2 (hidden) (empty) b2 [EOF] "); // Run converge a second time to converge the other divergent change let output = work_dir.run_jj(["converge"]).success(); insta::assert_snapshot!(output, @r" ------- stderr ------- Found 1 divergent change(s) in the specified revset: - Change: kmkuslswpqwq with 2 commits: kmkuslsw/0 b54f15d8 e2 | (divergent) e2 kmkuslsw/1 15962bae e1 | (divergent) e2 Attempting to converge change kmkuslswpqwq... Successfully converged change: created commit 3f08d00a88c4. Rebased 1 descendants Working copy (@) now at: xznxytkn f9fd4e7e g | g Parent commit (@-) : kmkuslsw 3f08d00a e1 e2 | e2 Added 1 files, modified 0 files, removed 0 files [EOF] "); // Verify the commit graph after converging the second divergent change insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ g xznxytkn f9fd4e7e - description: g ○ e1 e2 kmkuslsw 3f08d00a - description: e2 ○ f lylxulpl d50e2761 - description: f │ ○ d znkkpsqq b30d892b - description: d │ ○ b1 b2 zsuskuln ba447f02 - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); // Verify the evolution history after converging the second divergent change insta::assert_snapshot!(get_evolog(&work_dir, "e2"), @r" ○ kmkuslsw 3f08d00a e2 ├─╮ ○ │ kmkuslsw/1 b54f15d8 (hidden) e2 ├─╯ ○ kmkuslsw/2 15962bae (hidden) e2 ○ kmkuslsw/3 843de29d (hidden) (empty) e2 [EOF] "); // There are no more divergent changes now let output = work_dir.run_jj(["converge"]).success(); insta::assert_snapshot!(output, @r" ------- stderr ------- No divergent changes found. [EOF] "); } // This tests scenarios where the user specifies revisions to converge. More // precisely, the user specifies a revset that is used as the search space for // divergent commits. #[test] fn test_converge_simple_with_revisions_arg() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); // Set up commit graph with divergent changes create_commit_with_files(&work_dir, "a", &[], &[("file1", "1")]); create_commit_with_files(&work_dir, "b2", &["a"], &[("file2", "2")]); create_commit_with_files(&work_dir, "c", &["a"], &[("file3", "3")]); work_dir.run_jj(["rebase", "-r", "b2", "-o", "c"]).success(); work_dir .run_jj(["bookmark", "create", "b1", "-r", "at_operation(@-, b2)"]) .success(); create_commit_with_files(&work_dir, "d", &["b1"], &[("file4", "4")]); // Test the setup (commit B is duplicated) insta::assert_snapshot!(get_long_log_output(&work_dir), @r" @ d znkkpsqq bf5126ef - description: d ○ b1 zsuskuln/1 59a77004 - description: b2 │ ○ b2 zsuskuln/0 2c2bd25d - description: b2 │ ○ c royxmykx 4343fc61 - description: c ├─╯ ○ a rlvkpnrz e9a731d9 - description: a ◆ zzzzzzzz 00000000 [EOF] "); insta::assert_snapshot!(get_evolog(&work_dir, "b2"), @r" ○ zsuskuln/0 2c2bd25d (divergent) b2 ○ zsuskuln/1 59a77004 (divergent) b2 ○ zsuskuln/2 b2852eb2 (hidden) (empty) b2 [EOF] "); // `-r a::d` resolves to {a, b1, d}. b1 IS a divergent commit, but in that // revset there are no other commits with that change-id, so by design the // command does nothing (we could change that in the future). let output = work_dir.run_jj(["converge", "-r", "a::d"]).success(); insta::assert_snapshot!(output, @r" ------- stderr -------