// Copyright 2025 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 std::path::PathBuf; use crate::common::TestEnvironment; /// Integrating an already integrated operation is a no-op #[test] fn test_integrate_integrated_operation() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); let output = work_dir.run_jj(["op", "integrate", "@"]); insta::assert_snapshot!(output, @""); let output = work_dir.run_jj(["op", "log"]); insta::assert_snapshot!(output, @" @ 8f47435a3990 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' ○ 000000000000 root() [EOF] "); } #[test] fn test_integrate_sibling_operation() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); let base_op_id = work_dir.current_operation_id(); work_dir.run_jj(["new", "-m=first"]).success(); let unintegrated_id = work_dir.current_operation_id(); assert_ne!(unintegrated_id, base_op_id); // Manually remove the last operation from the operation log let heads_dir = work_dir .root() .join(PathBuf::from_iter([".jj", "repo", "op_heads", "heads"])); std::fs::rename( heads_dir.join(&unintegrated_id), heads_dir.join(&base_op_id), ) .unwrap(); // We use --ignore-working-copy to prevent the automatic reloading of the repo // at the unintegrated operation that's mentioned in // `.jj/working_copy/checkout`. let output = work_dir.run_jj(["new", "-m=second", "--ignore-working-copy"]); insta::assert_snapshot!(output, @""); // The working copy should now be at the old unintegrated sibling operation let output = work_dir.run_jj(["op", "log"]); insta::assert_snapshot!(output, @" ------- stderr ------- Internal error: The repo was loaded at operation 5959e60d9534, which seems to be a sibling of the working copy's operation 98a299ea1b9b Hint: Run `jj op integrate 98a299ea1b9b` to add the working copy's operation to the operation log. [EOF] [exit status: 255] "); // Integrate the operation let output = work_dir.run_jj(["op", "integrate", &unintegrated_id]); insta::assert_snapshot!(output, @" ------- stderr ------- The specified operation has been integrated with other existing operations. [EOF] "); let output = work_dir.run_jj(["op", "log"]); insta::assert_snapshot!(output, @" @ 5fff7495e1c0 test-username@host.example.com 2001-02-03 04:05:11.000 +07:00 - 2001-02-03 04:05:11.000 +07:00 ├─╮ reconcile divergent operations │ │ args: jj op integrate 98a299ea1b9bd7555bec90a7abf34b877f1ad2ec45e5c0a4962115b5ac1124124524b2935fdf149cdc6634524ce54683479cc978624f84d84270f42264fe0ef9 ○ │ 98a299ea1b9b test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 │ │ new empty commit │ │ args: jj new '-m=first' │ ○ 5959e60d9534 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00 ├─╯ new empty commit │ args: jj new '-m=second' --ignore-working-copy ○ 8f47435a3990 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' ○ 000000000000 root() [EOF] "); } #[test] fn test_integrate_rebase_descendants() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); work_dir .run_jj(["new", "--no-edit", "-m=child 1"]) .success(); let base_op_id = work_dir.current_operation_id(); work_dir.run_jj(["new", "-m=child 2"]).success(); let unintegrated_id = work_dir.current_operation_id(); assert_ne!(unintegrated_id, base_op_id); // Manually remove the last operation from the operation log let heads_dir = work_dir .root() .join(PathBuf::from_iter([".jj", "repo", "op_heads", "heads"])); std::fs::rename( heads_dir.join(&unintegrated_id), heads_dir.join(&base_op_id), ) .unwrap(); // We use --ignore-working-copy to prevent the automatic reloading of the repo // at the unintegrated operation that's mentioned in // `.jj/working_copy/checkout`. let output = work_dir.run_jj(["describe", "-m=parent", "--ignore-working-copy"]); insta::assert_snapshot!(output, @" ------- stderr ------- Rebased 1 descendant commits [EOF] "); // The working copy should now be at the old unintegrated sibling operation let output = work_dir.run_jj(["op", "log"]); insta::assert_snapshot!(output, @" ------- stderr ------- Internal error: The repo was loaded at operation 257b4e206712, which seems to be a sibling of the working copy's operation d3f34f652525 Hint: Run `jj op integrate d3f34f652525` to add the working copy's operation to the operation log. [EOF] [exit status: 255] "); // Integrate the operation let output = work_dir.run_jj(["op", "integrate", &unintegrated_id]); insta::assert_snapshot!(output, @" ------- stderr ------- Rebased 1 descendant commits onto commits rewritten by other operation The specified operation has been integrated with other existing operations. [EOF] "); let output = work_dir.run_jj(["op", "log"]); insta::assert_snapshot!(output, @" @ 3fe3cb32dee2 test-username@host.example.com 2001-02-03 04:05:12.000 +07:00 - 2001-02-03 04:05:12.000 +07:00 ├─╮ reconcile divergent operations │ │ args: jj op integrate d3f34f65252510f8e5c0cde929355401acd24be8498869ec70296063a464fd16a1adb9474e4c208a82adaa4316455645808c7ad980239720dfd16a2860e761d8 ○ │ d3f34f652525 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00 │ │ new empty commit │ │ args: jj new '-m=child 2' │ ○ 257b4e206712 test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00 ├─╯ describe commit e8849ae12c709f2321908879bc724fdb2ab8a781 │ args: jj describe '-m=parent' --ignore-working-copy ○ e4002698050b test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 │ new empty commit │ args: jj new --no-edit '-m=child 1' ○ 8f47435a3990 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' ○ 000000000000 root() [EOF] "); // Child 2 was successfully rebased let output = work_dir.run_jj(["log"]); insta::assert_snapshot!(output, @" @ kkmpptxz test.user@example.com 2001-02-03 08:05:12 9780be6d │ (empty) child 2 │ ○ rlvkpnrz test.user@example.com 2001-02-03 08:05:10 ce1fb6c9 ├─╯ (empty) child 1 ○ qpvuntsm test.user@example.com 2001-02-03 08:05:10 5f8729eb │ (empty) parent ◆ zzzzzzzz root() 00000000 [EOF] "); } #[test] fn test_integrate_concurrent_operations() { let test_env = TestEnvironment::default(); test_env.run_jj_in(".", ["git", "init", "repo"]).success(); let work_dir = test_env.work_dir("repo"); let base_op_id = work_dir.current_operation_id(); work_dir.run_jj(["describe", "-m=left"]).success(); let unintegrated_id = work_dir.current_operation_id(); assert_ne!(unintegrated_id, base_op_id); // Manually remove the last operation from the operation log let heads_dir = work_dir .root() .join(PathBuf::from_iter([".jj", "repo", "op_heads", "heads"])); std::fs::rename( heads_dir.join(&unintegrated_id), heads_dir.join(&base_op_id), ) .unwrap(); // We use --ignore-working-copy to prevent the automatic reloading of the repo // at the unintegrated operation that's mentioned in // `.jj/working_copy/checkout`. let output = work_dir.run_jj(["describe", "-m=right", "--ignore-working-copy"]); insta::assert_snapshot!(output, @""); // The working copy should now be at the old unintegrated sibling operation let output = work_dir.run_jj(["op", "log"]); insta::assert_snapshot!(output, @" ------- stderr ------- Internal error: The repo was loaded at operation 8975ceb25594, which seems to be a sibling of the working copy's operation c22efcff0067 Hint: Run `jj op integrate c22efcff0067` to add the working copy's operation to the operation log. [EOF] [exit status: 255] "); // Integrate the operation let output = work_dir.run_jj(["op", "integrate", &unintegrated_id]); insta::assert_snapshot!(output, @" ------- stderr ------- The specified operation has been integrated with other existing operations. [EOF] "); let output = work_dir.run_jj(["op", "log"]); insta::assert_snapshot!(output, @" @ 12fbf26d0f0b test-username@host.example.com 2001-02-03 04:05:11.000 +07:00 - 2001-02-03 04:05:11.000 +07:00 ├─╮ reconcile divergent operations │ │ args: jj op integrate c22efcff00672e0f82ca4a19b9b37c4910dcfc5a5ab017312720438121a4ef1d4de1dd5608bbd3044c309f6edf388cf08377fcfdab23e765e8a14eb896e85209 ○ │ c22efcff0067 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 │ │ describe commit e8849ae12c709f2321908879bc724fdb2ab8a781 │ │ args: jj describe '-m=left' │ ○ 8975ceb25594 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00 ├─╯ describe commit e8849ae12c709f2321908879bc724fdb2ab8a781 │ args: jj describe '-m=right' --ignore-working-copy ○ 8f47435a3990 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' ○ 000000000000 root() [EOF] "); // Produces divergence equivalent to concurrent `jj describe` let output = work_dir.run_jj(["log"]); insta::assert_snapshot!(output, @" @ qpvuntsm/1 test.user@example.com 2001-02-03 08:05:08 3c52528f (divergent) │ (empty) left │ ○ qpvuntsm/0 test.user@example.com 2001-02-03 08:05:09 fc350e9c (divergent) ├─╯ (empty) right ◆ zzzzzzzz root() 00000000 [EOF] "); }