mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-01 00:50:57 +00:00
Add a --use-destination-message option to jj squash
if `--use-destination-message/-u` is passed to `jj squash`, the resulting revision will use the description of the destination revision and the description(s) of the source revision(s) will be discarded.
This commit is contained in:
parent
0525dc9d86
commit
0ef25bb4b6
5 changed files with 124 additions and 8 deletions
|
@ -40,6 +40,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
* Operation objects in templates now have a `snapshot() -> Boolean` method that
|
||||
evaluates to true if the operation was a snapshot created by a non-mutating
|
||||
command (e.g. `jj log`).
|
||||
|
||||
* `jj squash` now accepts a `--use-destination-message/-u` option that uses the
|
||||
description of the destination for the new squashed revision and discards the
|
||||
descriptions of the source revisions.
|
||||
|
||||
* You can check whether Watchman fsmonitor is enabled or installed with the new
|
||||
`jj debug watchman status` command.
|
||||
|
|
|
@ -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;
|
||||
|
@ -97,7 +97,7 @@ pub(crate) fn cmd_move(
|
|||
&destination,
|
||||
matcher.as_ref(),
|
||||
&diff_selector,
|
||||
None,
|
||||
SquashedDescription::Combine,
|
||||
false,
|
||||
&args.paths,
|
||||
)?;
|
||||
|
|
|
@ -62,6 +62,10 @@ 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<String>,
|
||||
/// Use the description of the destination revision and discard the
|
||||
/// description(s) of the source revision(s)
|
||||
#[arg(long, short, conflicts_with = "message_paragraphs")]
|
||||
use_destination_message: bool,
|
||||
/// Interactively choose which parts to squash
|
||||
#[arg(long, short)]
|
||||
interactive: bool,
|
||||
|
@ -118,8 +122,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,
|
||||
|
@ -128,7 +130,7 @@ pub(crate) fn cmd_squash(
|
|||
&destination,
|
||||
matcher.as_ref(),
|
||||
&diff_selector,
|
||||
description,
|
||||
SquashedDescription::from_args(args),
|
||||
args.revision.is_none() && args.from.is_empty() && args.into.is_none(),
|
||||
&args.paths,
|
||||
)?;
|
||||
|
@ -136,6 +138,34 @@ 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 {
|
||||
// These options are incompatible and Clap is configured to prevent this.
|
||||
assert!(args.message_paragraphs.is_empty() || !args.use_destination_message);
|
||||
|
||||
if !args.message_paragraphs.is_empty() {
|
||||
let desc = join_message_paragraphs(&args.message_paragraphs);
|
||||
SquashedDescription::Exact(desc)
|
||||
} else if args.use_destination_message {
|
||||
SquashedDescription::UseDestination
|
||||
} else {
|
||||
SquashedDescription::Combine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn move_diff(
|
||||
ui: &mut Ui,
|
||||
|
@ -145,7 +175,7 @@ pub fn move_diff(
|
|||
destination: &Commit,
|
||||
matcher: &dyn Matcher,
|
||||
diff_selector: &DiffSelector,
|
||||
description: Option<String>,
|
||||
description: SquashedDescription,
|
||||
no_rev_arg: bool,
|
||||
path_arg: &[String],
|
||||
) -> Result<(), CommandError> {
|
||||
|
@ -235,8 +265,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()));
|
||||
|
|
|
@ -1770,6 +1770,10 @@ If a working-copy commit gets abandoned, it will be given a new, empty commit. T
|
|||
* `--from <FROM>` — Revision(s) to squash from (default: @)
|
||||
* `--into <INTO>` — Revision to squash into (default: @)
|
||||
* `-m`, `--message <MESSAGE>` — The description to use for squashed revision (don't open editor)
|
||||
* `-u`, `--use-destination-message` — Use the description of the destination revision and discard the description(s) of the source revision(s)
|
||||
|
||||
Possible values: `true`, `false`
|
||||
|
||||
* `-i`, `--interactive` — Interactively choose which parts to squash
|
||||
|
||||
Possible values: `true`, `false`
|
||||
|
|
|
@ -977,9 +977,84 @@ fn test_squash_empty() {
|
|||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_squash_use_destination_message() {
|
||||
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", "-u"]);
|
||||
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",
|
||||
"--use-destination-message",
|
||||
"--from",
|
||||
"description(b)::",
|
||||
"--into",
|
||||
"description(a)",
|
||||
],
|
||||
);
|
||||
insta::assert_snapshot!(get_log_output_with_description(&test_env, &repo_path), @r###"
|
||||
@ da1507508bdf
|
||||
◉ f1387f804776 a
|
||||
◉ 000000000000
|
||||
"###);
|
||||
}
|
||||
|
||||
// The --use-destination-message and --message options are incompatible.
|
||||
#[test]
|
||||
fn test_squash_use_destination_message_and_message_mutual_exclusion() {
|
||||
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, &["describe", "-m=b"]);
|
||||
insta::assert_snapshot!(test_env.jj_cmd_cli_error(
|
||||
&repo_path,
|
||||
&[
|
||||
"squash",
|
||||
"--message=123",
|
||||
"--use-destination-message",
|
||||
],
|
||||
), @r###"
|
||||
error: the argument '--message <MESSAGE>' cannot be used with '--use-destination-message'
|
||||
|
||||
Usage: jj squash --message <MESSAGE> [PATHS]...
|
||||
|
||||
For more information, try '--help'.
|
||||
"###);
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue