130 lines · 4.4 KB · Rust
RawDownload
use std::collections::HashSet; use clap_complete::ArgValueCandidates;use itertools::Itertools as _;use jj_lib::backend::BackendError;use jj_lib::revset::RevsetExpression; use crate::cli_util::CommandHelper;use crate::cli_util::RevisionArg;use crate::command_error::CommandError;use crate::complete;use crate::ui::Ui; /// Simplify parent edges for the specified revision(s).////// Removes all parents of each of the specified revisions that are also/// indirect ancestors of the same revisions through other parents. This has no/// effect on any revision's contents, including the working copy.////// In other words, for all (A, B, C) where A has (B, C) as parents and C is an/// ancestor of B, A will be rewritten to have only B as a parent instead of/// B+C.#[derive(clap::Args, Clone, Debug)]pub(crate) struct SimplifyParentsArgs {    /// Simplify specified revision(s) together with their trees of descendants    /// (can be repeated)    #[arg(        long,        short,        value_name = "REVSETS",        add = ArgValueCandidates::new(complete::mutable_revisions)    )]    source: Vec<RevisionArg>,    /// Simplify specified revision(s) (can be repeated)    ///    /// If both `--source` and `--revisions` are not provided, this defaults to    /// the `revsets.simplify-parents` setting, or `reachable(@, mutable())`    /// if it is not set.    #[arg(        long,        short,        value_name = "REVSETS",        add = ArgValueCandidates::new(complete::mutable_revisions)    )]    revisions: Vec<RevisionArg>,} pub(crate) fn cmd_simplify_parents(    ui: &mut Ui,    command: &CommandHelper,    args: &SimplifyParentsArgs,) -> Result<(), CommandError> {    let mut workspace_command = command.workspace_helper(ui)?;    let revs = if args.source.is_empty() && args.revisions.is_empty() {        let revs = workspace_command            .settings()            .get_string("revsets.simplify-parents")?;        workspace_command            .parse_revset(ui, &RevisionArg::from(revs))?            .expression()            .clone()    } else {        RevsetExpression::descendants(            workspace_command                .parse_union_revsets(ui, &args.source)?                .expression(),        )        .union(            workspace_command                .parse_union_revsets(ui, &args.revisions)?                .expression(),        )    };    let commit_ids: Vec<_> = workspace_command        .attach_revset_evaluator(revs)        .evaluate_to_commit_ids()?        .try_collect()?;    workspace_command.check_rewritable(&commit_ids)?;    let commit_ids_set: HashSet<_> = commit_ids.iter().cloned().collect();    let num_orig_commits = commit_ids.len();     let mut tx = workspace_command.start_transaction();    let mut simplified_commits = 0;    let mut edges = 0;    let mut reparented_descendants = 0;     tx.repo_mut()        .transform_descendants(commit_ids, |mut rewriter| {            let num_old_heads = rewriter.new_parents().len();            if commit_ids_set.contains(rewriter.old_commit().id()) && num_old_heads > 1 {                // TODO: BackendError is not the right error here because                // the error does not come from `Backend`, but `Index`.                rewriter                    .simplify_ancestor_merge()                    .map_err(|err| BackendError::Other(err.into()))?;            }            let num_new_heads = rewriter.new_parents().len();             if rewriter.parents_changed() {                rewriter.reparent().write()?;                 if num_new_heads < num_old_heads {                    simplified_commits += 1;                    edges += num_old_heads - num_new_heads;                } else {                    reparented_descendants += 1;                }            }            Ok(())        })?;     if let Some(mut formatter) = ui.status_formatter() {        if simplified_commits > 0 {            writeln!(                formatter,                "Removed {edges} edges from {simplified_commits} out of {num_orig_commits} \                 commits.",            )?;            if reparented_descendants > 0 {                writeln!(                    formatter,                    "Rebased {reparented_descendants} descendant commits",                )?;            }        }    }    tx.finish(ui, format!("simplify {num_orig_commits} commits"))?;     Ok(())}