Skip to content

Commit

Permalink
Implement advance-branches for jj new
Browse files Browse the repository at this point in the history
  • Loading branch information
emesterhazy committed Apr 9, 2024
1 parent 34d71b1 commit bf681cf
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 4 deletions.
29 changes: 26 additions & 3 deletions cli/src/commands/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ use jj_lib::revset::{RevsetExpression, RevsetIteratorExt};
use jj_lib::rewrite::{merge_commit_trees, rebase_commit};
use tracing::instrument;

use crate::cli_util::{short_commit_hash, CommandHelper, RevisionArg};
use crate::cli_util::{
short_commit_hash, AdvanceableBranch, CommandHelper, RevisionArg, WorkspaceCommandHelper,
};
use crate::command_error::{user_error, CommandError};
use crate::description_util::join_message_paragraphs;
use crate::ui::Ui;
Expand Down Expand Up @@ -99,6 +101,7 @@ Please use `jj new 'all:x|y'` instead of `jj new --allow-large-revsets x y`.",
.into_iter()
.collect_vec();
let target_ids = target_commits.iter().map(|c| c.id().clone()).collect_vec();
let advanceable_branches = get_advanceable_branches(args, &workspace_command, &target_commits)?;
let mut tx = workspace_command.start_transaction();
let mut num_rebased;
let new_commit;
Expand Down Expand Up @@ -135,11 +138,11 @@ Please use `jj new 'all:x|y'` instead of `jj new --allow-large-revsets x y`.",
.set_description(join_message_paragraphs(&args.message_paragraphs))
.write()?;
num_rebased = target_ids.len();
for child_commit in target_commits {
for child_commit in &target_commits {
rebase_commit(
command.settings(),
tx.mut_repo(),
&child_commit,
child_commit,
&[new_commit.clone()],
)?;
}
Expand Down Expand Up @@ -198,6 +201,26 @@ Please use `jj new 'all:x|y'` instead of `jj new --allow-large-revsets x y`.",
if num_rebased > 0 {
writeln!(ui.status(), "Rebased {num_rebased} descendant commits")?;
}

// Does nothing if there's no branches to advance.
tx.advance_branches(advanceable_branches, target_commits[0].id());

tx.finish(ui, "new empty commit")?;
Ok(())
}

// Branches are only advanced if `jj new` has a single target commit and the
// new commit is not being inserted before or after the target.
fn get_advanceable_branches(
args: &NewArgs,
ws: &WorkspaceCommandHelper,
target_commits: &[Commit],
) -> Result<Vec<AdvanceableBranch>, CommandError> {
let should_advance_branches =
target_commits.len() == 1 && !args.insert_before && !args.insert_after;
if should_advance_branches {
ws.get_advanceable_branches(target_commits[0].parent_ids())
} else {
Ok(Vec::new())
}
}
130 changes: 129 additions & 1 deletion cli/tests/test_advance_branches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn set_advance_branches(test_env: &TestEnvironment, enabled: bool) {
} else {
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = [""]
enabled-branches = []
"#,
);
}
Expand All @@ -51,8 +51,15 @@ 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"]);
Expand Down Expand Up @@ -100,6 +107,7 @@ fn test_advance_branches_enabled(make_commit: CommitFn) {
// 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"]);
Expand Down Expand Up @@ -141,6 +149,7 @@ fn test_advance_branches_at_minus(make_commit: CommitFn) {
// 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"]);
Expand Down Expand Up @@ -251,6 +260,7 @@ fn test_advance_branches_overrides(make_commit: CommitFn) {

// 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"]);
Expand Down Expand Up @@ -284,3 +294,121 @@ fn test_advance_branches_multiple_branches(make_commit: CommitFn) {
"###);
}
}

// 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");

// First, test with advance-branches enabled. Start by creating a branch on the
// root commit.
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");

// First, test with advance-branches enabled. Start by creating a branch on the
// root commit.
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");

// 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::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:
"###);
}

0 comments on commit bf681cf

Please sign in to comment.