use test_case::test_case;
use testutils::TestResult;
use testutils::git;
use crate::common::CommandOutput;
use crate::common::TestEnvironment;
use crate::common::TestWorkDir;
#[test]
fn test_workspaces_invalid_name() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let main_dir = test_env.work_dir("repo");
let output = main_dir.run_jj(["workspace", "add", "--name", "", "../secondary"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: New workspace name cannot be empty
[EOF]
[exit status: 1]
");
assert!(!test_env.env_root().join("secondary").exists());
let output = main_dir.run_jj(["workspace", "rename", ""]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: New workspace name cannot be empty
[EOF]
[exit status: 1]
");
}
#[test]
fn test_workspaces_add_second_and_third_workspace() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
main_dir.write_file("file", "contents");
main_dir.run_jj(["commit", "-m", "initial"]).success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 504e3d8c (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["workspace", "add", "--name", "second", "../secondary"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../secondary"
Working copy (@) now at: rzvqmyuk bcc858e1 (empty) (no description set)
Parent commit (@-) : qpvuntsm 7b22a8cb initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
@ 504e3d8c1bcd default@
│ ○ bcc858e1d93f second@
├─╯
○ 7b22a8cbe888 "initial"
◆ 000000000000
[EOF]
"#);
insta::assert_snapshot!(get_log_output(&secondary_dir), @r#"
@ bcc858e1d93f second@
│ ○ 504e3d8c1bcd default@
├─╯
○ 7b22a8cbe888 "initial"
◆ 000000000000
[EOF]
"#);
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 504e3d8c (empty) (no description set)
second: rzvqmyuk bcc858e1 (empty) (no description set)
[EOF]
");
main_dir.create_dir("../third");
let output = main_dir.run_jj(["workspace", "add", "--name", "third", "../third"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../third"
Working copy (@) now at: nuwvvtmy d55e769c (empty) (no description set)
Parent commit (@-) : qpvuntsm 7b22a8cb initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
let output = main_dir.run_jj(["workspace", "add", "--name", "third", "../tertiary"]);
insta::assert_snapshot!(output.normalize_backslash(), @"
------- stderr -------
Error: Workspace named 'third' already exists
[EOF]
[exit status: 1]
");
assert!(!test_env.env_root().join("tertiary").exists());
}
#[test]
fn test_workspaces_add_with_message() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.write_file("file", "contents");
main_dir.run_jj(["commit", "-m", "initial"]).success();
let output = main_dir.run_jj([
"workspace",
"add",
"--name",
"second",
"-m",
"add second workspace",
"../secondary",
]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../secondary"
Working copy (@) now at: pmmvwywv 47a2d9a1 (empty) add second workspace
Parent commit (@-) : qpvuntsm 7b22a8cb initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
main_dir
.run_jj([
"workspace",
"add",
"--name",
"third",
"-m",
"first message",
"-m",
"second message",
"../tertiary",
])
.success();
let output = main_dir.run_jj(["log", "-r", "third@", "-Tdescription", "--no-graph"]);
insta::assert_snapshot!(output.normalize_backslash(), @"
first message
second message
[EOF]
");
test_env.add_config(
r#"[templates]
commit_trailers = '"Signed-off-by: " ++ committer.email()'
"#,
);
let output = main_dir.run_jj(["workspace", "add", "--name", "fourth", "../quaternary"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../quaternary"
Working copy (@) now at: nppvrztz 0bfa7004 (empty) (no description set)
Parent commit (@-) : qpvuntsm 7b22a8cb initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
}
#[test]
fn test_workspaces_sparse_patterns() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "ws1"]).success();
let ws1_dir = test_env.work_dir("ws1");
let ws2_dir = test_env.work_dir("ws2");
let ws3_dir = test_env.work_dir("ws3");
let ws4_dir = test_env.work_dir("ws4");
let ws5_dir = test_env.work_dir("ws5");
let ws6_dir = test_env.work_dir("ws6");
ws1_dir
.run_jj(["sparse", "set", "--clear", "--add=foo"])
.success();
ws1_dir.run_jj(["workspace", "add", "../ws2"]).success();
let output = ws2_dir.run_jj(["sparse", "list"]);
insta::assert_snapshot!(output, @"
foo
[EOF]
");
ws2_dir.run_jj(["sparse", "set", "--add=bar"]).success();
ws2_dir.run_jj(["workspace", "add", "../ws3"]).success();
let output = ws3_dir.run_jj(["sparse", "list"]);
insta::assert_snapshot!(output, @"
bar
foo
[EOF]
");
ws3_dir
.run_jj(["workspace", "add", "--sparse-patterns=copy", "../ws4"])
.success();
let output = ws4_dir.run_jj(["sparse", "list"]);
insta::assert_snapshot!(output, @"
bar
foo
[EOF]
");
ws3_dir
.run_jj(["workspace", "add", "--sparse-patterns=full", "../ws5"])
.success();
let output = ws5_dir.run_jj(["sparse", "list"]);
insta::assert_snapshot!(output, @"
.
[EOF]
");
ws3_dir
.run_jj(["workspace", "add", "--sparse-patterns=empty", "../ws6"])
.success();
let output = ws6_dir.run_jj(["sparse", "list"]);
insta::assert_snapshot!(output, @"");
}
#[test]
fn test_workspaces_add_second_workspace_on_merge() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.run_jj(["describe", "-m=left"]).success();
main_dir.run_jj(["new", "@-", "-m=right"]).success();
main_dir.run_jj(["new", "@-+", "-m=merge"]).success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: zsuskuln 46ed31b6 (empty) merge
[EOF]
");
main_dir
.run_jj(["workspace", "add", "--name", "second", "../secondary"])
.success();
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
@ 46ed31b61ce9 default@ "merge"
├─╮
│ │ ○ d23b2d4ff55c second@
╭─┬─╯
│ ○ 3c52528f5893 "left"
○ │ a3155ab1bf5a "right"
├─╯
◆ 000000000000
[EOF]
"#);
}
#[test]
fn test_workspaces_add_ignore_working_copy() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let output = main_dir.run_jj(["workspace", "add", "--ignore-working-copy", "../secondary"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../secondary"
Error: This command must be able to update the working copy.
Hint: Don't use --ignore-working-copy.
[EOF]
[exit status: 1]
"#);
}
#[test]
fn test_workspaces_add_no_integrate_operation() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let output = main_dir.run_jj([
"workspace",
"add",
"--no-integrate-operation",
"../secondary",
]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../secondary"
Error: This command must be able to update the working copy.
Hint: Don't use --no-integrate-operation.
[EOF]
[exit status: 1]
"#);
}
#[test]
fn test_workspaces_add_at_operation() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.write_file("file1", "");
let output = main_dir.run_jj(["commit", "-m1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Working copy (@) now at: rlvkpnrz 59e07459 (empty) (no description set)
Parent commit (@-) : qpvuntsm 9e4b0b91 1
[EOF]
");
main_dir.write_file("file2", "");
let output = main_dir.run_jj(["commit", "-m2"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Working copy (@) now at: kkmpptxz 6e9610ac (empty) (no description set)
Parent commit (@-) : rlvkpnrz 8b7259b9 2
[EOF]
");
main_dir.write_file("file3", "");
let output = main_dir.run_jj(["workspace", "add", "--at-op=@-", "../secondary"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../secondary"
Working copy (@) now at: rzvqmyuk b8772476 (empty) (no description set)
Parent commit (@-) : qpvuntsm 9e4b0b91 1
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
let secondary_dir = test_env.work_dir("secondary");
secondary_dir.write_file("file4", "");
let output = secondary_dir.run_jj(["status"]);
insta::assert_snapshot!(output, @"
Working copy changes:
A file4
Working copy (@) : rzvqmyuk f2ff8257 (no description set)
Parent commit (@-): qpvuntsm 9e4b0b91 1
[EOF]
------- stderr -------
Concurrent modification detected, resolving automatically.
[EOF]
");
let output = secondary_dir.run_jj(["op", "log", "-Tdescription"]);
insta::assert_snapshot!(output, @"
@ snapshot working copy
○ reconcile divergent operations
├─╮
○ │ commit 9152e822279787a168ddf4cede6440a21faa00d7
│ ○ create initial working-copy commit in workspace secondary
│ ○ add workspace 'secondary'
├─╯
○ snapshot working copy
○ commit 093c3c9624b6cfe22b310586f5638792aa80e6d7
○ snapshot working copy
○ add workspace 'default'
○
[EOF]
");
}
#[test]
fn test_workspaces_add_workspace_at_revision() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
main_dir.write_file("file-1", "contents");
main_dir.run_jj(["commit", "-m", "first"]).success();
main_dir.write_file("file-2", "contents");
main_dir.run_jj(["commit", "-m", "second"]).success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: kkmpptxz 5ac9178d (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj([
"workspace",
"add",
"--name",
"second",
"../secondary",
"-r",
"@--",
]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../secondary"
Working copy (@) now at: zxsnswpr ea5860fb (empty) (no description set)
Parent commit (@-) : qpvuntsm 27473635 first
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
@ 5ac9178da8b2 default@
○ a47d8a593529 "second"
│ ○ ea5860fbd622 second@
├─╯
○ 27473635a942 "first"
◆ 000000000000
[EOF]
"#);
insta::assert_snapshot!(get_log_output(&secondary_dir), @r#"
@ ea5860fbd622 second@
│ ○ 5ac9178da8b2 default@
│ ○ a47d8a593529 "second"
├─╯
○ 27473635a942 "first"
◆ 000000000000
[EOF]
"#);
}
#[test]
fn test_workspaces_add_workspace_multiple_revisions() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.write_file("file-1", "contents");
main_dir.run_jj(["commit", "-m", "first"]).success();
main_dir.run_jj(["new", "-r", "root()"]).success();
main_dir.write_file("file-2", "contents");
main_dir.run_jj(["commit", "-m", "second"]).success();
main_dir.run_jj(["new", "-r", "root()"]).success();
main_dir.write_file("file-3", "contents");
main_dir.run_jj(["commit", "-m", "third"]).success();
main_dir.run_jj(["new", "-r", "root()"]).success();
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
@ 8d23abddc924
│ ○ eba7f49e2358 "third"
├─╯
│ ○ 62444a45efcf "second"
├─╯
│ ○ 27473635a942 "first"
├─╯
◆ 000000000000
[EOF]
"#);
let output = main_dir.run_jj([
"workspace",
"add",
"--name=merge",
"../merged",
"-r=subject(third)",
"-r=subject(second)",
"-r=subject(first)",
]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../merged"
Working copy (@) now at: wmwvqwsz 2d7c9a2d (empty) (no description set)
Parent commit (@-) : mzvwutvl eba7f49e third
Parent commit (@-) : kkmpptxz 62444a45 second
Parent commit (@-) : qpvuntsm 27473635 first
Added 3 files, modified 0 files, removed 0 files
[EOF]
"#);
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
@ 8d23abddc924 default@
│ ○ 2d7c9a2d41dc merge@
│ ├─┬─╮
│ │ │ ○ 27473635a942 "first"
├─────╯
│ │ ○ 62444a45efcf "second"
├───╯
│ ○ eba7f49e2358 "third"
├─╯
◆ 000000000000
[EOF]
"#);
}
#[test]
fn test_workspaces_add_workspace_from_subdir() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
let subdir_dir = main_dir.create_dir("subdir");
subdir_dir.write_file("file", "contents");
main_dir.run_jj(["commit", "-m", "initial"]).success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 0ba0ff35 (empty) (no description set)
[EOF]
");
let output = subdir_dir.run_jj(["workspace", "add", "../../secondary"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../../secondary"
Working copy (@) now at: rzvqmyuk dea1be10 (empty) (no description set)
Parent commit (@-) : qpvuntsm 80b67806 initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
let output = secondary_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 0ba0ff35 (empty) (no description set)
secondary: rzvqmyuk dea1be10 (empty) (no description set)
[EOF]
");
}
#[test]
fn test_workspaces_add_workspace_in_current_workspace() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.write_file("file", "contents");
main_dir.run_jj(["commit", "-m", "initial"]).success();
let output = main_dir.run_jj(["workspace", "add", "secondary"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
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 058f604d (empty) (no description set)
Parent commit (@-) : qpvuntsm 7b22a8cb initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 504e3d8c (empty) (no description set)
secondary: pmmvwywv 058f604d (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["workspace", "add", "./third"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "third"
Working copy (@) now at: zxsnswpr 1c1effec (empty) (no description set)
Parent commit (@-) : qpvuntsm 7b22a8cb initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 504e3d8c (empty) (no description set)
secondary: pmmvwywv 058f604d (empty) (no description set)
third: zxsnswpr 1c1effec (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["file", "list"]);
insta::assert_snapshot!(output.normalize_backslash(), @"
file
[EOF]
");
}
#[test]
fn test_workspace_add_override_path_in_store() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.write_file("file", "contents");
main_dir.run_jj(["commit", "-m", "initial"]).success();
let output = main_dir.run_jj(["workspace", "add", "--name", "second", "../secondary"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../secondary"
Working copy (@) now at: pmmvwywv 058f604d (empty) (no description set)
Parent commit (@-) : qpvuntsm 7b22a8cb initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 504e3d8c (empty) (no description set)
second: pmmvwywv 058f604d (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["operation", "restore", "@--"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Restored to operation: 98ef745836d5 (2001-02-03 08:05:08) commit 006bd1130b84e90ab082adeabd7409270d5a86da
[EOF]
");
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 504e3d8c (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["workspace", "add", "--name", "second", "../tertiary"]);
insta::assert_snapshot!(output.normalize_backslash(), @r#"
------- stderr -------
Created workspace in "../tertiary"
Working copy (@) now at: spxsnpux 96ef6c50 (empty) (no description set)
Parent commit (@-) : qpvuntsm 7b22a8cb initial
Added 1 files, modified 0 files, removed 0 files
[EOF]
"#);
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz 504e3d8c (empty) (no description set)
second: spxsnpux 96ef6c50 (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["workspace", "root", "--name", "second"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/tertiary
[EOF]
");
}
#[test]
fn test_workspaces_conflicting_edits() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
main_dir.write_file("file", "contents\n");
main_dir.run_jj(["new"]).success();
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ 393250c59e39 default@
│ ○ 547036666102 secondary@
├─╯
○ 9a462e35578a
◆ 000000000000
[EOF]
");
main_dir.write_file("file", "changed in main\n");
secondary_dir.write_file("file", "changed in second\n");
let output = main_dir.run_jj(["squash"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Rebased 1 descendant commits
Working copy (@) now at: mzvwutvl 3a9b690d (empty) (no description set)
Parent commit (@-) : qpvuntsm b853f7c8 (no description set)
[EOF]
");
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ 3a9b690d6e67 default@
│ ○ 90f3d42e0bff secondary@
├─╯
○ b853f7c8b006
◆ 000000000000
[EOF]
");
let output = secondary_dir.run_jj(["st"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: The working copy is stale (not updated since operation 58f8ef773e05).
Hint: Run `jj workspace update-stale` to update it.
See https://docs.jj-vcs.dev/latest/working-copy/#stale-working-copy for more information.
[EOF]
[exit status: 1]
");
let output = secondary_dir.run_jj(["log"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: The working copy is stale (not updated since operation 58f8ef773e05).
Hint: Run `jj workspace update-stale` to update it.
See https://docs.jj-vcs.dev/latest/working-copy/#stale-working-copy for more information.
[EOF]
[exit status: 1]
");
let output = secondary_dir.run_jj(["workspace", "update-stale"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Concurrent modification detected, resolving automatically.
Rebased 1 descendant commits onto commits rewritten by other operation
Working copy (@) now at: pmmvwywv/2 90f3d42e (divergent) (empty) (no description set)
Parent commit (@-) : qpvuntsm b853f7c8 (no description set)
Added 0 files, modified 1 files, removed 0 files
Updated working copy to fresh commit 90f3d42e0bff
[EOF]
");
insta::assert_snapshot!(get_log_output(&secondary_dir),
@"
@ 90f3d42e0bff secondary@ (divergent)
│ × 8823f4273170 (divergent)
├─╯
│ ○ 3a9b690d6e67 default@
├─╯
○ b853f7c8b006
◆ 000000000000
[EOF]
");
insta::assert_snapshot!(get_log_output(&secondary_dir), @"
@ 90f3d42e0bff secondary@ (divergent)
│ × 8823f4273170 (divergent)
├─╯
│ ○ 3a9b690d6e67 default@
├─╯
○ b853f7c8b006
◆ 000000000000
[EOF]
");
}
#[test]
fn test_workspaces_updated_by_other() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
main_dir.write_file("file", "contents\n");
main_dir.run_jj(["new"]).success();
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ 393250c59e39 default@
│ ○ 547036666102 secondary@
├─╯
○ 9a462e35578a
◆ 000000000000
[EOF]
");
main_dir.write_file("file", "changed in main\n");
let output = main_dir.run_jj(["squash"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Rebased 1 descendant commits
Working copy (@) now at: mzvwutvl 3a9b690d (empty) (no description set)
Parent commit (@-) : qpvuntsm b853f7c8 (no description set)
[EOF]
");
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ 3a9b690d6e67 default@
│ ○ 90f3d42e0bff secondary@
├─╯
○ b853f7c8b006
◆ 000000000000
[EOF]
");
let output = secondary_dir.run_jj(["st"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: The working copy is stale (not updated since operation 58f8ef773e05).
Hint: Run `jj workspace update-stale` to update it.
See https://docs.jj-vcs.dev/latest/working-copy/#stale-working-copy for more information.
[EOF]
[exit status: 1]
");
let output = secondary_dir.run_jj(["workspace", "update-stale"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Working copy (@) now at: pmmvwywv 90f3d42e (empty) (no description set)
Parent commit (@-) : qpvuntsm b853f7c8 (no description set)
Added 0 files, modified 1 files, removed 0 files
Updated working copy to fresh commit 90f3d42e0bff
[EOF]
");
insta::assert_snapshot!(get_log_output(&secondary_dir),
@"
@ 90f3d42e0bff secondary@
│ ○ 3a9b690d6e67 default@
├─╯
○ b853f7c8b006
◆ 000000000000
[EOF]
");
}
#[test]
fn test_workspaces_updated_by_other_automatic() {
let test_env = TestEnvironment::default();
test_env.add_config("snapshot.auto-update-stale = true\n");
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
main_dir.write_file("file", "contents\n");
main_dir.run_jj(["new"]).success();
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ 393250c59e39 default@
│ ○ 547036666102 secondary@
├─╯
○ 9a462e35578a
◆ 000000000000
[EOF]
");
main_dir.write_file("file", "changed in main\n");
let output = main_dir.run_jj(["squash"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Rebased 1 descendant commits
Working copy (@) now at: mzvwutvl 3a9b690d (empty) (no description set)
Parent commit (@-) : qpvuntsm b853f7c8 (no description set)
[EOF]
");
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ 3a9b690d6e67 default@
│ ○ 90f3d42e0bff secondary@
├─╯
○ b853f7c8b006
◆ 000000000000
[EOF]
");
let output = secondary_dir.run_jj(["st"]);
insta::assert_snapshot!(output, @"
The working copy has no changes.
Working copy (@) : pmmvwywv 90f3d42e (empty) (no description set)
Parent commit (@-): qpvuntsm b853f7c8 (no description set)
[EOF]
------- stderr -------
Working copy (@) now at: pmmvwywv 90f3d42e (empty) (no description set)
Parent commit (@-) : qpvuntsm b853f7c8 (no description set)
Added 0 files, modified 1 files, removed 0 files
Updated working copy to fresh commit 90f3d42e0bff
[EOF]
");
insta::assert_snapshot!(get_log_output(&secondary_dir),
@"
@ 90f3d42e0bff secondary@
│ ○ 3a9b690d6e67 default@
├─╯
○ b853f7c8b006
◆ 000000000000
[EOF]
");
}
#[test_case(false; "manual")]
#[test_case(true; "automatic")]
fn test_workspaces_current_op_discarded_by_other(automatic: bool) {
let test_env = TestEnvironment::default();
if automatic {
test_env.add_config("snapshot.auto-update-stale = true\n");
}
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
main_dir.write_file("modified", "base\n");
main_dir.write_file("deleted", "base\n");
main_dir.write_file("sparse", "base\n");
main_dir.run_jj(["new"]).success();
main_dir.write_file("modified", "main\n");
main_dir.run_jj(["new"]).success();
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
secondary_dir
.run_jj([
"sparse",
"set",
"--clear",
"--add=modified",
"--add=deleted",
"--add=added",
])
.success();
secondary_dir.write_file("modified", "secondary\n");
secondary_dir.remove_file("deleted");
secondary_dir.write_file("added", "secondary\n");
main_dir.run_jj(["abandon", "@-"]).success();
let output = main_dir.run_jj([
"operation",
"log",
"--template",
r#"id.short(10) ++ " " ++ description"#,
]);
insta::allow_duplicates! {
insta::assert_snapshot!(output, @"
@ 2dea766382 abandon commit de90575a14d8b9198dc0930f9de4a69f846ded36
○ 0d3faebd8c create initial working-copy commit in workspace secondary
○ 200ba564cb add workspace 'secondary'
○ b34eafb924 new empty commit
○ c883929a6b snapshot working copy
○ bd7b1cfa98 new empty commit
○ 4509e20d8c snapshot working copy
○ 90267f31f9 add workspace 'default'
○ 0000000000
[EOF]
");
}
main_dir.run_jj(["operation", "abandon", "..@-"]).success();
main_dir.run_jj(["util", "gc", "--expire=now"]).success();
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ 320bc89effc9 default@
│ ○ 891f00062e10 secondary@
├─╯
○ 367415be5b44
◆ 000000000000
[EOF]
");
}
if automatic {
secondary_dir.run_jj(["help"]).success();
let output = secondary_dir.run_jj(["st"]);
insta::assert_snapshot!(output, @"
Working copy changes:
C {modified => added}
D deleted
M modified
Working copy (@) : kmkuslsw 18851b39 RECOVERY COMMIT FROM `jj workspace update-stale`
Parent commit (@-): rzvqmyuk 891f0006 (empty) (no description set)
[EOF]
------- stderr -------
Failed to read working copy's current operation; attempting recovery. Error message from read attempt: Object 0d3faebd8cf4f0e39ea3eab47f22c9cdbcdaa54d95e79a86a0dab4ebe3b0377f69e1d64fa4661913c8c6af01dc0ebb5ad7c8b2bfa3d229827b2ba756d729e0bf of type operation not found
Created and checked out recovery commit 866928d1e0fd
[EOF]
");
} else {
let output = secondary_dir.run_jj(["st"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Could not read working copy's operation.
Hint: Run `jj workspace update-stale` to recover.
See https://docs.jj-vcs.dev/latest/working-copy/#stale-working-copy for more information.
[EOF]
[exit status: 1]
");
let output = secondary_dir.run_jj(["workspace", "update-stale"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Failed to read working copy's current operation; attempting recovery. Error message from read attempt: Object 0d3faebd8cf4f0e39ea3eab47f22c9cdbcdaa54d95e79a86a0dab4ebe3b0377f69e1d64fa4661913c8c6af01dc0ebb5ad7c8b2bfa3d229827b2ba756d729e0bf of type operation not found
Created and checked out recovery commit 866928d1e0fd
[EOF]
");
}
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
@ 320bc89effc9 default@
│ ○ 18851b397d09 secondary@ "RECOVERY COMMIT FROM `jj workspace update-stale`"
│ ○ 891f00062e10
├─╯
○ 367415be5b44
◆ 000000000000
[EOF]
"#);
}
let output = secondary_dir.run_jj(["sparse", "list"]);
insta::allow_duplicates! {
insta::assert_snapshot!(output, @"
added
deleted
modified
[EOF]
");
}
let output = secondary_dir.run_jj(["st"]);
insta::allow_duplicates! {
insta::assert_snapshot!(output, @"
Working copy changes:
C {modified => added}
D deleted
M modified
Working copy (@) : kmkuslsw 18851b39 RECOVERY COMMIT FROM `jj workspace update-stale`
Parent commit (@-): rzvqmyuk 891f0006 (empty) (no description set)
[EOF]
");
}
insta::allow_duplicates! {
insta::assert_snapshot!(secondary_dir.read_file("modified"), @"secondary");
}
let output = secondary_dir.run_jj(["evolog"]);
if automatic {
insta::assert_snapshot!(output, @"
@ kmkuslsw test.user@example.com 2001-02-03 08:05:18 secondary@ 18851b39
│ RECOVERY COMMIT FROM `jj workspace update-stale`
│ -- operation a35d39d101f4 snapshot working copy
○ kmkuslsw/1 test.user@example.com 2001-02-03 08:05:18 866928d1 (hidden)
(empty) RECOVERY COMMIT FROM `jj workspace update-stale`
-- operation 754c2986ff83 recovery commit
[EOF]
");
} else {
insta::assert_snapshot!(output, @"
@ kmkuslsw test.user@example.com 2001-02-03 08:05:18 secondary@ 18851b39
│ RECOVERY COMMIT FROM `jj workspace update-stale`
│ -- operation 3ae899f26750 snapshot working copy
○ kmkuslsw/1 test.user@example.com 2001-02-03 08:05:18 866928d1 (hidden)
(empty) RECOVERY COMMIT FROM `jj workspace update-stale`
-- operation 754c2986ff83 recovery commit
[EOF]
");
}
}
#[test]
fn test_workspaces_update_stale_noop() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let output = main_dir.run_jj(["workspace", "update-stale"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Attempted recovery, but the working copy is not stale
[EOF]
");
let output = main_dir.run_jj(["workspace", "update-stale", "--ignore-working-copy"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: This command must be able to update the working copy.
Hint: Don't use --ignore-working-copy.
[EOF]
[exit status: 1]
");
let output = main_dir.run_jj(["op", "log", "-Tdescription"]);
insta::assert_snapshot!(output, @"
@ add workspace 'default'
○
[EOF]
");
}
#[test]
fn test_workspaces_unpublished_operation_same_tree() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.run_jj(["desc", "-m=A"]).success();
let a_op_id = main_dir.current_operation_id();
main_dir.run_jj(["new", "-m=B"]).success();
let b_op_id = main_dir.current_operation_id();
main_dir.remove_file(format!(".jj/repo/op_heads/heads/{b_op_id}"));
main_dir.write_file(format!(".jj/repo/op_heads/heads/{a_op_id}"), "");
main_dir
.run_jj(["new", "-m=C", "--ignore-working-copy"])
.success();
let output = main_dir.run_jj(["status"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Internal error: The repo was loaded at operation 8627c7508be4, which seems to be a sibling of the working copy's operation eceacbdafd84
Hint: Run `jj op integrate eceacbdafd84` to add the working copy's operation to the operation log.
[EOF]
[exit status: 255]
");
let output = main_dir.run_jj(["workspace", "update-stale"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Working copy (@) now at: zsuskuln 36a15ac4 (empty) C
Parent commit (@-) : qpvuntsm 8777db25 (empty) A
Updated working copy to fresh commit 36a15ac414e8
[EOF]
");
}
#[test]
fn test_workspaces_update_stale_snapshot() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
main_dir.write_file("file", "changed in main\n");
main_dir.run_jj(["new"]).success();
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
main_dir.run_jj(["new"]).success();
secondary_dir.write_file("file", "changed in second\n");
let output = secondary_dir.run_jj(["workspace", "update-stale"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Concurrent modification detected, resolving automatically.
Attempted recovery, but the working copy is not stale
[EOF]
");
insta::assert_snapshot!(get_log_output(&secondary_dir), @"
@ 35d779b3baea secondary@
│ ○ c9516583d53b default@
│ ○ f6ae7810ef56
├─╯
○ 7d5738ba9943
◆ 000000000000
[EOF]
");
}
#[test]
fn test_colocated_workspace_update_stale() {
let test_env = TestEnvironment::default();
test_env
.run_jj_in(".", ["git", "init", "--colocate", "main"])
.success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
let git_repo = git::open(main_dir.root());
main_dir.write_file("file", "contents\n");
main_dir.run_jj(["new"]).success();
main_dir
.run_jj(["new", "--no-edit", "root()", "-mold book1"])
.success();
main_dir
.run_jj(["bookmark", "set", "-rsubject('old book1')", "book1"])
.success();
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
secondary_dir.write_file("file", "changed in secondary\n");
secondary_dir.run_jj(["squash"]).success();
secondary_dir
.run_jj(["new", "--no-edit", "root()", "-mnew book1"])
.success();
secondary_dir
.run_jj([
"bookmark",
"set",
"-rsubject('new book1')",
"--allow-backwards",
"book1",
])
.success();
secondary_dir.run_jj(["git", "export"]).success();
git::add_commit(&git_repo, "refs/heads/book2", "file", b"", "book2", &[]);
insta::assert_snapshot!(get_log_output(&secondary_dir), @r#"
@ 9cb8253861b5 secondary@
│ ○ f562bf82f2da default@
├─╯
○ 30ed2f28b710
│ ○ e97ad7861f78 book1 "new book1"
├─╯
│ ○ f656b467890b "old book1"
├─╯
◆ 000000000000
[EOF]
"#);
let output = main_dir.run_jj(["st"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: The working copy is stale (not updated since operation 8ab980a3d398).
Hint: Run `jj workspace update-stale` to update it.
See https://docs.jj-vcs.dev/latest/working-copy/#stale-working-copy for more information.
[EOF]
[exit status: 1]
");
let output = main_dir.run_jj(["workspace", "update-stale"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Working copy (@) now at: rlvkpnrz f562bf82 (empty) (no description set)
Parent commit (@-) : qpvuntsm 30ed2f28 (no description set)
Added 0 files, modified 1 files, removed 0 files
Updated working copy to fresh commit f562bf82f2da
Done importing changes from the underlying Git repo.
[EOF]
");
let output = main_dir.run_jj(["st"]);
insta::assert_snapshot!(output, @"
The working copy has no changes.
Working copy (@) : rlvkpnrz f562bf82 (empty) (no description set)
Parent commit (@-): qpvuntsm 30ed2f28 (no description set)
[EOF]
");
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
@ f562bf82f2da default@
│ ○ 9cb8253861b5 secondary@
├─╯
○ 30ed2f28b710
│ ○ 7fe3ff3b9a60 book2 "book2"
├─╯
│ ○ e97ad7861f78 book1 "new book1"
├─╯
│ ○ f656b467890b "old book1"
├─╯
◆ 000000000000
[EOF]
"#);
}
#[test]
fn test_workspaces_forget() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.write_file("file", "contents");
main_dir.run_jj(["new"]).success();
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
let output = main_dir.run_jj(["workspace", "forget"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: The current workspace 'default' no longer exists after this operation. The working copy was left untouched.
Hint: Restore to an operation that contains the workspace (e.g. `jj undo` or `jj redo`).
[EOF]
");
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
secondary: pmmvwywv 31da1455 (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["workspace", "root", "--name", "secondary"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/secondary
[EOF]
");
let output = main_dir.run_jj(["workspace", "root", "--name", "default"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: No such workspace: default
[EOF]
[exit status: 1]
");
insta::assert_snapshot!(get_log_output(&main_dir), @"
○ 31da14559558
○ 006bd1130b84
◆ 000000000000
[EOF]
");
let output = main_dir.run_jj(["log", "-r", "@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Workspace `default` doesn't have a working-copy commit
[EOF]
[exit status: 1]
");
let output = main_dir.run_jj(["workspace", "add", "."]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Destination path exists and is not an empty directory
[EOF]
[exit status: 1]
");
main_dir.run_jj(["workspace", "add", "../third"]).success();
let output = main_dir.run_jj(["workspace", "forget", "secondary", "nonexistent", "third"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No such workspace: nonexistent
[EOF]
");
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"");
}
#[test]
fn test_workspaces_forget_from_before_workspace_store() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.remove_dir_all(".jj/repo/workspace_store");
let output = main_dir.run_jj(["workspace", "forget"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: The current workspace 'default' no longer exists after this operation. The working copy was left untouched.
Hint: Restore to an operation that contains the workspace (e.g. `jj undo` or `jj redo`).
[EOF]
");
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"");
}
#[test]
fn test_workspaces_forget_nothing_changed() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let output = main_dir.run_jj(["workspace", "forget", "second", "third"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No such workspace: second
Warning: No such workspace: third
Nothing changed.
[EOF]
");
}
#[test]
fn test_workspaces_forget_multi_transaction() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.write_file("file", "contents");
main_dir.run_jj(["new"]).success();
main_dir.run_jj(["workspace", "add", "../second"]).success();
main_dir.run_jj(["workspace", "add", "../third"]).success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz f6bf8819 (empty) (no description set)
second: pmmvwywv 31da1455 (empty) (no description set)
third: rzvqmyuk bf5b5b4d (empty) (no description set)
[EOF]
");
main_dir
.run_jj(["workspace", "forget", "second", "third", "fourth"])
.success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz f6bf8819 (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["op", "log", "--limit", "1"]);
insta::assert_snapshot!(output, @"
@ 90493ae75198 test-username@host.example.com default@ 2001-02-03 04:05:12.000 +07:00 - 2001-02-03 04:05:12.000 +07:00
│ forget workspaces second, third
│ args: jj workspace forget second third fourth
[EOF]
");
main_dir.run_jj(["undo"]).success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: rlvkpnrz f6bf8819 (empty) (no description set)
second: pmmvwywv 31da1455 (empty) (no description set)
third: rzvqmyuk bf5b5b4d (empty) (no description set)
[EOF]
");
}
#[test]
fn test_workspaces_forget_abandon_commits() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.write_file("file", "contents");
main_dir.run_jj(["workspace", "add", "../second"]).success();
main_dir.run_jj(["workspace", "add", "../third"]).success();
main_dir.run_jj(["workspace", "add", "../fourth"]).success();
let third_dir = test_env.work_dir("third");
third_dir.run_jj(["edit", "second@"]).success();
let fourth_dir = test_env.work_dir("fourth");
fourth_dir.run_jj(["edit", "second@"]).success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: qpvuntsm 006bd113 (no description set)
fourth: uuqppmxq 94f41578 (empty) (no description set)
second: uuqppmxq 94f41578 (empty) (no description set)
third: uuqppmxq 94f41578 (empty) (no description set)
[EOF]
");
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ 006bd1130b84 default@
│ ○ 94f41578a9e1 fourth@ second@ third@
├─╯
◆ 000000000000
[EOF]
");
main_dir
.run_jj(["workspace", "forget", "default"])
.success();
insta::assert_snapshot!(get_log_output(&main_dir), @"
○ 94f41578a9e1 fourth@ second@ third@
│ ○ 006bd1130b84
├─╯
◆ 000000000000
[EOF]
");
main_dir.run_jj(["workspace", "forget", "second"]).success();
insta::assert_snapshot!(get_log_output(&main_dir), @"
○ 94f41578a9e1 fourth@ third@
│ ○ 006bd1130b84
├─╯
◆ 000000000000
[EOF]
");
main_dir
.run_jj(["workspace", "forget", "third", "fourth"])
.success();
insta::assert_snapshot!(get_log_output(&main_dir), @"
○ 006bd1130b84
◆ 000000000000
[EOF]
");
}
#[test]
fn test_list_workspaces_template() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
test_env.add_config(
r#"
templates.workspace_list = """name ++ ": " ++ target.commit_id().short() ++ " " ++
target.description().first_line() ++
if(target.current_working_copy(), " (current)") ++ "\n""""
"#,
);
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
main_dir.write_file("file", "contents");
main_dir.run_jj(["commit", "-m", "initial"]).success();
main_dir
.run_jj(["workspace", "add", "--name", "second", "../secondary"])
.success();
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: 504e3d8c1bcd (current)
second: 058f604dffcd
[EOF]
");
let output = secondary_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: 504e3d8c1bcd
second: 058f604dffcd (current)
[EOF]
");
let template = r#"name ++ ": " ++ target.commit_id().short() ++ "\n""#;
let output = main_dir.run_jj(["workspace", "list", "-T", template]);
insta::assert_snapshot!(output, @"
default: 504e3d8c1bcd
second: 058f604dffcd
[EOF]
");
}
#[test]
fn test_list_workspaces_template_root() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir
.run_jj(["workspace", "add", "--name", "second", "../secondary"])
.success();
let template = r#"name ++ ": " ++ root ++ "\n""#;
let output = main_dir.run_jj(["workspace", "list", "-T", template]);
insta::assert_snapshot!(output.normalize_backslash(), @"
default: $TEST_ENV/main
second: $TEST_ENV/secondary
[EOF]
");
}
#[test]
fn test_list_workspaces_template_root_unavailable() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir
.run_jj(["workspace", "add", "--name", "second", "../secondary"])
.success();
std::fs::remove_dir_all(test_env.env_root().join("secondary")).unwrap();
let template = r#"name ++ ": " ++ root ++ "\n""#;
let output = main_dir
.run_jj(["workspace", "list", "-T", template])
.normalize_backslash()
.normalize_stdout_with(|s| {
s.replace(
"The system cannot find the file specified.",
"No such file or directory",
)
});
insta::assert_snapshot!(output, @"
default: $TEST_ENV/main
second: <Error: Failed to resolve workspace root: second: $TEST_ENV/main/.jj/repo/../../../secondary: No such file or directory (os error 2)>
[EOF]
");
}
#[test]
fn test_workspaces_root() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let secondary_dir = test_env.work_dir("secondary");
let output = main_dir.run_jj(["workspace", "root"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/main
[EOF]
");
let main_subdir_dir = main_dir.create_dir("subdir");
let output = main_subdir_dir.run_jj(["workspace", "root"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/main
[EOF]
");
main_dir
.run_jj(["workspace", "add", "--name", "secondary", "../secondary"])
.success();
let output = main_dir.run_jj(["workspace", "root", "--name", "secondary"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/secondary
[EOF]
");
let output = secondary_dir.run_jj(["workspace", "root"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/secondary
[EOF]
");
let secondary_subdir_dir = secondary_dir.create_dir("subdir");
let output = secondary_subdir_dir.run_jj(["workspace", "root"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/secondary
[EOF]
");
}
#[test]
fn test_workspaces_relative_path() -> TestResult {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
let repo_file = test_env.env_root().join("secondary/.jj/repo");
let repo_path_bytes = std::fs::read(&repo_file)?;
let stored_path = String::from_utf8(repo_path_bytes)?;
assert_eq!(stored_path, "../../main/.jj/repo");
let secondary_dir = test_env.work_dir("secondary");
let output = secondary_dir.run_jj(["status"]);
insta::assert_snapshot!(output, @"
The working copy has no changes.
Working copy (@) : uuqppmxq 94f41578 (empty) (no description set)
Parent commit (@-): zzzzzzzz 00000000 (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: qpvuntsm e8849ae1 (empty) (no description set)
secondary: uuqppmxq 94f41578 (empty) (no description set)
[EOF]
");
let output = secondary_dir.run_jj(["workspace", "root"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/secondary
[EOF]
");
let secondary_subdir = secondary_dir.create_dir("subdir");
let output = secondary_subdir.run_jj(["workspace", "root"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/secondary
[EOF]
");
let output = main_dir.run_jj(["workspace", "root", "--name", "secondary"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/secondary
[EOF]
");
Ok(())
}
#[test]
fn test_workspaces_root_unavailable() -> TestResult {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir
.run_jj(["workspace", "add", "../secondary"])
.success();
std::fs::remove_dir_all(test_env.env_root().join("secondary"))?;
let output = main_dir.run_jj(["workspace", "root", "--name", "secondary"]);
insta::assert_snapshot!(output.normalize_backslash().strip_stderr_last_line(), @"
------- stderr -------
Error: Cannot resolve absolute workspace path: $TEST_ENV/main/.jj/repo/../../../secondary
[EOF]
[exit status: 1]
");
Ok(())
}
#[test]
fn test_debug_snapshot() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
work_dir.write_file("file", "contents");
work_dir.run_jj(["debug", "snapshot"]).success();
let output = work_dir.run_jj(["op", "log"]);
insta::assert_snapshot!(output, @"
@ a60628738654 test-username@host.example.com default@ 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
│ snapshot working copy
│ args: jj debug snapshot
○ 90267f31f904 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]
");
work_dir.run_jj(["describe", "-m", "initial"]).success();
let output = work_dir.run_jj(["op", "log"]);
insta::assert_snapshot!(output, @"
@ e6e34553de88 test-username@host.example.com default@ 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
│ describe commit 006bd1130b84e90ab082adeabd7409270d5a86da
│ args: jj describe -m initial
○ a60628738654 test-username@host.example.com default@ 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
│ snapshot working copy
│ args: jj debug snapshot
○ 90267f31f904 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_workspaces_rename_nothing_changed() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
let output = main_dir.run_jj(["workspace", "rename", "default"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Nothing changed.
[EOF]
");
}
#[test]
fn test_workspaces_rename_new_workspace_name_already_used() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir
.run_jj(["workspace", "add", "--name", "second", "../secondary"])
.success();
let output = main_dir.run_jj(["workspace", "rename", "second"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Failed to rename a workspace
Caused by: Workspace second already exists
[EOF]
[exit status: 1]
");
}
#[test]
fn test_workspaces_rename_forgotten_workspace() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir
.run_jj(["workspace", "add", "--name", "second", "../secondary"])
.success();
main_dir.run_jj(["workspace", "forget", "second"]).success();
let secondary_dir = test_env.work_dir("secondary");
let output = secondary_dir.run_jj(["workspace", "rename", "third"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: The current workspace 'second' is not tracked in the repo.
[EOF]
[exit status: 1]
");
}
#[test]
fn test_workspaces_rename_workspace() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir
.run_jj(["workspace", "add", "--name", "second", "../secondary"])
.success();
let secondary_dir = test_env.work_dir("secondary");
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: qpvuntsm e8849ae1 (empty) (no description set)
second: uuqppmxq 94f41578 (empty) (no description set)
[EOF]
");
let output = secondary_dir.run_jj(["workspace", "rename", "third"]);
insta::assert_snapshot!(output, @"");
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
default: qpvuntsm e8849ae1 (empty) (no description set)
third: uuqppmxq 94f41578 (empty) (no description set)
[EOF]
");
insta::assert_snapshot!(get_log_output(&main_dir), @"
@ e8849ae12c70 default@
│ ○ 94f41578a9e1 third@
├─╯
◆ 000000000000
[EOF]
");
insta::assert_snapshot!(get_log_output(&secondary_dir), @"
@ 94f41578a9e1 third@
│ ○ e8849ae12c70 default@
├─╯
◆ 000000000000
[EOF]
");
let output = main_dir.run_jj(["workspace", "root", "--name", "secondary"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: No such workspace: secondary
[EOF]
[exit status: 1]
");
let output = main_dir.run_jj(["workspace", "root", "--name", "third"]);
insta::assert_snapshot!(output, @"
$TEST_ENV/secondary
[EOF]
");
}
#[test]
fn test_workspaces_rename_workspace_from_before_workspace_store() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "main"]).success();
let main_dir = test_env.work_dir("main");
main_dir.remove_dir_all(".jj/repo/workspace_store");
let output = main_dir.run_jj(["workspace", "rename", "third"]);
insta::assert_snapshot!(output, @"");
let output = main_dir.run_jj(["workspace", "list"]);
insta::assert_snapshot!(output, @"
third: qpvuntsm e8849ae1 (empty) (no description set)
[EOF]
");
let output = main_dir.run_jj(["workspace", "root", "--name", "third"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Workspace has no recorded path: third
[EOF]
[exit status: 1]
");
}
#[must_use]
fn get_log_output(work_dir: &TestWorkDir) -> CommandOutput {
let template = r#"
separate(" ",
commit_id.short(),
bookmarks,
working_copies,
if(divergent, "(divergent)"),
surround('"', '"', description.first_line()),
)
"#;
work_dir.run_jj(["log", "-T", template, "-r", "all()"])
}