use std::collections::HashMap;
use clap_complete::ArgValueCompleter;
use futures::TryStreamExt as _;
use indexmap::IndexSet;
use itertools::Itertools as _;
use jj_lib::backend::CommitId;
use jj_lib::commit::Commit;
use jj_lib::commit::CommitIteratorExt as _;
use tracing::instrument;
use crate::cli_util::CommandHelper;
use crate::cli_util::RevisionArg;
use crate::command_error::CommandError;
use crate::complete;
use crate::ui::Ui;
#[derive(clap::Args, Clone, Debug)]
#[command(verbatim_doc_comment)]
pub(crate) struct ParallelizeArgs {
#[arg(value_name = "REVSETS")]
#[arg(add = ArgValueCompleter::new(complete::revset_expression_mutable))]
revisions_pos: Vec<RevisionArg>,
#[arg(short = 'r', hide = true, value_name = "REVSETS")]
#[arg(add = ArgValueCompleter::new(complete::revset_expression_mutable))]
revisions_opt: Vec<RevisionArg>,
}
#[instrument(skip_all)]
pub(crate) async fn cmd_parallelize(
ui: &mut Ui,
command: &CommandHelper,
args: &ParallelizeArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let target_commits: Vec<Commit> = workspace_command
.parse_union_revsets(ui, &[&*args.revisions_pos, &*args.revisions_opt].concat())?
.evaluate_to_commits()?
.try_collect()
.await?;
let mut new_target_parents: HashMap<CommitId, Vec<CommitId>> = HashMap::new();
let mut needs_rewrite = Vec::new();
for commit in target_commits.iter().rev() {
let mut new_parents = vec![];
for old_parent in commit.parent_ids() {
if let Some(grand_parents) = new_target_parents.get(old_parent) {
new_parents.extend_from_slice(grand_parents);
needs_rewrite.push(commit.id());
} else {
new_parents.push(old_parent.clone());
}
}
new_target_parents.insert(commit.id().clone(), new_parents);
}
workspace_command.check_rewritable(needs_rewrite).await?;
let mut tx = workspace_command.start_transaction();
let mut new_child_parents: HashMap<CommitId, IndexSet<CommitId>> = HashMap::new();
for commit in target_commits.iter().rev() {
let mut new_parents = IndexSet::new();
for old_parent in commit.parent_ids() {
if let Some(parents) = new_child_parents.get(old_parent) {
new_parents.extend(parents.iter().cloned());
}
}
new_parents.insert(commit.id().clone());
new_child_parents.insert(commit.id().clone(), new_parents);
}
tx.repo_mut()
.transform_descendants(
target_commits.iter().ids().cloned().collect_vec(),
async |mut rewriter| {
if let Some(new_parents) = new_target_parents.get(rewriter.old_commit().id()) {
rewriter.set_new_rewritten_parents(new_parents);
} else if rewriter
.old_commit()
.parent_ids()
.iter()
.any(|id| new_child_parents.contains_key(id))
{
let mut new_parents = vec![];
for parent in rewriter.old_commit().parent_ids() {
if let Some(parents) = new_child_parents.get(parent) {
new_parents.extend(parents.iter().cloned());
} else {
new_parents.push(parent.clone());
}
}
rewriter.set_new_rewritten_parents(&new_parents);
}
if rewriter.parents_changed() {
let builder = rewriter.rebase().await?;
builder.write().await?;
}
Ok(())
},
)
.await?;
tx.finish(ui, format!("parallelize {} commits", target_commits.len()))
.await
}