From 5a9bacd108f595f784219f1bdc754e36067b8964 Mon Sep 17 00:00:00 2001 From: Evan Mesterhazy Date: Tue, 2 Apr 2024 11:19:12 -0400 Subject: [PATCH] Implement --drop-message for jj squash If `-d/--drop-message` is passed to `jj squash`, the descriptions of the commits being squashed are dropped and the message of the destination commit is used. --- CHANGELOG.md | 3 +++ cli/src/commands/move.rs | 4 +-- cli/src/commands/squash.rs | 40 ++++++++++++++++++++++++----- cli/tests/cli-reference@.md.snap | 4 +++ cli/tests/test_squash_command.rs | 44 ++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f64bce337b..431d421990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 It can thereby be for all use cases where `jj move` can be used. The `--from` argument accepts a revset that resolves to more than one revision. +* `jj squash` now accepts a `--drop-message/-d` option that discards the + descriptions of the commits being squashed. + * Commit templates now support `immutable` keyword. * New template function `coalesce(content, ..)` is added. diff --git a/cli/src/commands/move.rs b/cli/src/commands/move.rs index 1599f2cc2a..54b7982c8c 100644 --- a/cli/src/commands/move.rs +++ b/cli/src/commands/move.rs @@ -16,7 +16,7 @@ use clap::ArgGroup; use jj_lib::object_id::ObjectId; use tracing::instrument; -use super::squash::move_diff; +use super::squash::{move_diff, SquashedDescription}; use crate::cli_util::{CommandHelper, RevisionArg}; use crate::command_error::{user_error, CommandError}; use crate::ui::Ui; @@ -93,7 +93,7 @@ pub(crate) fn cmd_move( &destination, matcher.as_ref(), &diff_selector, - None, + SquashedDescription::Combine, false, &args.paths, )?; diff --git a/cli/src/commands/squash.rs b/cli/src/commands/squash.rs index 484116498d..7a1e306382 100644 --- a/cli/src/commands/squash.rs +++ b/cli/src/commands/squash.rs @@ -62,6 +62,9 @@ pub(crate) struct SquashArgs { /// The description to use for squashed revision (don't open editor) #[arg(long = "message", short, value_name = "MESSAGE")] message_paragraphs: Vec, + /// Discard the descriptions of the squashed revisions. + #[arg(long, short)] + drop_message: bool, /// Interactively choose which parts to squash #[arg(long, short)] interactive: bool, @@ -112,8 +115,6 @@ pub(crate) fn cmd_squash( workspace_command.diff_selector(ui, args.tool.as_deref(), args.interactive)?; let mut tx = workspace_command.start_transaction(); let tx_description = format!("squash commits into {}", destination.id().hex()); - let description = (!args.message_paragraphs.is_empty()) - .then(|| join_message_paragraphs(&args.message_paragraphs)); move_diff( ui, &mut tx, @@ -122,7 +123,7 @@ pub(crate) fn cmd_squash( &destination, matcher.as_ref(), &diff_selector, - description, + SquashedDescription::from_args(args), args.revision.is_none() && args.from.is_none() && args.into.is_none(), &args.paths, )?; @@ -130,6 +131,30 @@ pub(crate) fn cmd_squash( Ok(()) } +// TODO(#2882): Remove public visibility once `jj move` is deleted. +pub(crate) enum SquashedDescription { + // Use this exact description. + Exact(String), + // Use the destination's description and discard the descriptions of the + // source revisions. + UseDestination, + // Combine the descriptions of the source and destination revisions. + Combine, +} + +// TODO(#2882): Remove public visibility once `jj move` is deleted. +impl SquashedDescription { + pub(crate) fn from_args(args: &SquashArgs) -> Self { + if !args.message_paragraphs.is_empty() { + SquashedDescription::Exact(join_message_paragraphs(&args.message_paragraphs)) + } else if args.drop_message { + SquashedDescription::UseDestination + } else { + SquashedDescription::Combine + } + } +} + #[allow(clippy::too_many_arguments)] pub fn move_diff( ui: &mut Ui, @@ -139,7 +164,7 @@ pub fn move_diff( destination: &Commit, matcher: &dyn Matcher, diff_selector: &DiffSelector, - description: Option, + description: SquashedDescription, no_rev_arg: bool, path_arg: &[String], ) -> Result<(), CommandError> { @@ -229,8 +254,11 @@ from the source will be moved into the destination. destination_tree = destination_tree.merge(&tree1, &tree2)?; } let description = match description { - Some(description) => description, - None => combine_messages(tx.base_repo(), &abandoned_commits, destination, settings)?, + SquashedDescription::Exact(description) => description, + SquashedDescription::UseDestination => destination.description().to_owned(), + SquashedDescription::Combine => { + combine_messages(tx.base_repo(), &abandoned_commits, destination, settings)? + } }; let mut predecessors = vec![destination.id().clone()]; predecessors.extend(sources.iter().map(|source| source.id().clone())); diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 4957f73fb4..54bff44f53 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -1723,6 +1723,10 @@ If a working-copy commit gets abandoned, it will be given a new, empty commit. T * `--from ` — Revision to squash from (default: @) * `--into ` — Revision to squash into (default: @) * `-m`, `--message ` — The description to use for squashed revision (don't open editor) +* `-d`, `--discard-message` — Discard the descriptions of the squashed revisions + + Possible values: `true`, `false` + * `-i`, `--interactive` — Interactively choose which parts to squash Possible values: `true`, `false` diff --git a/cli/tests/test_squash_command.rs b/cli/tests/test_squash_command.rs index 0156f88778..83e93c4dbb 100644 --- a/cli/tests/test_squash_command.rs +++ b/cli/tests/test_squash_command.rs @@ -968,9 +968,53 @@ fn test_squash_empty() { "###); } +#[test] +fn test_squash_drop_messages() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); + let repo_path = test_env.env_root().join("repo"); + + test_env.jj_cmd_ok(&repo_path, &["commit", "-m=a"]); + test_env.jj_cmd_ok(&repo_path, &["commit", "-m=b"]); + test_env.jj_cmd_ok(&repo_path, &["describe", "-m=c"]); + // Test the setup + insta::assert_snapshot!(get_log_output_with_description(&test_env, &repo_path), @r###" + @ 71f7c810d8ed c + ◉ 10dd87c3b4e2 b + ◉ 4c5b3042d9e0 a + ◉ 000000000000 + "###); + + // Squash the current revision using the short name for the option. + test_env.jj_cmd_ok(&repo_path, &["squash", "-d"]); + insta::assert_snapshot!(get_log_output_with_description(&test_env, &repo_path), @r###" + @ 10e30ce4a910 + ◉ 1c21278b775f b + ◉ 4c5b3042d9e0 a + ◉ 000000000000 + "###); + + // Undo and squash again, but this time squash both "b" and "c" into "a". + test_env.jj_cmd_ok(&repo_path, &["undo"]); + test_env.jj_cmd_ok( + &repo_path, + &["squash", "-d", "--from", "10d::", "--into", "4c5"], + ); + insta::assert_snapshot!(get_log_output_with_description(&test_env, &repo_path), @r###" + @ da1507508bdf + ◉ f1387f804776 a + ◉ 000000000000 + "###); +} + fn get_description(test_env: &TestEnvironment, repo_path: &Path, rev: &str) -> String { test_env.jj_cmd_success( repo_path, &["log", "--no-graph", "-T", "description", "-r", rev], ) } + +fn get_log_output_with_description(test_env: &TestEnvironment, repo_path: &Path) -> String { + let template = r#"separate(" ", commit_id.short(), description)"#; + test_env.jj_cmd_success(repo_path, &["log", "-T", template]) +}