diff --git a/CHANGELOG.md b/CHANGELOG.md index f16fb610d..9d2b41c3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking changes +* A default revset-alias function `trunk()` now exists. If you previously defined + your own `trunk()` alias it will continue to overwrite the built-in one. + Check [revsets.toml](cli/src/config/revsets.toml) and [revsets.md](docs/revset.md) + to understand how the function can be adapted. + ### New features * The `ancestors()` revset function now takes an optional `depth` argument diff --git a/cli/src/config.rs b/cli/src/config.rs index b2c22a06c..2060605f9 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -347,6 +347,7 @@ pub fn default_config() -> config::Config { .add_source(from_toml!("config/colors.toml")) .add_source(from_toml!("config/merge_tools.toml")) .add_source(from_toml!("config/misc.toml")) + .add_source(from_toml!("config/revsets.toml")) .add_source(from_toml!("config/templates.toml")); if cfg!(unix) { builder = builder.add_source(from_toml!("config/unix.toml")) diff --git a/cli/src/config/misc.toml b/cli/src/config/misc.toml index a68824134..ab831753a 100644 --- a/cli/src/config/misc.toml +++ b/cli/src/config/misc.toml @@ -1,9 +1,6 @@ [aliases] # Placeholder: added by user -[revset-aliases] -# Placeholder: added by user - [ui] paginate = "auto" pager = { command = ["less", "-FRX"], env = { LESSCHARSET = "utf-8" } } diff --git a/cli/src/config/revsets.toml b/cli/src/config/revsets.toml new file mode 100644 index 000000000..e735fe0d7 --- /dev/null +++ b/cli/src/config/revsets.toml @@ -0,0 +1,12 @@ +# NOTE: ensure you update docs/revsets.md with documentation when +# adding/updating any of these aliases + +[revset-aliases] +'trunk()' = ''' +latest( + remote_branches(exact:"main", exact:"origin") | + remote_branches(exact:"master", exact:"origin") | + remote_branches(exact:"trunk", exact:"origin") | + root() +) +''' diff --git a/cli/tests/test_builtin_aliases.rs b/cli/tests/test_builtin_aliases.rs new file mode 100644 index 000000000..2b1ce7d84 --- /dev/null +++ b/cli/tests/test_builtin_aliases.rs @@ -0,0 +1,139 @@ +// Copyright 2023 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::path::PathBuf; + +use common::TestEnvironment; + +pub mod common; + +fn set_up(trunk_name: &str) -> (TestEnvironment, PathBuf) { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_success(test_env.env_root(), &["init", "--git", "origin"]); + let origin_path = test_env.env_root().join("origin"); + let origin_git_repo_path = origin_path + .join(".jj") + .join("repo") + .join("store") + .join("git"); + + test_env.jj_cmd_success(&origin_path, &["describe", "-m=description 1"]); + test_env.jj_cmd_success(&origin_path, &["branch", "create", trunk_name]); + test_env.jj_cmd_success(&origin_path, &["new", "root()", "-m=description 2"]); + test_env.jj_cmd_success(&origin_path, &["branch", "create", "unrelated_branch"]); + test_env.jj_cmd_success(&origin_path, &["git", "export"]); + + test_env.jj_cmd_success( + test_env.env_root(), + &[ + "git", + "clone", + origin_git_repo_path.to_str().unwrap(), + "local", + ], + ); + let workspace_root = test_env.env_root().join("local"); + (test_env, workspace_root) +} + +#[test] +fn test_builtin_alias_trunk_matches_main() { + let (test_env, workspace_root) = set_up("main"); + + let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "trunk()"]); + insta::assert_snapshot!(stdout, @r###" + ◉ lzmmnrxq test.user@example.com 2001-02-03 04:05:08.000 +07:00 main 45a3aa29 + │ (empty) description 1 + ~ + "###); +} + +#[test] +fn test_builtin_alias_trunk_matches_master() { + let (test_env, workspace_root) = set_up("master"); + + let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "trunk()"]); + insta::assert_snapshot!(stdout, @r###" + ◉ lzmmnrxq test.user@example.com 2001-02-03 04:05:08.000 +07:00 master 45a3aa29 + │ (empty) description 1 + ~ + "###); +} + +#[test] +fn test_builtin_alias_trunk_matches_trunk() { + let (test_env, workspace_root) = set_up("trunk"); + + let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "trunk()"]); + insta::assert_snapshot!(stdout, @r###" + ◉ lzmmnrxq test.user@example.com 2001-02-03 04:05:08.000 +07:00 trunk 45a3aa29 + │ (empty) description 1 + ~ + "###); +} + +#[test] +fn test_builtin_alias_trunk_matches_exactly_one_commit() { + let (test_env, workspace_root) = set_up("main"); + let origin_path = test_env.env_root().join("origin"); + test_env.jj_cmd_success(&origin_path, &["new", "root()", "-m=description 3"]); + test_env.jj_cmd_success(&origin_path, &["branch", "create", "master"]); + + let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "trunk()"]); + insta::assert_snapshot!(stdout, @r###" + ◉ lzmmnrxq test.user@example.com 2001-02-03 04:05:08.000 +07:00 main 45a3aa29 + │ (empty) description 1 + ~ + "###); +} + +#[test] +fn test_builtin_alias_trunk_override_alias() { + let (test_env, workspace_root) = set_up("override-trunk"); + + test_env.add_config( + r#"revset-aliases.'trunk()' = 'latest(remote_branches(exact:"override-trunk", exact:"origin"))'"#, + ); + + let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "trunk()"]); + insta::assert_snapshot!(stdout, @r###" + ◉ lzmmnrxq test.user@example.com 2001-02-03 04:05:08.000 +07:00 override-trunk 45a3aa29 + │ (empty) description 1 + ~ + "###); +} + +#[test] +fn test_builtin_alias_trunk_no_match() { + let (test_env, workspace_root) = set_up("no-match-trunk"); + + let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["log", "-r", "trunk()"]); + insta::assert_snapshot!(stdout, @r###" + ◉ zzzzzzzz root() 00000000 + "###); + insta::assert_snapshot!(stderr, @r###" + "###); +} + +#[test] +fn test_builtin_alias_trunk_no_match_only_exact() { + let (test_env, workspace_root) = set_up("maint"); + + let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["log", "-r", "trunk()"]); + insta::assert_snapshot!(stdout, @r###" + ◉ zzzzzzzz root() 00000000 + "###); + insta::assert_snapshot!(stderr, @r###" + "###); +} diff --git a/docs/revsets.md b/docs/revsets.md index c8101552c..b7f543ae6 100644 --- a/docs/revsets.md +++ b/docs/revsets.md @@ -159,6 +159,25 @@ For example: 'user(x)' = 'author(x) | committer(x)' ``` +### Built-in Aliases + +The following aliases are built-in and used for certain operations. These functions +are defined as aliases in order to allow you to overwrite them as needed. +See [revsets.toml](https://github.com/martinvonz/jj/blob/main/cli/src/config/revsets.toml) +for a comprehensive list. + +* `trunk()`: Resolves to the head commit for the trunk branch of the `origin` + remote. The branches `main`, `master`, and `trunk` are tried in order. + If none of the branches exist, it evaluates to `root()`. + + You can [override](./config.md) this as appropriate. If you do, make sure it + always resolves to exactly one commit. For example: + + ```toml + [revset-aliases] + 'trunk()' = 'latest(remote_branches(exact:"your-branch", exact:"your-remote"))' + ``` + ## Examples Show the parent(s) of the working-copy commit (like `git log -1 HEAD`):