// Copyright 2022 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::Path; use crate::common::TestEnvironment; /// Test adding a second workspace#[test]fn test_workspaces_add_second_workspace() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); let secondary_path = test_env.env_root().join("secondary"); std::fs::write(main_path.join("file"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "initial"]); let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz 8183d0fc (empty) (no description set) "###); let (stdout, stderr) = test_env.jj_cmd_ok( &main_path, &["workspace", "add", "--name", "second", "../secondary"], ); insta::assert_snapshot!(stdout.replace('\\', "/"), @""); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../secondary" Working copy now at: rzvqmyuk 5ed2222c (empty) (no description set) Parent commit : qpvuntsm 751b12b7 initial Added 1 files, modified 0 files, removed 0 files "###); // Can see the working-copy commit in each workspace in the log output. The "@" // node in the graph indicates the current workspace's working-copy commit. insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ 5ed2222c28e2 second@ │ @ 8183d0fcaa4c default@ ├─╯ ○ 751b12b7b981 ◆ 000000000000 "###); insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###" @ 5ed2222c28e2 second@ │ ○ 8183d0fcaa4c default@ ├─╯ ○ 751b12b7b981 ◆ 000000000000 "###); // Both workspaces show up when we list them let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz 8183d0fc (empty) (no description set) second: rzvqmyuk 5ed2222c (empty) (no description set) "###);} /// Test how sparse patterns are inherited#[test]fn test_workspaces_sparse_patterns() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "ws1"]); let ws1_path = test_env.env_root().join("ws1"); let ws2_path = test_env.env_root().join("ws2"); let ws3_path = test_env.env_root().join("ws3"); let ws4_path = test_env.env_root().join("ws4"); let ws5_path = test_env.env_root().join("ws5"); let ws6_path = test_env.env_root().join("ws6"); test_env.jj_cmd_ok(&ws1_path, &["sparse", "set", "--clear", "--add=foo"]); test_env.jj_cmd_ok(&ws1_path, &["workspace", "add", "../ws2"]); let stdout = test_env.jj_cmd_success(&ws2_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @r###" foo "###); test_env.jj_cmd_ok(&ws2_path, &["sparse", "set", "--add=bar"]); test_env.jj_cmd_ok(&ws2_path, &["workspace", "add", "../ws3"]); let stdout = test_env.jj_cmd_success(&ws3_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @r###" bar foo "###); // --sparse-patterns behavior test_env.jj_cmd_ok( &ws3_path, &["workspace", "add", "--sparse-patterns=copy", "../ws4"], ); let stdout = test_env.jj_cmd_success(&ws4_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @r###" bar foo "###); test_env.jj_cmd_ok( &ws3_path, &["workspace", "add", "--sparse-patterns=full", "../ws5"], ); let stdout = test_env.jj_cmd_success(&ws5_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @r###" . "###); test_env.jj_cmd_ok( &ws3_path, &["workspace", "add", "--sparse-patterns=empty", "../ws6"], ); let stdout = test_env.jj_cmd_success(&ws6_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @"");} /// Test adding a second workspace while the current workspace is editing a/// merge#[test]fn test_workspaces_add_second_workspace_on_merge() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); test_env.jj_cmd_ok(&main_path, &["describe", "-m=left"]); test_env.jj_cmd_ok(&main_path, &["new", "@-", "-m=right"]); test_env.jj_cmd_ok(&main_path, &["new", "all:@-+", "-m=merge"]); let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: zsuskuln 35e47bff (empty) merge "###); test_env.jj_cmd_ok( &main_path, &["workspace", "add", "--name", "second", "../secondary"], ); // The new workspace's working-copy commit shares all parents with the old one. insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ 7013a493bd09 second@ ├─╮ │ │ @ 35e47bff781e default@ ╭─┬─╯ │ ○ 444b77e99d43 ○ │ 1694f2ddf8ec ├─╯ ◆ 000000000000 "###);} /// Test that --ignore-working-copy is respected#[test]fn test_workspaces_add_ignore_working_copy() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); // TODO: maybe better to error out early? let stderr = test_env.jj_cmd_failure( &main_path, &["workspace", "add", "--ignore-working-copy", "../secondary"], ); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../secondary" Error: This command must be able to update the working copy. Hint: Don't use --ignore-working-copy. "###);} /// Test that --at-op is respected#[test]fn test_workspaces_add_at_operation() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); std::fs::write(main_path.join("file1"), "").unwrap(); let (_stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["commit", "-m1"]); insta::assert_snapshot!(stderr, @r###" Working copy now at: rlvkpnrz 18d8b994 (empty) (no description set) Parent commit : qpvuntsm 3364a7ed 1 "###); std::fs::write(main_path.join("file2"), "").unwrap(); let (_stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["commit", "-m2"]); insta::assert_snapshot!(stderr, @r###" Working copy now at: kkmpptxz 2e7dc5ab (empty) (no description set) Parent commit : rlvkpnrz 0dbaa19a 2 "###); // --at-op should disable snapshot in the main workspace, but the newly // created workspace should still be writable. std::fs::write(main_path.join("file3"), "").unwrap(); let (_stdout, stderr) = test_env.jj_cmd_ok( &main_path, &["workspace", "add", "--at-op=@-", "../secondary"], ); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../secondary" Working copy now at: rzvqmyuk a4d1cbc9 (empty) (no description set) Parent commit : qpvuntsm 3364a7ed 1 Added 1 files, modified 0 files, removed 0 files "###); let secondary_path = test_env.env_root().join("secondary"); // New snapshot can be taken in the secondary workspace. std::fs::write(secondary_path.join("file4"), "").unwrap(); let (stdout, stderr) = test_env.jj_cmd_ok(&secondary_path, &["status"]); insta::assert_snapshot!(stdout, @r###" Working copy changes: A file4 Working copy : rzvqmyuk 2ba74f85 (no description set) Parent commit: qpvuntsm 3364a7ed 1 "###); insta::assert_snapshot!(stderr, @r###" Concurrent modification detected, resolving automatically. "###); let stdout = test_env.jj_cmd_success(&secondary_path, &["op", "log", "-Tdescription"]); insta::assert_snapshot!(stdout, @r#" @ snapshot working copy ○ reconcile divergent operations ├─╮ ○ │ commit cd06097124e3e5860867e35c2bb105902c28ea38 │ ○ create initial working-copy commit in workspace secondary │ ○ add workspace 'secondary' ├─╯ ○ snapshot working copy ○ commit 1c867a0762e30de4591890ea208849f793742c1b ○ snapshot working copy ○ add workspace 'default' ○ "#);} /// Test adding a workspace, but at a specific revision using '-r'#[test]fn test_workspaces_add_workspace_at_revision() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); let secondary_path = test_env.env_root().join("secondary"); std::fs::write(main_path.join("file-1"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "first"]); std::fs::write(main_path.join("file-2"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "second"]); let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: kkmpptxz dadeedb4 (empty) (no description set) "###); let (_, stderr) = test_env.jj_cmd_ok( &main_path, &[ "workspace", "add", "--name", "second", "../secondary", "-r", "@--", ], ); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../secondary" Working copy now at: zxsnswpr e374e74a (empty) (no description set) Parent commit : qpvuntsm f6097c2f first Added 1 files, modified 0 files, removed 0 files "###); // Can see the working-copy commit in each workspace in the log output. The "@" // node in the graph indicates the current workspace's working-copy commit. insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ e374e74aa0c8 second@ │ @ dadeedb493e8 default@ │ ○ c420244c6398 ├─╯ ○ f6097c2f7cac ◆ 000000000000 "###); insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###" @ e374e74aa0c8 second@ │ ○ dadeedb493e8 default@ │ ○ c420244c6398 ├─╯ ○ f6097c2f7cac ◆ 000000000000 "###);} /// Test multiple `-r` flags to `workspace add` to create a workspace/// working-copy commit with multiple parents.#[test]fn test_workspaces_add_workspace_multiple_revisions() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); std::fs::write(main_path.join("file-1"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "first"]); test_env.jj_cmd_ok(&main_path, &["new", "-r", "root()"]); std::fs::write(main_path.join("file-2"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "second"]); test_env.jj_cmd_ok(&main_path, &["new", "-r", "root()"]); std::fs::write(main_path.join("file-3"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "third"]); test_env.jj_cmd_ok(&main_path, &["new", "-r", "root()"]); insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" @ 5b36783cd11c │ ○ 6c843d62ca29 ├─╯ │ ○ 544cd61f2d26 ├─╯ │ ○ f6097c2f7cac ├─╯ ◆ 000000000000 "###); let (_, stderr) = test_env.jj_cmd_ok( &main_path, &[ "workspace", "add", "--name=merge", "../merged", "-r=description(third)", "-r=description(second)", "-r=description(first)", ], ); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../merged" Working copy now at: wmwvqwsz f4fa64f4 (empty) (no description set) Parent commit : mzvwutvl 6c843d62 third Parent commit : kkmpptxz 544cd61f second Parent commit : qpvuntsm f6097c2f first Added 3 files, modified 0 files, removed 0 files "###); insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ f4fa64f40944 merge@ ├─┬─╮ │ │ ○ f6097c2f7cac │ ○ │ 544cd61f2d26 │ ├─╯ ○ │ 6c843d62ca29 ├─╯ │ @ 5b36783cd11c default@ ├─╯ ◆ 000000000000 "###);} #[test]fn test_workspaces_add_workspace_from_subdir() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); let subdir_path = main_path.join("subdir"); let secondary_path = test_env.env_root().join("secondary"); std::fs::create_dir(&subdir_path).unwrap(); std::fs::write(subdir_path.join("file"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "initial"]); let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz e1038e77 (empty) (no description set) "###); // Create workspace while in sub-directory of current workspace let (stdout, stderr) = test_env.jj_cmd_ok(&subdir_path, &["workspace", "add", "../../secondary"]); insta::assert_snapshot!(stdout.replace('\\', "/"), @""); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../../secondary" Working copy now at: rzvqmyuk 7ad84461 (empty) (no description set) Parent commit : qpvuntsm a3a43d9e initial Added 1 files, modified 0 files, removed 0 files "###); // Both workspaces show up when we list them let stdout = test_env.jj_cmd_success(&secondary_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz e1038e77 (empty) (no description set) secondary: rzvqmyuk 7ad84461 (empty) (no description set) "###);} #[test]fn test_workspaces_add_workspace_in_current_workspace() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); std::fs::write(main_path.join("file"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "initial"]); // Try to create workspace using name instead of path let (stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["workspace", "add", "secondary"]); insta::assert_snapshot!(stdout.replace('\\', "/"), @""); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "secondary" Warning: Workspace created inside current directory. If this was unintentional, delete the "secondary" directory and run `jj workspace forget secondary` to remove it. Working copy now at: pmmvwywv 0a77a39d (empty) (no description set) Parent commit : qpvuntsm 751b12b7 initial Added 1 files, modified 0 files, removed 0 files "###); // Workspace created despite warning let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz 46d9ba8b (no description set) secondary: pmmvwywv 0a77a39d (empty) (no description set) "###); // Use explicit path instead (no warning) let (stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["workspace", "add", "./third"]); insta::assert_snapshot!(stdout.replace('\\', "/"), @""); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "third" Working copy now at: zxsnswpr 64746d4b (empty) (no description set) Parent commit : qpvuntsm 751b12b7 initial Added 1 files, modified 0 files, removed 0 files "###); // Both workspaces created let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz 477c647f (no description set) secondary: pmmvwywv 0a77a39d (empty) (no description set) third: zxsnswpr 64746d4b (empty) (no description set) "###); // Can see files from the other workspaces in main workspace, since they are // child directories and will therefore be snapshotted let stdout = test_env.jj_cmd_success(&main_path, &["file", "list"]); insta::assert_snapshot!(stdout.replace('\\', "/"), @r###" file secondary/file third/file "###);} /// Test making changes to the working copy in a workspace as it gets rewritten/// from another workspace#[test]fn test_workspaces_conflicting_edits() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); let secondary_path = test_env.env_root().join("secondary"); std::fs::write(main_path.join("file"), "contents\n").unwrap(); test_env.jj_cmd_ok(&main_path, &["new"]); test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]); insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ 3224de8ae048 secondary@ │ @ 06b57f44a3ca default@ ├─╯ ○ 506f4ec3c2c6 ◆ 000000000000 "###); // Make changes in both working copies std::fs::write(main_path.join("file"), "changed in main\n").unwrap(); std::fs::write(secondary_path.join("file"), "changed in second\n").unwrap(); // Squash the changes from the main workspace into the initial commit (before // running any command in the secondary workspace let (stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["squash"]); insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" Rebased 1 descendant commits Working copy now at: mzvwutvl a58c9a9b (empty) (no description set) Parent commit : qpvuntsm d4124476 (no description set) "###); // The secondary workspace's working-copy commit was updated insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" @ a58c9a9b19ce default@ │ ○ e82cd4ee8faa secondary@ ├─╯ ○ d41244767d45 ◆ 000000000000 "###);Lines 1–500 of 1170. Load more
// Copyright 2022 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::Path; use crate::common::TestEnvironment; /// Test adding a second workspace #[test] fn test_workspaces_add_second_workspace() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); let secondary_path = test_env.env_root().join("secondary"); std::fs::write(main_path.join("file"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "initial"]); let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz 8183d0fc (empty) (no description set) "###); let (stdout, stderr) = test_env.jj_cmd_ok( &main_path, &["workspace", "add", "--name", "second", "../secondary"], ); insta::assert_snapshot!(stdout.replace('\\', "/"), @""); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../secondary" Working copy now at: rzvqmyuk 5ed2222c (empty) (no description set) Parent commit : qpvuntsm 751b12b7 initial Added 1 files, modified 0 files, removed 0 files "###); // Can see the working-copy commit in each workspace in the log output. The "@" // node in the graph indicates the current workspace's working-copy commit. insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ 5ed2222c28e2 second@ │ @ 8183d0fcaa4c default@ ├─╯ ○ 751b12b7b981 ◆ 000000000000 "###); insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###" @ 5ed2222c28e2 second@ │ ○ 8183d0fcaa4c default@ ├─╯ ○ 751b12b7b981 ◆ 000000000000 "###); // Both workspaces show up when we list them let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz 8183d0fc (empty) (no description set) second: rzvqmyuk 5ed2222c (empty) (no description set) "###); } /// Test how sparse patterns are inherited #[test] fn test_workspaces_sparse_patterns() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "ws1"]); let ws1_path = test_env.env_root().join("ws1"); let ws2_path = test_env.env_root().join("ws2"); let ws3_path = test_env.env_root().join("ws3"); let ws4_path = test_env.env_root().join("ws4"); let ws5_path = test_env.env_root().join("ws5"); let ws6_path = test_env.env_root().join("ws6"); test_env.jj_cmd_ok(&ws1_path, &["sparse", "set", "--clear", "--add=foo"]); test_env.jj_cmd_ok(&ws1_path, &["workspace", "add", "../ws2"]); let stdout = test_env.jj_cmd_success(&ws2_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @r###" foo "###); test_env.jj_cmd_ok(&ws2_path, &["sparse", "set", "--add=bar"]); test_env.jj_cmd_ok(&ws2_path, &["workspace", "add", "../ws3"]); let stdout = test_env.jj_cmd_success(&ws3_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @r###" bar foo "###); // --sparse-patterns behavior test_env.jj_cmd_ok( &ws3_path, &["workspace", "add", "--sparse-patterns=copy", "../ws4"], ); let stdout = test_env.jj_cmd_success(&ws4_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @r###" bar foo "###); test_env.jj_cmd_ok( &ws3_path, &["workspace", "add", "--sparse-patterns=full", "../ws5"], ); let stdout = test_env.jj_cmd_success(&ws5_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @r###" . "###); test_env.jj_cmd_ok( &ws3_path, &["workspace", "add", "--sparse-patterns=empty", "../ws6"], ); let stdout = test_env.jj_cmd_success(&ws6_path, &["sparse", "list"]); insta::assert_snapshot!(stdout, @""); } /// Test adding a second workspace while the current workspace is editing a /// merge #[test] fn test_workspaces_add_second_workspace_on_merge() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); test_env.jj_cmd_ok(&main_path, &["describe", "-m=left"]); test_env.jj_cmd_ok(&main_path, &["new", "@-", "-m=right"]); test_env.jj_cmd_ok(&main_path, &["new", "all:@-+", "-m=merge"]); let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: zsuskuln 35e47bff (empty) merge "###); test_env.jj_cmd_ok( &main_path, &["workspace", "add", "--name", "second", "../secondary"], ); // The new workspace's working-copy commit shares all parents with the old one. insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ 7013a493bd09 second@ ├─╮ │ │ @ 35e47bff781e default@ ╭─┬─╯ │ ○ 444b77e99d43 ○ │ 1694f2ddf8ec ├─╯ ◆ 000000000000 "###); } /// Test that --ignore-working-copy is respected #[test] fn test_workspaces_add_ignore_working_copy() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); // TODO: maybe better to error out early? let stderr = test_env.jj_cmd_failure( &main_path, &["workspace", "add", "--ignore-working-copy", "../secondary"], ); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../secondary" Error: This command must be able to update the working copy. Hint: Don't use --ignore-working-copy. "###); } /// Test that --at-op is respected #[test] fn test_workspaces_add_at_operation() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); std::fs::write(main_path.join("file1"), "").unwrap(); let (_stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["commit", "-m1"]); insta::assert_snapshot!(stderr, @r###" Working copy now at: rlvkpnrz 18d8b994 (empty) (no description set) Parent commit : qpvuntsm 3364a7ed 1 "###); std::fs::write(main_path.join("file2"), "").unwrap(); let (_stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["commit", "-m2"]); insta::assert_snapshot!(stderr, @r###" Working copy now at: kkmpptxz 2e7dc5ab (empty) (no description set) Parent commit : rlvkpnrz 0dbaa19a 2 "###); // --at-op should disable snapshot in the main workspace, but the newly // created workspace should still be writable. std::fs::write(main_path.join("file3"), "").unwrap(); let (_stdout, stderr) = test_env.jj_cmd_ok( &main_path, &["workspace", "add", "--at-op=@-", "../secondary"], ); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../secondary" Working copy now at: rzvqmyuk a4d1cbc9 (empty) (no description set) Parent commit : qpvuntsm 3364a7ed 1 Added 1 files, modified 0 files, removed 0 files "###); let secondary_path = test_env.env_root().join("secondary"); // New snapshot can be taken in the secondary workspace. std::fs::write(secondary_path.join("file4"), "").unwrap(); let (stdout, stderr) = test_env.jj_cmd_ok(&secondary_path, &["status"]); insta::assert_snapshot!(stdout, @r###" Working copy changes: A file4 Working copy : rzvqmyuk 2ba74f85 (no description set) Parent commit: qpvuntsm 3364a7ed 1 "###); insta::assert_snapshot!(stderr, @r###" Concurrent modification detected, resolving automatically. "###); let stdout = test_env.jj_cmd_success(&secondary_path, &["op", "log", "-Tdescription"]); insta::assert_snapshot!(stdout, @r#" @ snapshot working copy ○ reconcile divergent operations ├─╮ ○ │ commit cd06097124e3e5860867e35c2bb105902c28ea38 │ ○ create initial working-copy commit in workspace secondary │ ○ add workspace 'secondary' ├─╯ ○ snapshot working copy ○ commit 1c867a0762e30de4591890ea208849f793742c1b ○ snapshot working copy ○ add workspace 'default' ○ "#); } /// Test adding a workspace, but at a specific revision using '-r' #[test] fn test_workspaces_add_workspace_at_revision() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); let secondary_path = test_env.env_root().join("secondary"); std::fs::write(main_path.join("file-1"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "first"]); std::fs::write(main_path.join("file-2"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "second"]); let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: kkmpptxz dadeedb4 (empty) (no description set) "###); let (_, stderr) = test_env.jj_cmd_ok( &main_path, &[ "workspace", "add", "--name", "second", "../secondary", "-r", "@--", ], ); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../secondary" Working copy now at: zxsnswpr e374e74a (empty) (no description set) Parent commit : qpvuntsm f6097c2f first Added 1 files, modified 0 files, removed 0 files "###); // Can see the working-copy commit in each workspace in the log output. The "@" // node in the graph indicates the current workspace's working-copy commit. insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ e374e74aa0c8 second@ │ @ dadeedb493e8 default@ │ ○ c420244c6398 ├─╯ ○ f6097c2f7cac ◆ 000000000000 "###); insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###" @ e374e74aa0c8 second@ │ ○ dadeedb493e8 default@ │ ○ c420244c6398 ├─╯ ○ f6097c2f7cac ◆ 000000000000 "###); } /// Test multiple `-r` flags to `workspace add` to create a workspace /// working-copy commit with multiple parents. #[test] fn test_workspaces_add_workspace_multiple_revisions() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); std::fs::write(main_path.join("file-1"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "first"]); test_env.jj_cmd_ok(&main_path, &["new", "-r", "root()"]); std::fs::write(main_path.join("file-2"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "second"]); test_env.jj_cmd_ok(&main_path, &["new", "-r", "root()"]); std::fs::write(main_path.join("file-3"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "third"]); test_env.jj_cmd_ok(&main_path, &["new", "-r", "root()"]); insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" @ 5b36783cd11c │ ○ 6c843d62ca29 ├─╯ │ ○ 544cd61f2d26 ├─╯ │ ○ f6097c2f7cac ├─╯ ◆ 000000000000 "###); let (_, stderr) = test_env.jj_cmd_ok( &main_path, &[ "workspace", "add", "--name=merge", "../merged", "-r=description(third)", "-r=description(second)", "-r=description(first)", ], ); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../merged" Working copy now at: wmwvqwsz f4fa64f4 (empty) (no description set) Parent commit : mzvwutvl 6c843d62 third Parent commit : kkmpptxz 544cd61f second Parent commit : qpvuntsm f6097c2f first Added 3 files, modified 0 files, removed 0 files "###); insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ f4fa64f40944 merge@ ├─┬─╮ │ │ ○ f6097c2f7cac │ ○ │ 544cd61f2d26 │ ├─╯ ○ │ 6c843d62ca29 ├─╯ │ @ 5b36783cd11c default@ ├─╯ ◆ 000000000000 "###); } #[test] fn test_workspaces_add_workspace_from_subdir() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); let subdir_path = main_path.join("subdir"); let secondary_path = test_env.env_root().join("secondary"); std::fs::create_dir(&subdir_path).unwrap(); std::fs::write(subdir_path.join("file"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "initial"]); let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz e1038e77 (empty) (no description set) "###); // Create workspace while in sub-directory of current workspace let (stdout, stderr) = test_env.jj_cmd_ok(&subdir_path, &["workspace", "add", "../../secondary"]); insta::assert_snapshot!(stdout.replace('\\', "/"), @""); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "../../secondary" Working copy now at: rzvqmyuk 7ad84461 (empty) (no description set) Parent commit : qpvuntsm a3a43d9e initial Added 1 files, modified 0 files, removed 0 files "###); // Both workspaces show up when we list them let stdout = test_env.jj_cmd_success(&secondary_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz e1038e77 (empty) (no description set) secondary: rzvqmyuk 7ad84461 (empty) (no description set) "###); } #[test] fn test_workspaces_add_workspace_in_current_workspace() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); std::fs::write(main_path.join("file"), "contents").unwrap(); test_env.jj_cmd_ok(&main_path, &["commit", "-m", "initial"]); // Try to create workspace using name instead of path let (stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["workspace", "add", "secondary"]); insta::assert_snapshot!(stdout.replace('\\', "/"), @""); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "secondary" Warning: Workspace created inside current directory. If this was unintentional, delete the "secondary" directory and run `jj workspace forget secondary` to remove it. Working copy now at: pmmvwywv 0a77a39d (empty) (no description set) Parent commit : qpvuntsm 751b12b7 initial Added 1 files, modified 0 files, removed 0 files "###); // Workspace created despite warning let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz 46d9ba8b (no description set) secondary: pmmvwywv 0a77a39d (empty) (no description set) "###); // Use explicit path instead (no warning) let (stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["workspace", "add", "./third"]); insta::assert_snapshot!(stdout.replace('\\', "/"), @""); insta::assert_snapshot!(stderr.replace('\\', "/"), @r###" Created workspace in "third" Working copy now at: zxsnswpr 64746d4b (empty) (no description set) Parent commit : qpvuntsm 751b12b7 initial Added 1 files, modified 0 files, removed 0 files "###); // Both workspaces created let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]); insta::assert_snapshot!(stdout, @r###" default: rlvkpnrz 477c647f (no description set) secondary: pmmvwywv 0a77a39d (empty) (no description set) third: zxsnswpr 64746d4b (empty) (no description set) "###); // Can see files from the other workspaces in main workspace, since they are // child directories and will therefore be snapshotted let stdout = test_env.jj_cmd_success(&main_path, &["file", "list"]); insta::assert_snapshot!(stdout.replace('\\', "/"), @r###" file secondary/file third/file "###); } /// Test making changes to the working copy in a workspace as it gets rewritten /// from another workspace #[test] fn test_workspaces_conflicting_edits() { let test_env = TestEnvironment::default(); test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]); let main_path = test_env.env_root().join("main"); let secondary_path = test_env.env_root().join("secondary"); std::fs::write(main_path.join("file"), "contents\n").unwrap(); test_env.jj_cmd_ok(&main_path, &["new"]); test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]); insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" ○ 3224de8ae048 secondary@ │ @ 06b57f44a3ca default@ ├─╯ ○ 506f4ec3c2c6 ◆ 000000000000 "###); // Make changes in both working copies std::fs::write(main_path.join("file"), "changed in main\n").unwrap(); std::fs::write(secondary_path.join("file"), "changed in second\n").unwrap(); // Squash the changes from the main workspace into the initial commit (before // running any command in the secondary workspace let (stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["squash"]); insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" Rebased 1 descendant commits Working copy now at: mzvwutvl a58c9a9b (empty) (no description set) Parent commit : qpvuntsm d4124476 (no description set) "###); // The secondary workspace's working-copy commit was updated insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" @ a58c9a9b19ce default@ │ ○ e82cd4ee8faa secondary@ ├─╯ ○ d41244767d45 ◆ 000000000000 "###);