126 lines · 4.2 KB · Rust
RawDownload
use std::collections::HashSet; use clap_complete::ArgValueCompleter;use itertools::Itertools as _;use jj_lib::backend::BackendError; 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 = ArgValueCompleter::new(complete::revset_expression_mutable),    )]    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 = ArgValueCompleter::new(complete::revset_expression_mutable),    )]    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))?            .resolve()?    } else {        workspace_command            .parse_union_revsets(ui, &args.source)?            .resolve()?            .descendants()            .union(                &workspace_command                    .parse_union_revsets(ui, &args.revisions)?                    .resolve()?,            )    };    workspace_command.check_rewritable_expr(&revs)?;    let commit_ids: Vec<_> = revs        .evaluate(workspace_command.repo().as_ref())?        .iter()        .try_collect()?;    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, async |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()        && 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(())}