From bc57754c5831813059f9707c26d48f92476d45c3 Mon Sep 17 00:00:00 2001 From: Vamsi Avula Date: Sat, 12 Aug 2023 10:19:27 +0000 Subject: [PATCH] cli: add support for setting default description That is, jj will use ui.default_description as a starting point when user is about to describe an empty change. I think it might be confusing to do this with -m / --stdin (violates WYSIWYG), so I'm only doing this when jj invokes an editor. Also, this could evolve into a proper template in the future instead of just plain text, to allow inheriting from parent change(s), for example. Partially addresses #1354. --- CHANGELOG.md | 6 +++++- cli/src/commands/mod.rs | 22 +++++++++++++------- cli/src/config-schema.json | 7 ++++++- cli/tests/test_commit_command.rs | 32 ++++++++++++++++++++++++++++++ cli/tests/test_describe_command.rs | 31 +++++++++++++++++++++++++++++ docs/config.md | 16 ++++++++++++--- 6 files changed, 102 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2608997e..66d6ee9e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,11 +59,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `-m/--message` arguments. Each passed message will be combined into paragraphs (separated by a blank line) +* It is now possible to set a default description using the new + `ui.default-description` option, to use when describing changes with an empty + description. + ### Fixed bugs * SSH authentication could hang when ssh-agent couldn't be reached [#1970](https://github.com/martinvonz/jj/issues/1970) - + * SSH authentication can now use ed25519 and ed25519-sk keys. They still need to be password-less. diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index a9701b16e..d48438a35 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -2015,7 +2015,7 @@ fn cmd_describe( } else if args.no_edit { commit.description().to_owned() } else { - let template = description_template_for_commit(ui, &workspace_command, &commit)?; + let template = description_template_for_commit(ui, command, &workspace_command, &commit)?; edit_description(workspace_command.repo(), &template, command.settings())? }; if description == *commit.description() && !args.reset_author { @@ -2048,7 +2048,7 @@ fn cmd_commit(ui: &mut Ui, command: &CommandHelper, args: &CommitArgs) -> Result let description = if !args.message_paragraphs.is_empty() { cli_util::join_message_paragraphs(&args.message_paragraphs) } else { - let template = description_template_for_commit(ui, &workspace_command, &commit)?; + let template = description_template_for_commit(ui, command, &workspace_command, &commit)?; edit_description(workspace_command.repo(), &template, command.settings())? }; @@ -2991,6 +2991,7 @@ don't make any changes, then the operation will be aborted.", fn description_template_for_commit( ui: &Ui, + command: &CommandHelper, workspace_command: &WorkspaceCommandHelper, commit: &Commit, ) -> Result { @@ -3003,12 +3004,19 @@ fn description_template_for_commit( &EverythingMatcher, &[DiffFormat::Summary], )?; - if diff_summary_bytes.is_empty() { - Ok(commit.description().to_owned()) + let description = if commit.description().is_empty() { + command + .settings() + .config() + .get_string("ui.default-description") + .unwrap_or("".to_owned()) } else { - Ok(commit.description().to_owned() - + "\n" - + &diff_summary_to_description(&diff_summary_bytes)) + commit.description().to_owned() + }; + if diff_summary_bytes.is_empty() { + Ok(description) + } else { + Ok(description + "\n" + &diff_summary_to_description(&diff_summary_bytes)) } } diff --git a/cli/src/config-schema.json b/cli/src/config-schema.json index 6fbdebc44..d0569b680 100644 --- a/cli/src/config-schema.json +++ b/cli/src/config-schema.json @@ -46,6 +46,11 @@ "description": "Default command to run when no explicit command is given", "default": "log" }, + "default-description": { + "type": "string", + "description": "Default description to use when describing changes with an empty description", + "default": "" + }, "color": { "description": "Whether to colorize command output", "enum": [ @@ -62,7 +67,7 @@ "never", "auto" ], - "default": "auto" + "default": "auto" }, "pager": { "type": "string", diff --git a/cli/tests/test_commit_command.rs b/cli/tests/test_commit_command.rs index 0cdba0a6a..32b398525 100644 --- a/cli/tests/test_commit_command.rs +++ b/cli/tests/test_commit_command.rs @@ -75,6 +75,38 @@ fn test_commit_with_editor() { "###); } +#[test] +fn test_commit_with_default_description() { + let mut test_env = TestEnvironment::default(); + test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); + test_env.add_config(r#"ui.default-description = "\n\nTESTED=TODO""#); + let workspace_path = test_env.env_root().join("repo"); + + std::fs::write(workspace_path.join("file1"), "foo\n").unwrap(); + std::fs::write(workspace_path.join("file2"), "bar\n").unwrap(); + let edit_script = test_env.set_up_fake_editor(); + std::fs::write(&edit_script, ["dump editor"].join("\0")).unwrap(); + test_env.jj_cmd_success(&workspace_path, &["commit"]); + + insta::assert_snapshot!(get_log_output(&test_env, &workspace_path), @r#" + @ 8dc0591d00f7 + ◉ 7e780ba80aeb TESTED=TODO + ◉ 000000000000 + "#); + assert_eq!( + std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), + r#" + +TESTED=TODO +JJ: This commit contains the following changes: +JJ: A file1 +JJ: A file2 + +JJ: Lines starting with "JJ: " (like this one) will be removed. +"# + ); +} + #[test] fn test_commit_without_working_copy() { let test_env = TestEnvironment::default(); diff --git a/cli/tests/test_describe_command.rs b/cli/tests/test_describe_command.rs index d49d74370..c5d9a765b 100644 --- a/cli/tests/test_describe_command.rs +++ b/cli/tests/test_describe_command.rs @@ -234,6 +234,37 @@ fn test_multiple_message_args() { "###); } +#[test] +fn test_describe_default_description() { + let mut test_env = TestEnvironment::default(); + test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); + test_env.add_config(r#"ui.default-description = "\n\nTESTED=TODO""#); + let workspace_path = test_env.env_root().join("repo"); + + std::fs::write(workspace_path.join("file1"), "foo\n").unwrap(); + std::fs::write(workspace_path.join("file2"), "bar\n").unwrap(); + let edit_script = test_env.set_up_fake_editor(); + std::fs::write(&edit_script, ["dump editor"].join("\0")).unwrap(); + let stdout = test_env.jj_cmd_success(&workspace_path, &["describe"]); + + insta::assert_snapshot!(stdout, @r#" + Working copy now at: qpvuntsm 7e780ba8 TESTED=TODO + Parent commit : zzzzzzzz 00000000 (empty) (no description set) + "#); + assert_eq!( + std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), + r#" + +TESTED=TODO +JJ: This commit contains the following changes: +JJ: A file1 +JJ: A file2 + +JJ: Lines starting with "JJ: " (like this one) will be removed. +"# + ); +} + #[test] fn test_describe_author() { let test_env = TestEnvironment::default(); diff --git a/docs/config.md b/docs/config.md index 8a800c943..c192b71aa 100644 --- a/docs/config.md +++ b/docs/config.md @@ -135,6 +135,16 @@ subcommand name, subcommand alias, or user-defined alias (defaults to `"log"`). ui.default-command = "log" ``` +### Default description + +The value of the `ui.default-description` setting will be used to prepopulate +the editor when describing changes with an empty description. This could be a +useful reminder to fill in things like BUG=, TESTED= etc. + +```toml +ui.default-description = "\n\nTESTED=TODO" +``` + ### Diff format ```toml @@ -179,13 +189,13 @@ revsets.log = "main.." ```toml # Possible values: "curved" (default), "square", "ascii", "ascii-large", -# "legacy" +# "legacy" ui.graph.style = "square" ``` ### Wrap log content -If enabled, `log`/`obslog`/`op log` content will be wrapped based on +If enabled, `log`/`obslog`/`op log` content will be wrapped based on the terminal width. ```toml @@ -274,7 +284,7 @@ a `$`): Additionally, paging behavior can be toggled via `ui.paginate` like so: ```toml -# Enable pagination for commands that support it (default) +# Enable pagination for commands that support it (default) ui.paginate = "auto" # Disable all pagination, equivalent to using --no-pager ui.paginate = "never"