jj/cli/tests/test_advance_branches.rs
2024-04-20 10:26:04 -04:00

448 lines
16 KiB
Rust

// Copyright 2024 The Jujutsu Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::path::Path;
use test_case::test_case;
use crate::common::TestEnvironment;
fn get_log_output_with_branches(test_env: &TestEnvironment, cwd: &Path) -> String {
// Don't include commit IDs since they will be different depending on
// whether the test runs with `jj commit` or `jj describe` + `jj new`.
let template = r#""branches{" ++ local_branches ++ "} desc: " ++ description"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}
fn set_advance_branches(test_env: &TestEnvironment, enabled: bool) {
if enabled {
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = ["glob:*"]
"#,
);
} else {
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = []
"#,
);
}
}
// Runs a command in the specified test environment and workspace path that
// describes the current commit with `commit_message` and creates a new commit
// on top of it.
type CommitFn = fn(env: &TestEnvironment, workspace_path: &Path, commit_message: &str);
// Implements CommitFn using the `jj commit` command.
fn commit_cmd(env: &TestEnvironment, workspace_path: &Path, commit_message: &str) {
env.jj_cmd_ok(workspace_path, &["commit", "-m", commit_message]);
}
// Implements CommitFn using the `jj describe` and `jj new`.
fn describe_new_cmd(env: &TestEnvironment, workspace_path: &Path, commit_message: &str) {
env.jj_cmd_ok(workspace_path, &["describe", "-m", commit_message]);
env.jj_cmd_ok(workspace_path, &["new"]);
}
// Check that enabling and disabling advance-branches works as expected.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_branches_enabled(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
// First, test with advance-branches enabled. Start by creating a branch on the
// root commit.
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "test_branch"],
);
// Check the initial state of the repo.
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{test_branch} desc:
"###);
}
// Run jj commit, which will advance the branch pointing to @-.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{test_branch} desc: first
◉ branches{} desc:
"###);
}
// Now disable advance branches and commit again. The branch shouldn't move.
set_advance_branches(&test_env, false);
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{} desc: second
◉ branches{test_branch} desc: first
◉ branches{} desc:
"###);
}
}
// Check that only a branch pointing to @- advances. Branches pointing to @ are
// not advanced.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_branches_at_minus(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(&workspace_path, &["branch", "create", "test_branch"]);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{test_branch} desc:
◉ branches{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{test_branch} desc: first
◉ branches{} desc:
"###);
}
// Create a second branch pointing to @. On the next commit, only the first
// branch, which points to @-, will advance.
test_env.jj_cmd_ok(&workspace_path, &["branch", "create", "test_branch2"]);
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{test_branch test_branch2} desc: second
◉ branches{} desc: first
◉ branches{} desc:
"###);
}
}
// Test that per-branch overrides invert the behavior of
// experimental-advance-branches.enabled.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_branches_overrides(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
// advance-branches is disabled by default.
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "test_branch"],
);
// Check the initial state of the repo.
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{test_branch} desc:
"###);
}
// Commit will not advance the branch since advance-branches is disabled.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{} desc: first
◉ branches{test_branch} desc:
"###);
}
// Now enable advance branches for "test_branch", move the branch, and commit
// again.
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = ["test_branch"]
"#,
);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "set", "test_branch", "-r", "@-"],
);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{test_branch} desc: first
◉ branches{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{test_branch} desc: second
◉ branches{} desc: first
◉ branches{} desc:
"###);
}
// Now disable advance branches for "test_branch" and "second_branch", which
// we will use later. Disabling always takes precedence over enabling.
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = ["test_branch", "second_branch"]
disabled-branches = ["test_branch"]
"#,
);
make_commit(&test_env, &workspace_path, "third");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{} desc: third
◉ branches{test_branch} desc: second
◉ branches{} desc: first
◉ branches{} desc:
"###);
}
// If we create a new branch at @- and move test_branch there as well. When
// we commit, only "second_branch" will advance since "test_branch" is disabled.
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "second_branch", "-r", "@-"],
);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "set", "test_branch", "-r", "@-"],
);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{second_branch test_branch} desc: third
◉ branches{} desc: second
◉ branches{} desc: first
◉ branches{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "fourth");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{second_branch} desc: fourth
◉ branches{test_branch} desc: third
◉ branches{} desc: second
◉ branches{} desc: first
◉ branches{} desc:
"###);
}
}
// If multiple eligible branches point to @-, all of them will be advanced.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_branches_multiple_branches(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "first_branch"],
);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "second_branch"],
);
insta::allow_duplicates! {
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{first_branch second_branch} desc:
"###);
}
// Both branches are eligible and both will advance.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{first_branch second_branch} desc: first
◉ branches{} desc:
"###);
}
}
// Call `jj new` on an interior commit and see that the branch pointing to its
// parent's parent is advanced.
#[test]
fn test_new_advance_branches_interior() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{} desc:
"###);
// Create a gap in the commits for us to insert our new commit with --before.
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "second"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "third"]);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@---", "test_branch"],
);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{} desc: third
◉ branches{} desc: second
◉ branches{test_branch} desc: first
◉ branches{} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["new", "-r", "@--"]);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
│ ◉ branches{} desc: third
├─╯
◉ branches{test_branch} desc: second
◉ branches{} desc: first
◉ branches{} desc:
"###);
}
// If the `--before` flag is passed to `jj new`, branches are not advanced.
#[test]
fn test_new_advance_branches_before() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{} desc:
"###);
// Create a gap in the commits for us to insert our new commit with --before.
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "second"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "third"]);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@---", "test_branch"],
);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{} desc: third
◉ branches{} desc: second
◉ branches{test_branch} desc: first
◉ branches{} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["new", "--before", "-r", "@-"]);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
◉ branches{} desc: third
@ branches{} desc:
◉ branches{} desc: second
◉ branches{test_branch} desc: first
◉ branches{} desc:
"###);
}
// If the `--after` flag is passed to `jj new`, branches are not advanced.
#[test]
fn test_new_advance_branches_after() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "test_branch"],
);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{test_branch} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["describe", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "--after"]);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
◉ branches{} desc: first
◉ branches{test_branch} desc:
"###);
}
#[test]
fn test_new_advance_branches_merge_children() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(&workspace_path, &["desc", "-m", "0"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "-m", "1"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "description(0)", "-m", "2"]);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "test_branch", "-r", "description(0)"],
);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc: 2
│ ◉ branches{} desc: 1
├─╯
◉ branches{test_branch} desc: 0
◉ branches{} desc:
"###);
// The branch won't advance because `jj new` had multiple targets.
test_env.jj_cmd_ok(
&workspace_path,
&["new", "description(1)", "description(2)"],
);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
├─╮
│ ◉ branches{} desc: 2
◉ │ branches{} desc: 1
├─╯
◉ branches{test_branch} desc: 0
◉ branches{} desc:
"###);
}