forked from mirrors/jj
cmd: Allow multiple -s
for jj rebase
This addresses a portion of #1158 One application (not the prettiest, but useful until we have `jj sync`): jj rebase -s oldmain+~:main -d main -L
This commit is contained in:
parent
39dd1a99c1
commit
f04458a245
3 changed files with 114 additions and 14 deletions
|
@ -16,6 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
* `jj restore` without `--from` works correctly even if `@` is a merge
|
* `jj restore` without `--from` works correctly even if `@` is a merge
|
||||||
commit.
|
commit.
|
||||||
|
|
||||||
|
* `jj rebase` now accepts multiple `-s` arguments. Revsets with multiple commits
|
||||||
|
are allowed with `--allow-large-revsets`.
|
||||||
|
|
||||||
### Fixed bugs
|
### Fixed bugs
|
||||||
|
|
||||||
* Modify/delete conflicts now include context lines
|
* Modify/delete conflicts now include context lines
|
||||||
|
|
|
@ -733,9 +733,15 @@ struct RebaseArgs {
|
||||||
/// Rebase the whole branch (relative to destination's ancestors)
|
/// Rebase the whole branch (relative to destination's ancestors)
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
branch: Option<RevisionArg>,
|
branch: Option<RevisionArg>,
|
||||||
/// Rebase this revision and its descendants
|
|
||||||
|
/// Rebase specified revision(s) together their tree of descendants (can be
|
||||||
|
/// repeated)
|
||||||
|
///
|
||||||
|
/// Each specified revision will become a direct child of the destination
|
||||||
|
/// revision(s), even if some of the source revisions are descendants
|
||||||
|
/// of others.
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
source: Option<RevisionArg>,
|
source: Vec<RevisionArg>,
|
||||||
/// Rebase only this revision, rebasing descendants onto this revision's
|
/// Rebase only this revision, rebasing descendants onto this revision's
|
||||||
/// parent(s)
|
/// parent(s)
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
|
@ -2762,13 +2768,18 @@ fn cmd_rebase(ui: &mut Ui, command: &CommandHelper, args: &RebaseArgs) -> Result
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
if let Some(rev_str) = &args.revision {
|
if let Some(rev_str) = &args.revision {
|
||||||
rebase_revision(ui, command, &mut workspace_command, &new_parents, rev_str)?;
|
rebase_revision(ui, command, &mut workspace_command, &new_parents, rev_str)?;
|
||||||
} else if let Some(source_str) = &args.source {
|
} else if !args.source.is_empty() {
|
||||||
|
let source_commits = resolve_mutliple_nonempty_revsets_flag_guarded(
|
||||||
|
&workspace_command,
|
||||||
|
&args.source,
|
||||||
|
args.allow_large_revsets,
|
||||||
|
)?;
|
||||||
rebase_descendants(
|
rebase_descendants(
|
||||||
ui,
|
ui,
|
||||||
command,
|
command,
|
||||||
&mut workspace_command,
|
&mut workspace_command,
|
||||||
&new_parents,
|
&new_parents,
|
||||||
source_str,
|
&source_commits,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
let branch_str = args.branch.as_deref().unwrap_or("@");
|
let branch_str = args.branch.as_deref().unwrap_or("@");
|
||||||
|
@ -2825,17 +2836,27 @@ fn rebase_descendants(
|
||||||
command: &CommandHelper,
|
command: &CommandHelper,
|
||||||
workspace_command: &mut WorkspaceCommandHelper,
|
workspace_command: &mut WorkspaceCommandHelper,
|
||||||
new_parents: &[Commit],
|
new_parents: &[Commit],
|
||||||
source_str: &str,
|
old_commits: &IndexSet<Commit>,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
let old_commit = workspace_command.resolve_single_rev(source_str)?;
|
for old_commit in old_commits.iter() {
|
||||||
workspace_command.check_rewritable(&old_commit)?;
|
workspace_command.check_rewritable(old_commit)?;
|
||||||
check_rebase_destinations(workspace_command.repo(), new_parents, &old_commit)?;
|
check_rebase_destinations(workspace_command.repo(), new_parents, old_commit)?;
|
||||||
let mut tx = workspace_command.start_transaction(&format!(
|
}
|
||||||
|
let tx_message = if old_commits.len() == 1 {
|
||||||
|
format!(
|
||||||
"rebase commit {} and descendants",
|
"rebase commit {} and descendants",
|
||||||
old_commit.id().hex()
|
old_commits.first().unwrap().id().hex()
|
||||||
));
|
)
|
||||||
rebase_commit(command.settings(), tx.mut_repo(), &old_commit, new_parents)?;
|
} else {
|
||||||
let num_rebased = tx.mut_repo().rebase_descendants(command.settings())? + 1;
|
format!("rebase {} commits and their descendants", old_commits.len())
|
||||||
|
};
|
||||||
|
let mut tx = workspace_command.start_transaction(&tx_message);
|
||||||
|
// `rebase_descendants` takes care of sorting in reverse topological order, so
|
||||||
|
// no need to do it here.
|
||||||
|
for old_commit in old_commits {
|
||||||
|
rebase_commit(command.settings(), tx.mut_repo(), old_commit, new_parents)?;
|
||||||
|
}
|
||||||
|
let num_rebased = old_commits.len() + tx.mut_repo().rebase_descendants(command.settings())?;
|
||||||
writeln!(ui, "Rebased {num_rebased} commits")?;
|
writeln!(ui, "Rebased {num_rebased} commits")?;
|
||||||
tx.finish(ui)?;
|
tx.finish(ui)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -426,6 +426,82 @@ fn test_rebase_with_descendants() {
|
||||||
o a
|
o a
|
||||||
o
|
o
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
|
// Rebase several subtrees at once.
|
||||||
|
test_env.jj_cmd_success(&repo_path, &["undo"]);
|
||||||
|
let stdout = test_env.jj_cmd_success(&repo_path, &["rebase", "-s=c", "-s=d", "-d=a"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
Rebased 2 commits
|
||||||
|
Working copy now at: 92c2bc9a8623 d
|
||||||
|
Added 0 files, modified 0 files, removed 2 files
|
||||||
|
"###);
|
||||||
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||||
|
@ d
|
||||||
|
│ o c
|
||||||
|
├─╯
|
||||||
|
│ o b
|
||||||
|
o │ a
|
||||||
|
├─╯
|
||||||
|
o
|
||||||
|
"###);
|
||||||
|
|
||||||
|
test_env.jj_cmd_success(&repo_path, &["undo"]);
|
||||||
|
// Reminder of the setup
|
||||||
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||||
|
@ d
|
||||||
|
o c
|
||||||
|
├─╮
|
||||||
|
o │ b
|
||||||
|
│ o a
|
||||||
|
├─╯
|
||||||
|
o
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// `d` was a descendant of `b`, and both are moved to be direct descendants of
|
||||||
|
// `a`. `c` remains a descendant of `b`.
|
||||||
|
let stdout = test_env.jj_cmd_success(&repo_path, &["rebase", "-s=b", "-s=d", "-d=a"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
Rebased 3 commits
|
||||||
|
Working copy now at: f1e71cb78a06 d
|
||||||
|
Added 0 files, modified 0 files, removed 2 files
|
||||||
|
"###);
|
||||||
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||||
|
o c
|
||||||
|
│ @ d
|
||||||
|
o │ b
|
||||||
|
├─╯
|
||||||
|
o a
|
||||||
|
o
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Same test as above, but with duplicate commits and multiple commits per
|
||||||
|
// argument
|
||||||
|
test_env.jj_cmd_success(&repo_path, &["undo"]);
|
||||||
|
let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-s=b|d", "-s=d", "-d=a"]);
|
||||||
|
insta::assert_snapshot!(stderr, @r###"
|
||||||
|
Error: Revset "b|d" resolved to more than one revision
|
||||||
|
Hint: The revset "b|d" resolved to these revisions:
|
||||||
|
df54a9fd85ae d
|
||||||
|
d370aee184ba b
|
||||||
|
If this was intentional, specify the `--allow-large-revsets` argument
|
||||||
|
"###);
|
||||||
|
let stdout = test_env.jj_cmd_success(
|
||||||
|
&repo_path,
|
||||||
|
&["rebase", "-s=b|d", "-s=d", "-d=a", "--allow-large-revsets"],
|
||||||
|
);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
Rebased 3 commits
|
||||||
|
Working copy now at: d17539f7ea7c d
|
||||||
|
Added 0 files, modified 0 files, removed 2 files
|
||||||
|
"###);
|
||||||
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||||
|
o c
|
||||||
|
o b
|
||||||
|
│ @ d
|
||||||
|
├─╯
|
||||||
|
o a
|
||||||
|
o
|
||||||
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
|
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
|
||||||
|
|
Loading…
Reference in a new issue