From 7108cd8edbc135f1828cb90965b0df2d3c7590f5 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 1 Feb 2023 13:09:11 +0100 Subject: [PATCH] jj new --insert --- src/commands/mod.rs | 86 ++++++++++++++++++++++++++++----------- tests/test_new_command.rs | 40 ++++++++++++++++++ 2 files changed, 103 insertions(+), 23 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 274ad8b1680..fd689ea10c4 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -438,6 +438,7 @@ struct EditArgs { /// For more information, see /// https://github.com/martinvonz/jj/blob/main/docs/working-copy.md. #[derive(clap::Args, Clone, Debug)] +#[command(group(ArgGroup::new("order").args(&["rebase_children", "insert"])))] struct NewArgs { /// Parent(s) of the new change #[arg(default_value = "@")] @@ -451,6 +452,9 @@ struct NewArgs { /// Rebase the children of the parents onto the new change #[arg(long, short = 'C')] rebase_children: bool, + /// Insert the commit before the given changes + #[arg(long, short = 'I')] + insert: bool, } /// Move changes from one revision into another @@ -1978,38 +1982,74 @@ fn cmd_new(ui: &mut Ui, command: &CommandHelper, args: &NewArgs) -> Result<(), C let commits = resolve_base_revs(&workspace_command, &args.revisions)?; let parent_ids = commits.iter().map(|c| c.id().clone()).collect::>(); let mut tx = workspace_command.start_transaction("new empty commit"); - let merged_tree = merge_commit_trees(tx.base_repo().as_repo_ref(), &commits); - let new_commit = tx - .mut_repo() - .new_commit( - command.settings(), - parent_ids.clone(), - merged_tree.id().clone(), - ) - .set_description(&args.message) - .write()?; let mut num_rebased = 0; - if args.rebase_children { - // Replace parents in all children by the new commit - let old_parents = RevsetExpression::commits(parent_ids); - let children = tx + let new_commit; + if args.insert { + // Instead of having the new commit as a child of the + // changes given on the command line, add it between + // the changes parents and the changes. + let grandparents = tx .base_workspace_helper() - .evaluate_revset(&old_parents.children())? + .evaluate_revset(&RevsetExpression::commits(parent_ids.clone()).parents())? .iter() .commits(tx.base_repo().store()) .collect::, _>>()?; - num_rebased = children.len(); - for child in children { - let commit_parents = RevsetExpression::commits(child.parent_ids().to_owned()); - let new_parents = commit_parents.minus(&old_parents); - let mut new_parents = tx + let merged_tree = merge_commit_trees(tx.base_repo().as_repo_ref(), &grandparents); + let grandparent_ids = grandparents.iter().map(|c| c.id().clone()).collect(); + new_commit = tx + .mut_repo() + .new_commit( + command.settings(), + grandparent_ids, + merged_tree.id().clone(), + ) + .set_description(&args.message) + .write()?; + num_rebased = parent_ids.len(); + for parent_id in parent_ids { + let parent_commit = tx .base_workspace_helper() - .evaluate_revset(&new_parents)? + .resolve_single_rev(&parent_id.hex())?; + rebase_commit( + command.settings(), + tx.mut_repo(), + &parent_commit, + &[new_commit.clone()], + )?; + } + } else { + let merged_tree = merge_commit_trees(tx.base_repo().as_repo_ref(), &commits); + new_commit = tx + .mut_repo() + .new_commit( + command.settings(), + parent_ids.clone(), + merged_tree.id().clone(), + ) + .set_description(&args.message) + .write()?; + if args.rebase_children { + // Replace parents in all children by the new commit + let old_parents = RevsetExpression::commits(parent_ids); + let children = tx + .base_workspace_helper() + .evaluate_revset(&old_parents.children())? .iter() .commits(tx.base_repo().store()) .collect::, _>>()?; - new_parents.push(new_commit.clone()); - rebase_commit(command.settings(), tx.mut_repo(), &child, &new_parents)?; + num_rebased = children.len(); + for child in children { + let commit_parents = RevsetExpression::commits(child.parent_ids().to_owned()); + let new_parents = commit_parents.minus(&old_parents); + let mut new_parents = tx + .base_workspace_helper() + .evaluate_revset(&new_parents)? + .iter() + .commits(tx.base_repo().store()) + .collect::, _>>()?; + new_parents.push(new_commit.clone()); + rebase_commit(command.settings(), tx.mut_repo(), &child, &new_parents)?; + } } } if num_rebased > 0 { diff --git a/tests/test_new_command.rs b/tests/test_new_command.rs index 8f203fba8ed..a51693068e1 100644 --- a/tests/test_new_command.rs +++ b/tests/test_new_command.rs @@ -151,6 +151,46 @@ fn test_new_rebase_children() { "###); } +#[test] +fn test_new_insert() { + 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"); + setup_before_insertion(&test_env, &repo_path); + insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" + @ F + |\ + o | E + | o D + |/ + | o C + | o B + | o A + |/ + o root + "###); + + let stdout = test_env.jj_cmd_success(&repo_path, &["new", "--insert", "-m", "G", "C", "F"]); + insta::assert_snapshot!(stdout, @r###" + Rebased 2 descendant commits + Working copy now at: ff6bbbc7b8df G + "###); + insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" + o F + | o C + |/ + @-. G + |\ \ + o | | E + | o | D + |/ / + | o B + | o A + |/ + o root + "###); +} + fn setup_before_insertion(test_env: &TestEnvironment, repo_path: &Path) { test_env.jj_cmd_success(repo_path, &["branch", "create", "A"]); test_env.jj_cmd_success(repo_path, &["commit", "-m", "A"]);