diff --git a/CHANGELOG.md b/CHANGELOG.md index 47cede4f4..57c68df33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 parameters. For example, `author(foo@)` is now an error, and the revset alias `'revset-aliases.foo@' = '@'` will be failed to parse. +* The `root` revset symbol has been converted to function `root()`. + * `jj git push` will now push all branches in the range `remote_branches()..@` instead of only branches pointing to `@` or `@-`. diff --git a/cli/src/config/templates.toml b/cli/src/config/templates.toml index a05961cbe..94fc708dd 100644 --- a/cli/src/config/templates.toml +++ b/cli/src/config/templates.toml @@ -115,7 +115,7 @@ builtin_op_log_comfortable = 'builtin_op_log_compact ++ "\n"' 'builtin_log_root(change_id, commit_id)' = ''' separate(" ", format_short_change_id(change_id), - label("root", "root"), + label("root", "root()"), format_short_commit_id(commit_id), branches ) diff --git a/cli/tests/test_abandon_command.rs b/cli/tests/test_abandon_command.rs index 0a0c023e2..f6fc26276 100644 --- a/cli/tests/test_abandon_command.rs +++ b/cli/tests/test_abandon_command.rs @@ -20,7 +20,7 @@ pub mod common; fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, parents: &[&str]) { if parents.is_empty() { - test_env.jj_cmd_success(repo_path, &["new", "root", "-m", name]); + test_env.jj_cmd_success(repo_path, &["new", "root()", "-m", name]); } else { let mut args = vec!["new", "-m", name]; args.extend(parents); @@ -153,7 +153,7 @@ fn test_rebase_branch_with_merge() { ◉ a b e?? "###); - let stderr = test_env.jj_cmd_failure(&repo_path, &["abandon", "root"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["abandon", "root()"]); insta::assert_snapshot!(stderr, @r###" Error: Cannot rewrite the root commit "###); diff --git a/cli/tests/test_alias.rs b/cli/tests/test_alias.rs index cbd2f21fd..4288c26b7 100644 --- a/cli/tests/test_alias.rs +++ b/cli/tests/test_alias.rs @@ -135,9 +135,9 @@ fn test_alias_cannot_override_builtin() { test_env.add_config(r#"aliases.log = ["rebase"]"#); // Alias should be ignored - let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "root"]); + let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "root()"]); insta::assert_snapshot!(stdout, @r###" - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); } diff --git a/cli/tests/test_branch_command.rs b/cli/tests/test_branch_command.rs index 6347de5f3..1df245e0b 100644 --- a/cli/tests/test_branch_command.rs +++ b/cli/tests/test_branch_command.rs @@ -51,7 +51,7 @@ fn test_branch_forbidden_at_root() { test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); let repo_path = test_env.env_root().join("repo"); - let stderr = test_env.jj_cmd_failure(&repo_path, &["branch", "create", "fred", "-r=root"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["branch", "create", "fred", "-r=root()"]); insta::assert_snapshot!(stderr, @r###" Error: Cannot rewrite the root commit "###); @@ -448,7 +448,7 @@ fn test_branch_list_filtered_by_revset() { test_env.jj_cmd_success(test_env.env_root(), &["init", "remote", "--git"]); let remote_path = test_env.env_root().join("remote"); for branch in ["remote-keep", "remote-delete", "remote-rewrite"] { - test_env.jj_cmd_success(&remote_path, &["new", "root", "-m", branch]); + test_env.jj_cmd_success(&remote_path, &["new", "root()", "-m", branch]); test_env.jj_cmd_success(&remote_path, &["branch", "set", branch]); } test_env.jj_cmd_success(&remote_path, &["new"]); @@ -462,7 +462,7 @@ fn test_branch_list_filtered_by_revset() { &["git", "clone", remote_git_path.to_str().unwrap(), "local"], ); let local_path = test_env.env_root().join("local"); - test_env.jj_cmd_success(&local_path, &["new", "root", "-m", "local-keep"]); + test_env.jj_cmd_success(&local_path, &["new", "root()", "-m", "local-keep"]); test_env.jj_cmd_success(&local_path, &["branch", "set", "local-keep"]); // Mutate refs in local repository diff --git a/cli/tests/test_checkout.rs b/cli/tests/test_checkout.rs index d2a704142..ea1111048 100644 --- a/cli/tests/test_checkout.rs +++ b/cli/tests/test_checkout.rs @@ -63,10 +63,10 @@ fn test_checkout_not_single_rev() { test_env.jj_cmd_success(&repo_path, &["commit", "-m", "fourth"]); test_env.jj_cmd_success(&repo_path, &["commit", "-m", "fifth"]); - let stderr = test_env.jj_cmd_failure(&repo_path, &["checkout", "root..@"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["checkout", "root()..@"]); insta::assert_snapshot!(stderr, @r###" - Error: Revset "root..@" resolved to more than one revision - Hint: The revset "root..@" resolved to these revisions: + Error: Revset "root()..@" resolved to more than one revision + Hint: The revset "root()..@" resolved to these revisions: royxmykx 2f859371 (empty) (no description set) mzvwutvl 5c1afd8b (empty) fifth zsuskuln 009f88bf (empty) fourth @@ -75,10 +75,10 @@ fn test_checkout_not_single_rev() { ... "###); - let stderr = test_env.jj_cmd_failure(&repo_path, &["checkout", "root..@-"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["checkout", "root()..@-"]); insta::assert_snapshot!(stderr, @r###" - Error: Revset "root..@-" resolved to more than one revision - Hint: The revset "root..@-" resolved to these revisions: + Error: Revset "root()..@-" resolved to more than one revision + Hint: The revset "root()..@-" resolved to these revisions: mzvwutvl 5c1afd8b (empty) fifth zsuskuln 009f88bf (empty) fourth kkmpptxz 3fa8931e (empty) third diff --git a/cli/tests/test_chmod_command.rs b/cli/tests/test_chmod_command.rs index f258720e1..2c752e50e 100644 --- a/cli/tests/test_chmod_command.rs +++ b/cli/tests/test_chmod_command.rs @@ -26,7 +26,7 @@ fn create_commit( files: &[(&str, &str)], ) { if parents.is_empty() { - test_env.jj_cmd_success(repo_path, &["new", "root", "-m", name]); + test_env.jj_cmd_success(repo_path, &["new", "root()", "-m", name]); } else { let mut args = vec!["new", "-m", name]; args.extend(parents); diff --git a/cli/tests/test_commit_template.rs b/cli/tests/test_commit_template.rs index 39351fb50..68f5c2ddd 100644 --- a/cli/tests/test_commit_template.rs +++ b/cli/tests/test_commit_template.rs @@ -141,7 +141,7 @@ fn test_log_default() { │ (empty) description 1 ◉ qpvuntsm test.user@example.com 2001-02-03 04:05:08.000 +07:00 4291e264 │ add a file - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); // Color @@ -151,7 +151,7 @@ fn test_log_default() { │ (empty) description 1 ◉ qpvuntsm test.user@example.com 2001-02-03 04:05:08.000 +07:00 4291e264 │ add a file - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); // Color without graph @@ -161,7 +161,7 @@ fn test_log_default() { (empty) description 1 qpvuntsm test.user@example.com 2001-02-03 04:05:08.000 +07:00 4291e264 add a file - zzzzzzzz root 00000000 + zzzzzzzz root() 00000000 "###); } @@ -184,7 +184,7 @@ fn test_log_builtin_templates() { insta::assert_snapshot!(render(r#"builtin_log_oneline"#), @r###" @ rlvkpnrz (no email set) 2001-02-03 04:05:08.000 +07:00 dc315397 (empty) (no description set) ◉ qpvuntsm test.user 2001-02-03 04:05:07.000 +07:00 230dd059 (empty) (no description set) - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(render(r#"builtin_log_compact"#), @r###" @@ -192,7 +192,7 @@ fn test_log_builtin_templates() { │ (empty) (no description set) ◉ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059 │ (empty) (no description set) - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(render(r#"builtin_log_comfortable"#), @r###" @@ -202,7 +202,7 @@ fn test_log_builtin_templates() { ◉ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059 │ (empty) (no description set) │ - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(render(r#"builtin_log_detailed"#), @r###" @@ -250,7 +250,7 @@ fn test_log_builtin_templates_colored() { insta::assert_snapshot!(render(r#"builtin_log_oneline"#), @r###" @ rlvkpnrz (no email set) 2001-02-03 04:05:08.000 +07:00 dc315397 (empty) (no description set) ◉ qpvuntsm test.user 2001-02-03 04:05:07.000 +07:00 230dd059 (empty) (no description set) - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(render(r#"builtin_log_compact"#), @r###" @@ -258,7 +258,7 @@ fn test_log_builtin_templates_colored() { │ (empty) (no description set) ◉ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059 │ (empty) (no description set) - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(render(r#"builtin_log_comfortable"#), @r###" @@ -268,7 +268,7 @@ fn test_log_builtin_templates_colored() { ◉ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059 │ (empty) (no description set) │ - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(render(r#"builtin_log_detailed"#), @r###" @@ -309,7 +309,7 @@ fn test_log_obslog_divergence() { insta::assert_snapshot!(stdout, @r###" @ qpvuntsm test.user@example.com 2001-02-03 04:05:08.000 +07:00 7a17d52e │ description 1 - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); // Create divergence @@ -324,7 +324,7 @@ fn test_log_obslog_divergence() { │ description 2 │ @ qpvuntsm?? test.user@example.com 2001-02-03 04:05:08.000 +07:00 7a17d52e ├─╯ description 1 - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); // Color @@ -334,7 +334,7 @@ fn test_log_obslog_divergence() { │ description 2 │ @ qpvuntsm?? test.user@example.com 2001-02-03 04:05:08.000 +07:00 7a17d52e ├─╯ description 1 - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); // Obslog and hidden divergent @@ -375,7 +375,7 @@ fn test_log_git_head() { │ initial ◉ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 master HEAD@git 230dd059 │ (empty) (no description set) - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); } @@ -400,7 +400,7 @@ fn test_log_customize_short_id() { insta::assert_snapshot!(stdout, @r###" @ Q_pvun test.user@example.com 2001-02-03 04:05:08.000 +07:00 6_9542 │ (empty) first - ◉ Z_zzzz root 0_0000 + ◉ Z_zzzz root() 0_0000 "###); // Customize only the change id @@ -418,6 +418,6 @@ fn test_log_customize_short_id() { insta::assert_snapshot!(stdout, @r###" @ QPVUNTSM test.user@example.com 2001-02-03 04:05:08.000 +07:00 69542c19 │ (empty) first - ◉ ZZZZZZZZ root 00000000 + ◉ ZZZZZZZZ root() 00000000 "###); } diff --git a/cli/tests/test_debug_command.rs b/cli/tests/test_debug_command.rs index da3f15797..0a513ca0c 100644 --- a/cli/tests/test_debug_command.rs +++ b/cli/tests/test_debug_command.rs @@ -25,7 +25,7 @@ fn test_debug_revset() { test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); let workspace_path = test_env.env_root().join("repo"); - let stdout = test_env.jj_cmd_success(&workspace_path, &["debug", "revset", "root"]); + let stdout = test_env.jj_cmd_success(&workspace_path, &["debug", "revset", "root()"]); insta::with_settings!({filters => vec![ (r"(?m)(^ .*\n)+", " ..\n"), ]}, { diff --git a/cli/tests/test_diff_command.rs b/cli/tests/test_diff_command.rs index 3284a9184..52a32ad23 100644 --- a/cli/tests/test_diff_command.rs +++ b/cli/tests/test_diff_command.rs @@ -154,16 +154,16 @@ fn test_diff_types() { let file_path = repo_path.join("foo"); // Missing - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=missing"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m=missing"]); // Normal file - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=file"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m=file"]); std::fs::write(&file_path, "foo").unwrap(); // Conflict (add/add) - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=conflict"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m=conflict"]); std::fs::write(&file_path, "foo").unwrap(); - test_env.jj_cmd_success(&repo_path, &["new", "root"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()"]); std::fs::write(&file_path, "bar").unwrap(); test_env.jj_cmd_success(&repo_path, &["move", r#"--to=description("conflict")"#]); @@ -173,12 +173,12 @@ fn test_diff_types() { use std::path::PathBuf; // Executable - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=executable"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m=executable"]); std::fs::write(&file_path, "foo").unwrap(); std::fs::set_permissions(&file_path, std::fs::Permissions::from_mode(0o755)).unwrap(); // Symlink - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=symlink"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m=symlink"]); std::os::unix::fs::symlink(PathBuf::from("."), &file_path).unwrap(); } @@ -689,7 +689,7 @@ fn test_diff_external_tool() { │ -- │ file1 │ file2 - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 -- "###); @@ -793,7 +793,7 @@ fn test_diff_stat_long_name_or_stat() { let repo_path = test_env.env_root().join("repo"); let get_stat = |test_env: &TestEnvironment, path_length: usize, stat_size: usize| { - test_env.jj_cmd_success(&repo_path, &["new", "root"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()"]); let ascii_name = "1234567890".chars().cycle().take(path_length).join(""); let han_name = "一二三四五六七八九十" .chars() diff --git a/cli/tests/test_duplicate_command.rs b/cli/tests/test_duplicate_command.rs index 0f5e61752..e9b18d8c9 100644 --- a/cli/tests/test_duplicate_command.rs +++ b/cli/tests/test_duplicate_command.rs @@ -20,7 +20,7 @@ pub mod common; fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, parents: &[&str]) { if parents.is_empty() { - test_env.jj_cmd_success(repo_path, &["new", "root", "-m", name]); + test_env.jj_cmd_success(repo_path, &["new", "root()", "-m", name]); } else { let mut args = vec!["new", "-m", name]; args.extend(parents); @@ -49,7 +49,7 @@ fn test_duplicate() { ◉ 000000000000 "###); - let stderr = test_env.jj_cmd_failure(&repo_path, &["duplicate", "root"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["duplicate", "root()"]); insta::assert_snapshot!(stderr, @r###" Error: Cannot rewrite the root commit "###); diff --git a/cli/tests/test_edit_command.rs b/cli/tests/test_edit_command.rs index c80275a30..c05b4fbf9 100644 --- a/cli/tests/test_edit_command.rs +++ b/cli/tests/test_edit_command.rs @@ -116,6 +116,6 @@ fn test_edit_root() { let test_env = TestEnvironment::default(); test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); let repo_path = test_env.env_root().join("repo"); - let stderr = test_env.jj_cmd_failure(&repo_path, &["edit", "root"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["edit", "root()"]); insta::assert_snapshot!(stderr, @"Error: Cannot rewrite the root commit"); } diff --git a/cli/tests/test_git_colocated.rs b/cli/tests/test_git_colocated.rs index eb73e687b..3b79d5172 100644 --- a/cli/tests/test_git_colocated.rs +++ b/cli/tests/test_git_colocated.rs @@ -313,7 +313,7 @@ fn test_git_colocated_external_checkout() { let git_repo = git2::Repository::init(&repo_path).unwrap(); test_env.jj_cmd_success(&repo_path, &["init", "--git-repo=."]); test_env.jj_cmd_success(&repo_path, &["ci", "-m=A"]); - test_env.jj_cmd_success(&repo_path, &["new", "-m=B", "root"]); + test_env.jj_cmd_success(&repo_path, &["new", "-m=B", "root()"]); test_env.jj_cmd_success(&repo_path, &["new"]); // Checked out anonymous branch diff --git a/cli/tests/test_git_fetch.rs b/cli/tests/test_git_fetch.rs index 0ca2523d0..6512457ff 100644 --- a/cli/tests/test_git_fetch.rs +++ b/cli/tests/test_git_fetch.rs @@ -58,7 +58,7 @@ fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String { fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, parents: &[&str]) { let descr = format!("descr_for_{name}"); if parents.is_empty() { - test_env.jj_cmd_success(repo_path, &["new", "root", "-m", &descr]); + test_env.jj_cmd_success(repo_path, &["new", "root()", "-m", &descr]); } else { let mut args = vec!["new", "-m", &descr]; args.extend(parents); @@ -299,7 +299,7 @@ fn test_git_fetch_conflicting_branches() { add_git_remote(&test_env, &repo_path, "rem1"); // Create a rem1 branch locally - test_env.jj_cmd_success(&repo_path, &["new", "root"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()"]); test_env.jj_cmd_success(&repo_path, &["branch", "create", "rem1"]); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" rem1: kkmpptxz fcdbbd73 (empty) (no description set) @@ -329,7 +329,7 @@ fn test_git_fetch_conflicting_branches_colocated() { insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @""); // Create a rem1 branch locally - test_env.jj_cmd_success(&repo_path, &["new", "root"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()"]); test_env.jj_cmd_success(&repo_path, &["branch", "create", "rem1"]); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" rem1: zsuskuln f652c321 (empty) (no description set) diff --git a/cli/tests/test_git_push.rs b/cli/tests/test_git_push.rs index 8e52383de..d6e8c317c 100644 --- a/cli/tests/test_git_push.rs +++ b/cli/tests/test_git_push.rs @@ -30,7 +30,7 @@ fn set_up() -> (TestEnvironment, PathBuf) { test_env.jj_cmd_success(&origin_path, &["describe", "-m=description 1"]); test_env.jj_cmd_success(&origin_path, &["branch", "create", "branch1"]); - test_env.jj_cmd_success(&origin_path, &["new", "root", "-m=description 2"]); + test_env.jj_cmd_success(&origin_path, &["new", "root()", "-m=description 2"]); test_env.jj_cmd_success(&origin_path, &["branch", "create", "branch2"]); test_env.jj_cmd_success(&origin_path, &["git", "export"]); @@ -505,7 +505,7 @@ fn test_git_push_missing_author() { .assert() .success(); }; - run_without_var("JJ_USER", &["checkout", "root", "-m=initial"]); + run_without_var("JJ_USER", &["checkout", "root()", "-m=initial"]); run_without_var("JJ_USER", &["branch", "create", "missing-name"]); let stderr = test_env.jj_cmd_failure( &workspace_root, @@ -514,7 +514,7 @@ fn test_git_push_missing_author() { insta::assert_snapshot!(stderr, @r###" Error: Won't push commit 944313939bbd since it has no author and/or committer set "###); - run_without_var("JJ_EMAIL", &["checkout", "root", "-m=initial"]); + run_without_var("JJ_EMAIL", &["checkout", "root()", "-m=initial"]); run_without_var("JJ_EMAIL", &["branch", "create", "missing-email"]); let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push", "--branch=missing-email"]); @@ -540,7 +540,7 @@ fn test_git_push_missing_committer() { insta::assert_snapshot!(stderr, @r###" Error: Won't push commit 4fd190283d1a since it has no author and/or committer set "###); - test_env.jj_cmd_success(&workspace_root, &["checkout", "root"]); + test_env.jj_cmd_success(&workspace_root, &["checkout", "root()"]); test_env.jj_cmd_success(&workspace_root, &["branch", "create", "missing-email"]); run_without_var("JJ_EMAIL", &["describe", "-m=no committer email"]); let stderr = @@ -591,7 +591,7 @@ fn test_git_push_conflicting_branches() { .delete() .unwrap(); test_env.jj_cmd_success(&workspace_root, &["git", "import"]); - test_env.jj_cmd_success(&workspace_root, &["new", "root", "-m=description 3"]); + test_env.jj_cmd_success(&workspace_root, &["new", "root()", "-m=description 3"]); test_env.jj_cmd_success(&workspace_root, &["branch", "set", "branch2"]); test_env.jj_cmd_success(&workspace_root, &["git", "fetch"]); insta::assert_snapshot!(test_env.jj_cmd_success(&workspace_root, &["branch", "list"]), @r###" diff --git a/cli/tests/test_init_command.rs b/cli/tests/test_init_command.rs index d0392e212..1902715b7 100644 --- a/cli/tests/test_init_command.rs +++ b/cli/tests/test_init_command.rs @@ -321,7 +321,7 @@ fn test_init_git_external_but_git_dir_exists() { // The local ".git" repository is unrelated, so no commits should be imported let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); insta::assert_snapshot!(stdout, @r###" - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); // Check that Git HEAD is not set because this isn't a colocated repo diff --git a/cli/tests/test_interdiff_command.rs b/cli/tests/test_interdiff_command.rs index 0c9ca4ad6..a3ef2fed8 100644 --- a/cli/tests/test_interdiff_command.rs +++ b/cli/tests/test_interdiff_command.rs @@ -27,7 +27,7 @@ fn test_interdiff_basic() { std::fs::write(repo_path.join("file2"), "foo\n").unwrap(); test_env.jj_cmd_success(&repo_path, &["branch", "create", "left"]); - test_env.jj_cmd_success(&repo_path, &["checkout", "root"]); + test_env.jj_cmd_success(&repo_path, &["checkout", "root()"]); std::fs::write(repo_path.join("file3"), "foo\n").unwrap(); test_env.jj_cmd_success(&repo_path, &["new"]); std::fs::write(repo_path.join("file2"), "foo\nbar\n").unwrap(); @@ -91,7 +91,7 @@ fn test_interdiff_paths() { std::fs::write(repo_path.join("file2"), "bar\n").unwrap(); test_env.jj_cmd_success(&repo_path, &["branch", "create", "left"]); - test_env.jj_cmd_success(&repo_path, &["checkout", "root"]); + test_env.jj_cmd_success(&repo_path, &["checkout", "root()"]); std::fs::write(repo_path.join("file1"), "foo\n").unwrap(); std::fs::write(repo_path.join("file2"), "foo\n").unwrap(); test_env.jj_cmd_success(&repo_path, &["new"]); @@ -139,7 +139,7 @@ fn test_interdiff_conflicting() { std::fs::write(repo_path.join("file"), "bar\n").unwrap(); test_env.jj_cmd_success(&repo_path, &["branch", "create", "left"]); - test_env.jj_cmd_success(&repo_path, &["checkout", "root"]); + test_env.jj_cmd_success(&repo_path, &["checkout", "root()"]); std::fs::write(repo_path.join("file"), "abc\n").unwrap(); test_env.jj_cmd_success(&repo_path, &["new"]); std::fs::write(repo_path.join("file"), "def\n").unwrap(); diff --git a/cli/tests/test_log_command.rs b/cli/tests/test_log_command.rs index ca4071e7d..aae4ab68b 100644 --- a/cli/tests/test_log_command.rs +++ b/cli/tests/test_log_command.rs @@ -49,16 +49,16 @@ fn test_log_legacy_range_operator() { insta::assert_snapshot!(stdout, @r###" @ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059 │ (empty) (no description set) - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(stderr, @r###" The `:` revset operator is deprecated. Please switch to `::`. "###); - let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r=root:@"]); + let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r=root():@"]); insta::assert_snapshot!(stdout, @r###" @ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059 │ (empty) (no description set) - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(stderr, @r###" The `:` revset operator is deprecated. Please switch to `::`. @@ -589,7 +589,7 @@ fn test_log_prefix_highlight_counts_hidden_commits() { ); // Create 2^7 hidden commits - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m", "extra"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m", "extra"]); for _ in 0..7 { test_env.jj_cmd_success(&repo_path, &["duplicate", "description(extra)"]); } @@ -954,7 +954,7 @@ fn test_default_revset() { test_env.jj_cmd_success(&repo_path, &["describe", "-m", "add a file"]); // Set configuration to only show the root commit. - test_env.add_config(r#"revsets.log = "root""#); + test_env.add_config(r#"revsets.log = "root()""#); // Log should only contain one line (for the root commit), and not show the // commit created above. @@ -979,7 +979,7 @@ fn test_default_revset_per_repo() { // Set configuration to only show the root commit. std::fs::write( repo_path.join(".jj/repo/config.toml"), - r#"revsets.log = "root""#, + r#"revsets.log = "root()""#, ) .unwrap(); @@ -1005,7 +1005,7 @@ fn test_multiple_revsets() { } // Default revset should be overridden if one or more -r options are specified. - test_env.add_config(r#"revsets.log = "root""#); + test_env.add_config(r#"revsets.log = "root()""#); insta::assert_snapshot!( test_env.jj_cmd_success(&repo_path, &["log", "-T", "branches", "-rfoo"]), diff --git a/cli/tests/test_new_command.rs b/cli/tests/test_new_command.rs index d4b2244b8..f04d5d678 100644 --- a/cli/tests/test_new_command.rs +++ b/cli/tests/test_new_command.rs @@ -34,7 +34,7 @@ fn test_new() { "###); // Start a new change off of a specific commit (the root commit in this case). - test_env.jj_cmd_success(&repo_path, &["new", "-m", "off of root", "root"]); + test_env.jj_cmd_success(&repo_path, &["new", "-m", "off of root", "root()"]); insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" @ 026537ddb96b801b9cb909985d5443aab44616c1 off of root │ ◉ 4f2d6e0a3482a6a34e4856a4a63869c0df109e79 a new commit @@ -53,7 +53,7 @@ fn test_new_merge() { test_env.jj_cmd_success(&repo_path, &["branch", "create", "main"]); test_env.jj_cmd_success(&repo_path, &["describe", "-m", "add file1"]); std::fs::write(repo_path.join("file1"), "a").unwrap(); - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m", "add file2"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m", "add file2"]); std::fs::write(repo_path.join("file2"), "b").unwrap(); // Create a merge commit @@ -100,7 +100,7 @@ fn test_new_merge() { "###); // merge with root - let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "@", "root"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "@", "root()"]); insta::assert_snapshot!(stderr, @r###" Error: Cannot merge with root revision "###); @@ -391,7 +391,7 @@ fn test_new_insert_before_root() { "###); let stderr = - test_env.jj_cmd_failure(&repo_path, &["new", "--insert-before", "-m", "G", "root"]); + test_env.jj_cmd_failure(&repo_path, &["new", "--insert-before", "-m", "G", "root()"]); insta::assert_snapshot!(stderr, @r###" Error: Cannot insert a commit before the root commit "###); @@ -404,9 +404,9 @@ fn setup_before_insertion(test_env: &TestEnvironment, repo_path: &Path) { test_env.jj_cmd_success(repo_path, &["commit", "-m", "B"]); test_env.jj_cmd_success(repo_path, &["branch", "create", "C"]); test_env.jj_cmd_success(repo_path, &["describe", "-m", "C"]); - test_env.jj_cmd_success(repo_path, &["new", "-m", "D", "root"]); + test_env.jj_cmd_success(repo_path, &["new", "-m", "D", "root()"]); test_env.jj_cmd_success(repo_path, &["branch", "create", "D"]); - test_env.jj_cmd_success(repo_path, &["new", "-m", "E", "root"]); + test_env.jj_cmd_success(repo_path, &["new", "-m", "E", "root()"]); test_env.jj_cmd_success(repo_path, &["branch", "create", "E"]); test_env.jj_cmd_success(repo_path, &["new", "-m", "F", "D", "E"]); test_env.jj_cmd_success(repo_path, &["branch", "create", "F"]); diff --git a/cli/tests/test_obslog_command.rs b/cli/tests/test_obslog_command.rs index 648d15d81..17d03d984 100644 --- a/cli/tests/test_obslog_command.rs +++ b/cli/tests/test_obslog_command.rs @@ -26,7 +26,7 @@ fn test_obslog_with_or_without_diff() { test_env.jj_cmd_success(&repo_path, &["new", "-m", "my description"]); std::fs::write(repo_path.join("file1"), "foo\nbar\n").unwrap(); std::fs::write(repo_path.join("file2"), "foo\n").unwrap(); - test_env.jj_cmd_success(&repo_path, &["rebase", "-r", "@", "-d", "root"]); + test_env.jj_cmd_success(&repo_path, &["rebase", "-r", "@", "-d", "root()"]); std::fs::write(repo_path.join("file1"), "resolved\n").unwrap(); let stdout = test_env.jj_cmd_success(&repo_path, &["obslog"]); diff --git a/cli/tests/test_rebase_command.rs b/cli/tests/test_rebase_command.rs index 7f6e5aa32..8f49464eb 100644 --- a/cli/tests/test_rebase_command.rs +++ b/cli/tests/test_rebase_command.rs @@ -20,7 +20,7 @@ pub mod common; fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, parents: &[&str]) { if parents.is_empty() { - test_env.jj_cmd_success(repo_path, &["new", "root", "-m", name]); + test_env.jj_cmd_success(repo_path, &["new", "root()", "-m", name]); } else { let mut args = vec!["new", "-m", name]; args.extend(parents); @@ -275,7 +275,7 @@ fn test_rebase_single_revision() { // Now, let's try moving the merge commit. After, both parents of "c" ("a" and // "b") should become parents of "d". - let stdout = test_env.jj_cmd_success(&repo_path, &["rebase", "-r", "c", "-d", "root"]); + let stdout = test_env.jj_cmd_success(&repo_path, &["rebase", "-r", "c", "-d", "root()"]); insta::assert_snapshot!(stdout, @r###" Also rebased 1 descendant commits onto parent of rebased commit Working copy now at: vruxwmqv bf87078f d | d @@ -401,8 +401,10 @@ fn test_rebase_multiple_destinations() { Error: More than one revset resolved to revision d370aee184ba "###); - let stderr = - test_env.jj_cmd_failure(&repo_path, &["rebase", "-r", "a", "-d", "b", "-d", "root"]); + let stderr = test_env.jj_cmd_failure( + &repo_path, + &["rebase", "-r", "a", "-d", "b", "-d", "root()"], + ); insta::assert_snapshot!(stderr, @r###" Error: Cannot merge with root revision "###); diff --git a/cli/tests/test_resolve_command.rs b/cli/tests/test_resolve_command.rs index ab6722680..932bfeb48 100644 --- a/cli/tests/test_resolve_command.rs +++ b/cli/tests/test_resolve_command.rs @@ -26,7 +26,7 @@ fn create_commit( files: &[(&str, &str)], ) { if parents.is_empty() { - test_env.jj_cmd_success(repo_path, &["new", "root", "-m", name]); + test_env.jj_cmd_success(repo_path, &["new", "root()", "-m", name]); } else { let mut args = vec!["new", "-m", name]; args.extend(parents); diff --git a/cli/tests/test_restore_command.rs b/cli/tests/test_restore_command.rs index dfac6cb68..ca82ee390 100644 --- a/cli/tests/test_restore_command.rs +++ b/cli/tests/test_restore_command.rs @@ -69,7 +69,7 @@ fn test_restore() { insta::assert_snapshot!(stdout, @""); // Cannot restore the root revision - let stderr = test_env.jj_cmd_failure(&repo_path, &["restore", "-c=root"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["restore", "-c=root()"]); insta::assert_snapshot!(stderr, @r###" Error: Cannot rewrite the root commit "###); @@ -255,7 +255,7 @@ fn create_commit( files: &[(&str, &str)], ) { if parents.is_empty() { - test_env.jj_cmd_success(repo_path, &["new", "root", "-m", name]); + test_env.jj_cmd_success(repo_path, &["new", "root()", "-m", name]); } else { let mut args = vec!["new", "-m", name]; args.extend(parents); diff --git a/cli/tests/test_revset_output.rs b/cli/tests/test_revset_output.rs index 55643f1ff..41ced192f 100644 --- a/cli/tests/test_revset_output.rs +++ b/cli/tests/test_revset_output.rs @@ -151,12 +151,12 @@ fn test_bad_function_call() { = Invalid arguments to revset function "branches": Invalid string pattern kind "bad" "###); - let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root::whatever()"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root()::whatever()"]); insta::assert_snapshot!(stderr, @r###" - Error: Failed to parse revset: --> 1:7 + Error: Failed to parse revset: --> 1:9 | - 1 | root::whatever() - | ^------^ + 1 | root()::whatever() + | ^------^ | = Revset function "whatever" doesn't exist "###); @@ -254,7 +254,7 @@ fn test_alias() { test_env.add_config( r###" [revset-aliases] - 'my-root' = 'root' + 'my-root' = 'root()' 'syntax-error' = 'whatever &' 'recurse' = 'recurse1' 'recurse1' = 'recurse2()' @@ -266,20 +266,20 @@ fn test_alias() { let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "my-root"]); insta::assert_snapshot!(stdout, @r###" - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "identity(my-root)"]); insta::assert_snapshot!(stdout, @r###" - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); - let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root & syntax-error"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root() & syntax-error"]); insta::assert_snapshot!(stderr, @r###" - Error: Failed to parse revset: --> 1:8 + Error: Failed to parse revset: --> 1:10 | - 1 | root & syntax-error - | ^----------^ + 1 | root() & syntax-error + | ^----------^ | = Alias "syntax-error" cannot be expanded --> 1:11 @@ -316,12 +316,12 @@ fn test_alias() { = Invalid arguments to revset function "author": Expected function argument of string pattern "###); - let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root & recurse"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root() & recurse"]); insta::assert_snapshot!(stderr, @r###" - Error: Failed to parse revset: --> 1:8 + Error: Failed to parse revset: --> 1:10 | - 1 | root & recurse - | ^-----^ + 1 | root() & recurse + | ^-----^ | = Alias "recurse" cannot be expanded --> 1:1 @@ -384,16 +384,16 @@ fn test_bad_alias_decl() { test_env.add_config( r#" [revset-aliases] - 'my-root' = 'root' - '"bad"' = 'root' - 'badfn(a, a)' = 'root' + 'my-root' = 'root()' + '"bad"' = 'root()' + 'badfn(a, a)' = 'root()' "#, ); // Invalid declaration should be warned and ignored. let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r", "my-root"]); insta::assert_snapshot!(stdout, @r###" - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); insta::assert_snapshot!(stderr, @r###" Failed to load "revset-aliases."bad"": --> 1:1 diff --git a/cli/tests/test_templater.rs b/cli/tests/test_templater.rs index 692f59c92..21a3753e7 100644 --- a/cli/tests/test_templater.rs +++ b/cli/tests/test_templater.rs @@ -33,9 +33,9 @@ fn test_templater_branches() { // Created some branches on the remote test_env.jj_cmd_success(&origin_path, &["describe", "-m=description 1"]); test_env.jj_cmd_success(&origin_path, &["branch", "create", "branch1"]); - test_env.jj_cmd_success(&origin_path, &["new", "root", "-m=description 2"]); + test_env.jj_cmd_success(&origin_path, &["new", "root()", "-m=description 2"]); test_env.jj_cmd_success(&origin_path, &["branch", "create", "branch2"]); - test_env.jj_cmd_success(&origin_path, &["new", "root", "-m=description 3"]); + test_env.jj_cmd_success(&origin_path, &["new", "root()", "-m=description 3"]); test_env.jj_cmd_success(&origin_path, &["branch", "create", "branch3"]); test_env.jj_cmd_success(&origin_path, &["git", "export"]); test_env.jj_cmd_success( diff --git a/cli/tests/test_tree_level_conflicts.rs b/cli/tests/test_tree_level_conflicts.rs index 5ce7e02a6..047a95951 100644 --- a/cli/tests/test_tree_level_conflicts.rs +++ b/cli/tests/test_tree_level_conflicts.rs @@ -24,9 +24,9 @@ fn test_enable_tree_level_conflicts() { // Create a few commits before we enable tree-level conflicts let file_path = repo_path.join("file"); - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=left"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m=left"]); std::fs::write(&file_path, "left").unwrap(); - test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=right"]); + test_env.jj_cmd_success(&repo_path, &["new", "root()", "-m=right"]); std::fs::write(&file_path, "right").unwrap(); test_env.jj_cmd_success( &repo_path, @@ -48,7 +48,7 @@ fn test_enable_tree_level_conflicts() { │ │ right ◉ │ rlvkpnrz test.user@example.com 2001-02-03 04:05:09.000 +07:00 32003b88 ├─╯ left - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); // Enable tree-level conflicts @@ -65,7 +65,7 @@ fn test_enable_tree_level_conflicts() { │ │ right ◉ │ rlvkpnrz test.user@example.com 2001-02-03 04:05:09.000 +07:00 32003b88 ├─╯ left - ◉ zzzzzzzz root 00000000 + ◉ zzzzzzzz root() 00000000 "###); // ...but at least it has no diff let stdout = test_env.jj_cmd_success(&repo_path, &["diff"]); diff --git a/docs/glossary.md b/docs/glossary.md index 0b5f7d13e..13af2a23f 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -159,9 +159,9 @@ copy rewrites the working copy commit. The root commit is a virtual commit at the root of every repository. It has a commit ID consisting of all '0's (`00000000...`) and a change ID consisting of all 'z's (`zzzzzzzz...`). It can be referred to in [revsets](#revset) by the -special name `root`. Note that our definition of "root commit" is different from +function `root()`. Note that our definition of "root commit" is different from Git's; Git's "root commits" are the first commit(s) in the repository, i.e. the -commits `jj log -r root+` will show. +commits `jj log -r root()+` will show. ## Tree diff --git a/docs/revsets.md b/docs/revsets.md index 5014a7d8f..5b66709f9 100644 --- a/docs/revsets.md +++ b/docs/revsets.md @@ -18,9 +18,6 @@ ID or a Git ref pointing to them). ## Symbols -The symbol `root` refers to the virtual commit that is the oldest ancestor of -all other commits. - The `@` expression refers to the working copy commit in the current workspace. Use `@` to refer to the working-copy commit in another workspace. Use `@` to refer to a remote-tracking branch. @@ -41,11 +38,10 @@ Taking shell quoting into account, you may need to use something like Jujutsu attempts to resolve a symbol in the following order: -1. `root` -2. Tag name -3. Branch name -4. Git ref -5. Commit ID or change ID +1. Tag name +2. Branch name +3. Git ref +4. Commit ID or change ID ## Operators @@ -107,6 +103,7 @@ revsets (expressions) as arguments. * `git_head()`: The Git `HEAD` target as of the last import. Equivalent to `present(HEAD@git)`. * `visible_heads()`: All visible heads (same as `heads(all())`). +* `root()`: The virtual commit that is the oldest ancestor of all other commits. * `heads(x)`: Commits in `x` that are not ancestors of other commits in `x`. Note that this is different from [Mercurial's](https://repo.mercurial-scm.org/hg/help/revsets) `heads(x)` @@ -127,7 +124,7 @@ revsets (expressions) as arguments. * `committer(pattern)`: Commits with the given string in the committer's name or email. * `empty()`: Commits modifying no files. This also includes `merges()` without - user modifications and `root`. + user modifications and `root()`. * `file(pattern..)`: Commits modifying the paths specified by the `pattern..`. Paths are relative to the directory `jj` was invoked from. A directory name will match all files in that directory and its subdirectories. For example, @@ -186,7 +183,7 @@ jj log -r :@ Show the initial commits in the repo (the ones Git calls "root commits"): ``` -jj log -r root+ +jj log -r root()+ ``` Show some important commits (like `git --simplify-by-decoration`): diff --git a/docs/tutorial.md b/docs/tutorial.md index 7e93cda8f..f1097a98e 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -137,12 +137,12 @@ for context. The `~` indicates that the commit has parents that are not included in the graph. We can use the `-r` flag to select a different set of revisions to list. The flag accepts a ["revset"](revsets.md), which is an expression in a simple language for specifying revisions. For example, `@` -refers to the working-copy commit, `root` refers to the root commit, +refers to the working-copy commit, `root()` refers to the root commit, `branches()` refers to all commits pointed to by branches. We can combine expressions with `|` for union, `&` for intersection and `~` for difference. For example: ```shell -$ jj log -r '@ | root | branches()' +$ jj log -r '@ | root() | branches()' @ mpqrykypylvy martinvonz@google.com 2023-02-12 15:00:22.000 -08:00 aef4df99ea11 ╷ (empty) (no description set) ╷ ◉ kowxouwzwxmv octocat@nowhere.com 2014-06-10 15:22:26.000 -07:00 test b3cbd5bbd7e8 @@ -156,8 +156,8 @@ $ jj log -r '@ | root | branches()' ``` The `000000000000` commit (change ID `zzzzzzzzzzzz`) is a virtual commit that's -called the "root commit". It's the root commit of every repo. The `root` symbol -in the revset matches it. +called the "root commit". It's the root commit of every repo. The `root()` +function in the revset matches it. There are also operators for getting the parents (`foo-`), children (`foo+`), ancestors (`:foo`), descendants (`foo:`), DAG range (`foo:bar`, like diff --git a/lib/src/revset.rs b/lib/src/revset.rs index 8ab58dd80..ba8e46f90 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -256,6 +256,7 @@ pub enum RevsetCommitRef { remote: String, }, VisibleHeads, + Root, Branches(StringPattern), RemoteBranches { branch_pattern: StringPattern, @@ -363,6 +364,10 @@ impl RevsetExpression { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::VisibleHeads)) } + pub fn root() -> Rc { + Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Root)) + } + pub fn branches(pattern: StringPattern) -> Rc { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Branches( pattern, @@ -1078,6 +1083,10 @@ static BUILTIN_FUNCTION_MAP: Lazy> = Lazy: expect_no_arguments(name, arguments_pair)?; Ok(RevsetExpression::visible_heads()) }); + map.insert("root", |name, arguments_pair, _state| { + expect_no_arguments(name, arguments_pair)?; + Ok(RevsetExpression::root()) + }); map.insert("branches", |name, arguments_pair, state| { let ([], [opt_arg]) = expect_arguments(name, arguments_pair)?; let pattern = if let Some(arg) = opt_arg { @@ -1966,8 +1975,8 @@ impl SymbolResolver for FailingSymbolResolver { pub type PrefixResolver<'a, T> = Box PrefixResolution + 'a>; -/// Resolves the "root" symbol, branches, remote branches, tags, git -/// refs, and full and abbreviated commit and change ids. +/// Resolves branches, remote branches, tags, git refs, and full and abbreviated +/// commit and change ids. pub struct DefaultSymbolResolver<'a> { repo: &'a dyn Repo, commit_id_resolver: PrefixResolver<'a, CommitId>, @@ -2002,68 +2011,66 @@ impl<'a> DefaultSymbolResolver<'a> { impl SymbolResolver for DefaultSymbolResolver<'_> { fn resolve_symbol(&self, symbol: &str) -> Result, RevsetResolutionError> { - if symbol == "root" { - Ok(vec![self.repo.store().root_commit_id().clone()]) - } else if symbol.is_empty() { - Err(RevsetResolutionError::EmptyString) - } else { - // Try to resolve as a tag - let target = self.repo.view().get_tag(symbol); - if target.is_present() { - return Ok(target.added_ids().cloned().collect()); - } - - // Try to resolve as a branch - if let Some(ids) = resolve_local_branch(self.repo, symbol) { - return Ok(ids); - } - - // Try to resolve as a git ref - if let Some(ids) = resolve_git_ref(self.repo, symbol) { - return Ok(ids); - } - - // Try to resolve as a full commit id. - if let Some(ids) = resolve_full_commit_id(self.repo, symbol)? { - return Ok(ids); - } - - // Try to resolve as a commit id. - if let Some(prefix) = HexPrefix::new(symbol) { - match (self.commit_id_resolver)(self.repo, &prefix) { - PrefixResolution::AmbiguousMatch => { - return Err(RevsetResolutionError::AmbiguousCommitIdPrefix( - symbol.to_owned(), - )); - } - PrefixResolution::SingleMatch(id) => { - return Ok(vec![id]); - } - PrefixResolution::NoMatch => { - // Fall through - } - } - } - - // Try to resolve as a change id. - if let Some(prefix) = to_forward_hex(symbol).as_deref().and_then(HexPrefix::new) { - match (self.change_id_resolver)(self.repo, &prefix) { - PrefixResolution::AmbiguousMatch => { - return Err(RevsetResolutionError::AmbiguousChangeIdPrefix( - symbol.to_owned(), - )); - } - PrefixResolution::SingleMatch(ids) => { - return Ok(ids); - } - PrefixResolution::NoMatch => { - // Fall through - } - } - } - - Err(make_no_such_symbol_error(self.repo, symbol)) + if symbol.is_empty() { + return Err(RevsetResolutionError::EmptyString); } + + // Try to resolve as a tag + let target = self.repo.view().get_tag(symbol); + if target.is_present() { + return Ok(target.added_ids().cloned().collect()); + } + + // Try to resolve as a branch + if let Some(ids) = resolve_local_branch(self.repo, symbol) { + return Ok(ids); + } + + // Try to resolve as a git ref + if let Some(ids) = resolve_git_ref(self.repo, symbol) { + return Ok(ids); + } + + // Try to resolve as a full commit id. + if let Some(ids) = resolve_full_commit_id(self.repo, symbol)? { + return Ok(ids); + } + + // Try to resolve as a commit id. + if let Some(prefix) = HexPrefix::new(symbol) { + match (self.commit_id_resolver)(self.repo, &prefix) { + PrefixResolution::AmbiguousMatch => { + return Err(RevsetResolutionError::AmbiguousCommitIdPrefix( + symbol.to_owned(), + )); + } + PrefixResolution::SingleMatch(id) => { + return Ok(vec![id]); + } + PrefixResolution::NoMatch => { + // Fall through + } + } + } + + // Try to resolve as a change id. + if let Some(prefix) = to_forward_hex(symbol).as_deref().and_then(HexPrefix::new) { + match (self.change_id_resolver)(self.repo, &prefix) { + PrefixResolution::AmbiguousMatch => { + return Err(RevsetResolutionError::AmbiguousChangeIdPrefix( + symbol.to_owned(), + )); + } + PrefixResolution::SingleMatch(ids) => { + return Ok(ids); + } + PrefixResolution::NoMatch => { + // Fall through + } + } + } + + Err(make_no_such_symbol_error(self.repo, symbol)) } } @@ -2086,6 +2093,7 @@ fn resolve_commit_ref( } } RevsetCommitRef::VisibleHeads => Ok(repo.view().heads().iter().cloned().collect_vec()), + RevsetCommitRef::Root => Ok(vec![repo.store().root_commit_id().clone()]), RevsetCommitRef::Branches(pattern) => { let view = repo.view(); let commit_ids = filter_map_values_by_key_pattern(view.branches(), pattern) @@ -2995,6 +3003,11 @@ mod tests { message: "Expected 1 arguments".to_string() }) ); + assert_eq!( + parse("root()"), + Ok(Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Root))) + ); + assert!(parse("root(a)").is_err()); assert_eq!( parse(r#"description("")"#), Ok(RevsetExpression::filter( diff --git a/lib/tests/test_revset.rs b/lib/tests/test_revset.rs index 7a38a1854..ba40ee2ff 100644 --- a/lib/tests/test_revset.rs +++ b/lib/tests/test_revset.rs @@ -70,18 +70,6 @@ fn revset_for_commits<'index>( .unwrap() } -#[test_case(false ; "local backend")] -#[test_case(true ; "git backend")] -fn test_resolve_symbol_root(use_git: bool) { - let test_repo = TestRepo::init(use_git); - let repo = &test_repo.repo; - - assert_matches!( - resolve_symbol(repo.as_ref(), "root"), - Ok(v) if v == vec![repo.store().root_commit_id().clone()] - ); -} - #[test] fn test_resolve_symbol_empty_string() { let test_repo = TestRepo::init(true); @@ -738,16 +726,20 @@ fn test_resolve_symbol_git_refs() { vec![commit2.id().clone()] ); - // Cannot shadow root symbols + // "@" (quoted) can be resolved, and root is a normal symbol. let ws_id = WorkspaceId::default(); mut_repo .set_wc_commit(ws_id.clone(), commit1.id().clone()) .unwrap(); mut_repo.set_git_ref_target("@", RefTarget::normal(commit2.id().clone())); mut_repo.set_git_ref_target("root", RefTarget::normal(commit3.id().clone())); + assert_eq!( + resolve_symbol(mut_repo, r#""@""#).unwrap(), + vec![commit2.id().clone()] + ); assert_eq!( resolve_symbol(mut_repo, "root").unwrap(), - vec![mut_repo.store().root_commit().id().clone()] + vec![commit3.id().clone()] ); // Conflicted ref resolves to its "adds" @@ -812,7 +804,7 @@ fn test_evaluate_expression_root_and_checkout(use_git: bool) { // Can find the root commit assert_eq!( - resolve_commit_ids(mut_repo, "root"), + resolve_commit_ids(mut_repo, "root()"), vec![root_commit.id().clone()] ); @@ -847,7 +839,7 @@ fn test_evaluate_expression_heads(use_git: bool) { // Heads of the root is the root assert_eq!( - resolve_commit_ids(mut_repo, "heads(root)"), + resolve_commit_ids(mut_repo, "heads(root())"), vec![root_commit.id().clone()] ); @@ -912,7 +904,7 @@ fn test_evaluate_expression_roots(use_git: bool) { // Roots of the root is the root assert_eq!( - resolve_commit_ids(mut_repo, "roots(root)"), + resolve_commit_ids(mut_repo, "roots(root())"), vec![root_commit.id().clone()] ); @@ -966,7 +958,7 @@ fn test_evaluate_expression_parents(use_git: bool) { let commit5 = graph_builder.commit_with_parents(&[&commit2]); // The root commit has no parents - assert_eq!(resolve_commit_ids(mut_repo, "root-"), vec![]); + assert_eq!(resolve_commit_ids(mut_repo, "root()-"), vec![]); // Can find parents of the current working-copy commit mut_repo @@ -1065,7 +1057,7 @@ fn test_evaluate_expression_children(use_git: bool) { // Can find children of the root commit assert_eq!( - resolve_commit_ids(mut_repo, "root+"), + resolve_commit_ids(mut_repo, "root()+"), vec![commit1.id().clone()] ); @@ -1094,11 +1086,11 @@ fn test_evaluate_expression_children(use_git: bool) { // Can find children of children, which may be optimized to single query assert_eq!( - resolve_commit_ids(mut_repo, "root++"), + resolve_commit_ids(mut_repo, "root()++"), vec![commit4.id().clone(), commit2.id().clone()] ); assert_eq!( - resolve_commit_ids(mut_repo, &format!("(root | {})++", commit1.id().hex())), + resolve_commit_ids(mut_repo, &format!("(root() | {})++", commit1.id().hex())), vec![ commit5.id().clone(), commit4.id().clone(), @@ -1136,7 +1128,7 @@ fn test_evaluate_expression_ancestors(use_git: bool) { // The ancestors of the root commit is just the root commit itself assert_eq!( - resolve_commit_ids(mut_repo, ":root"), + resolve_commit_ids(mut_repo, ":root()"), vec![root_commit.id().clone()] ); @@ -1205,7 +1197,7 @@ fn test_evaluate_expression_range(use_git: bool) { // The range from the root to the root is empty (because the left side of the // range is exclusive) - assert_eq!(resolve_commit_ids(mut_repo, "root..root"), vec![]); + assert_eq!(resolve_commit_ids(mut_repo, "root()..root()"), vec![]); // Linear range assert_eq!( @@ -1267,7 +1259,7 @@ fn test_evaluate_expression_dag_range(use_git: bool) { // Can get DAG range of just the root commit assert_eq!( - resolve_commit_ids(mut_repo, "root:root"), + resolve_commit_ids(mut_repo, "root():root()"), vec![root_commit_id.clone()] ); @@ -1364,7 +1356,7 @@ fn test_evaluate_expression_connected(use_git: bool) { // Can connect just the root commit assert_eq!( - resolve_commit_ids(mut_repo, "connected(root)"), + resolve_commit_ids(mut_repo, "connected(root())"), vec![root_commit_id.clone()] ); @@ -1454,7 +1446,7 @@ fn test_evaluate_expression_descendants(use_git: bool) { // The descendants of the root commit are all the commits in the repo assert_eq!( - resolve_commit_ids(mut_repo, "root:"), + resolve_commit_ids(mut_repo, "root():"), vec![ commit6.id().clone(), commit5.id().clone(), @@ -1939,7 +1931,7 @@ fn test_evaluate_expression_latest(use_git: bool) { // Should not panic if count is larger than the candidates size assert_eq!( - resolve_commit_ids(mut_repo, "latest(~root, 5)"), + resolve_commit_ids(mut_repo, "latest(~root(), 5)"), vec![ commit4_t1.id().clone(), commit3_t2.id().clone(), @@ -2091,7 +2083,7 @@ fn test_evaluate_expression_author(use_git: bool) { assert_eq!( resolve_commit_ids( mut_repo, - &format!("root.. & (author(name1) | {})", commit3.id().hex()) + &format!("root().. & (author(name1) | {})", commit3.id().hex()) ), vec![commit3.id().clone(), commit1.id().clone()] ); @@ -2156,7 +2148,7 @@ fn test_evaluate_expression_mine(use_git: bool) { assert_eq!( resolve_commit_ids( mut_repo, - &format!("root.. & (mine() | {})", commit1.id().hex()) + &format!("root().. & (mine() | {})", commit1.id().hex()) ), vec![ commit3.id().clone(), @@ -2481,7 +2473,7 @@ fn test_evaluate_expression_filter_combinator(use_git: bool) { // Intersected with a set node assert_eq!( - resolve_commit_ids(mut_repo, "root.. & ~description(1)"), + resolve_commit_ids(mut_repo, "root().. & ~description(1)"), vec![commit3.id().clone(), commit2.id().clone()], ); assert_eq!(