ok/jj
1
0
Fork 0
forked from mirrors/jj

jj new --insert-after

This commit is contained in:
Samuel Tardieu 2023-02-04 21:42:03 +01:00
parent 4119aa44a9
commit 605a39b84f
3 changed files with 154 additions and 1 deletions

View file

@ -133,6 +133,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `jj new --insert-before` inserts the new commit between the target commit and
its parents.
* `jj new --insert-after` inserts the new commit between the target commit and
its children.
### Fixed bugs
* When sharing the working copy with a Git repo, we used to forget to export

View file

@ -446,6 +446,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(&["insert_after", "insert_before"])))]
struct NewArgs {
/// Parent(s) of the new change
#[arg(default_value = "@")]
@ -459,6 +460,9 @@ struct NewArgs {
/// Allow revsets expanding to multiple commits in a single argument
#[arg(long, short = 'L')]
allow_large_revsets: bool,
/// Insert the new change between the target commit(s) and their children
#[arg(long, short = 'A', visible_alias = "after")]
insert_after: bool,
/// Insert the new change between the target commit(s) and their parents
#[arg(long, short = 'B', visible_alias = "before")]
insert_before: bool,
@ -2097,9 +2101,45 @@ fn cmd_new(ui: &mut Ui, command: &CommandHelper, args: &NewArgs) -> Result<(), C
let merged_tree = merge_commit_trees(tx.base_repo().as_repo_ref(), &target_commits);
new_commit = tx
.mut_repo()
.new_commit(command.settings(), target_ids, merged_tree.id().clone())
.new_commit(
command.settings(),
target_ids.clone(),
merged_tree.id().clone(),
)
.set_description(&args.message)
.write()?;
if args.insert_after {
// Each child of the targets will be rebased: its set of parents will be updated
// so that the targets are replaced by the new commit.
let old_parents = RevsetExpression::commits(target_ids);
// Exclude children that are ancestors of the new commit
let to_rebase = old_parents.children().minus(&old_parents.ancestors());
let commits_to_rebase: Vec<Commit> = tx
.base_workspace_helper()
.evaluate_revset(&to_rebase)?
.iter()
.commits(tx.base_repo().store())
.try_collect()?;
num_rebased = commits_to_rebase.len();
for child_commit in commits_to_rebase {
let commit_parents =
RevsetExpression::commits(child_commit.parent_ids().to_owned());
let new_parents = commit_parents.minus(&old_parents);
let mut new_parent_commits: Vec<Commit> = tx
.base_workspace_helper()
.evaluate_revset(&new_parents)?
.iter()
.commits(tx.base_repo().store())
.try_collect()?;
new_parent_commits.push(new_commit.clone());
rebase_commit(
command.settings(),
tx.mut_repo(),
&child_commit,
&new_parent_commits,
)?;
}
}
}
num_rebased += tx.mut_repo().rebase_descendants(command.settings())?;
if num_rebased > 0 {

View file

@ -106,6 +106,116 @@ fn test_new_merge() {
"###);
}
#[test]
fn test_new_insert_after() {
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-after", "-m", "G", "B", "D"]);
insta::assert_snapshot!(stdout, @r###"
Rebased 2 descendant commits
Working copy now at: ca7c6481a8dd G
"###);
insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###"
o C
| o F
| |\
|/ /
@ | G
|\ \
| | o E
o | | D
| |/
|/|
| o B
| o A
|/
o root
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["new", "--insert-after", "-m", "H", "D"]);
insta::assert_snapshot!(stdout, @r###"
Rebased 3 descendant commits
Working copy now at: fcf8281b4135 H
"###);
insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###"
o C
| o F
| |\
|/ /
o | G
|\ \
@ | | H
| | o E
o | | D
| |/
|/|
| o B
| o A
|/
o root
"###);
}
#[test]
fn test_new_insert_after_children() {
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
"###);
// Check that inserting G after A and C doesn't try to rebase B (which is
// initially a child of A) onto G as that would create a cycle since B is
// a parent of C which is a parent G.
let stdout =
test_env.jj_cmd_success(&repo_path, &["new", "--insert-after", "-m", "G", "A", "C"]);
insta::assert_snapshot!(stdout, @r###"
Working copy now at: b48d4d73a39c G
"###);
insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###"
@ G
|\
| | o F
| | |\
| | o | E
| | | o D
| | |/
o | | C
o | | B
|/ /
o | A
|/
o root
"###);
}
#[test]
fn test_new_insert_before() {
let test_env = TestEnvironment::default();