From e0210367405449adf1f5da95ed438b1e04fdf789 Mon Sep 17 00:00:00 2001 From: Benjamin Tan Date: Fri, 26 Apr 2024 21:56:55 +0800 Subject: [PATCH] cli: backout: add `templates.backout_description` configuration Closes #5676. --- CHANGELOG.md | 3 +++ cli/src/commands/backout.rs | 43 +++++++++++++++++++++---------- cli/src/config/templates.toml | 8 ++++++ cli/tests/cli-reference@.md.snap | 6 +++-- cli/tests/test_backout_command.rs | 40 ++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22ec3841a..46c58d43b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). useful for making machine-readable templates by escaping problematic characters like `\n`. +* The description of commits backed out by `jj backout` can now be configured + using `templates.backout_description`. + ### Fixed bugs * `jj status` now shows untracked files under untracked directories. diff --git a/cli/src/commands/backout.rs b/cli/src/commands/backout.rs index f5c49a8ee..230866ffc 100644 --- a/cli/src/commands/backout.rs +++ b/cli/src/commands/backout.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use bstr::ByteVec as _; use clap_complete::ArgValueCandidates; use itertools::Itertools as _; use jj_lib::object_id::ObjectId; @@ -22,9 +23,13 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; use crate::complete; +use crate::formatter::PlainTextFormatter; use crate::ui::Ui; -/// Apply the reverse of a revision on top of another revision +/// Apply the reverse of given revisions on top of another revision +/// +/// The description of the new revisions can be customized with the +/// `templates.backout_description` config variable. #[derive(clap::Args, Clone, Debug)] pub(crate) struct BackoutArgs { /// The revision(s) to apply the reverse of @@ -67,7 +72,6 @@ pub(crate) fn cmd_backout( let destination = workspace_command.resolve_single_rev(ui, revision_str)?; parents.push(destination); } - let mut tx = workspace_command.start_transaction(); let transaction_description = if to_back_out.len() == 1 { format!("back out commit {}", to_back_out[0].id().hex()) } else { @@ -77,18 +81,31 @@ pub(crate) fn cmd_backout( to_back_out.len() - 1 ) }; + let commits_to_back_out_with_new_commit_descriptions = { + let template_text = command + .settings() + .get_string("templates.backout_description")?; + let template = workspace_command.parse_commit_template(ui, &template_text)?; + + to_back_out + .into_iter() + .map(|commit| { + let mut output = Vec::new(); + template + .format(&commit, &mut PlainTextFormatter::new(&mut output)) + .expect("write() to vec backed formatter should never fail"); + // Template output is usually UTF-8, but it can contain file content. + let commit_description = output.into_string_lossy(); + (commit, commit_description) + }) + .collect_vec() + }; + let mut tx = workspace_command.start_transaction(); let mut new_base_tree = merge_commit_trees(tx.repo(), &parents)?; - for commit_to_back_out in to_back_out { - let commit_to_back_out_subject = commit_to_back_out - .description() - .lines() - .next() - .unwrap_or_default(); - let new_commit_description = format!( - "Back out \"{}\"\n\nThis backs out commit {}.\n", - commit_to_back_out_subject, - &commit_to_back_out.id().hex() - ); + + for (commit_to_back_out, new_commit_description) in + commits_to_back_out_with_new_commit_descriptions + { let old_base_tree = commit_to_back_out.parent_tree(tx.repo())?; let old_tree = commit_to_back_out.tree()?; let new_tree = new_base_tree.merge(&old_tree, &old_base_tree)?; diff --git a/cli/src/config/templates.toml b/cli/src/config/templates.toml index 92408f25e..be8968e17 100644 --- a/cli/src/config/templates.toml +++ b/cli/src/config/templates.toml @@ -1,4 +1,12 @@ [templates] +backout_description = ''' +concat( + 'Back out "' ++ description.first_line() ++ '"' ++ "\n", + "\n", + "This backs out commit " ++ commit_id ++ ".\n", +) +''' + bookmark_list = ''' if(remote, if(tracked, diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 1a46be0e6..2cb4f7cdd 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -122,7 +122,7 @@ To get started, see the tutorial at https://jj-vcs.github.io/jj/latest/tutorial/ * `abandon` — Abandon a revision * `absorb` — Move changes from a revision into the stack of mutable revisions -* `backout` — Apply the reverse of a revision on top of another revision +* `backout` — Apply the reverse of given revisions on top of another revision * `bookmark` — Manage bookmarks [default alias: b] * `commit` — Update the description and create a new change on top * `config` — Manage config options @@ -259,7 +259,9 @@ The modification made by `jj absorb` can be reviewed by `jj op show -p`. ## `jj backout` -Apply the reverse of a revision on top of another revision +Apply the reverse of given revisions on top of another revision + +The description of the new revisions can be customized with the `templates.backout_description` config variable. **Usage:** `jj backout [OPTIONS]` diff --git a/cli/tests/test_backout_command.rs b/cli/tests/test_backout_command.rs index b71da4ac9..39fffa7d2 100644 --- a/cli/tests/test_backout_command.rs +++ b/cli/tests/test_backout_command.rs @@ -188,6 +188,46 @@ fn test_backout_multiple() { "#); } +#[test] +fn test_backout_description_template() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]); + test_env.add_config( + r#" + [templates] + backout_description = ''' + separate(" ", + "Revert commit", + commit_id.short(), + '"' ++ description.first_line() ++ '"', + ) + ''' + "#, + ); + let repo_path = test_env.env_root().join("repo"); + create_commit(&test_env, &repo_path, "a", &[], &[("a", "a\n")]); + + // Test the setup + insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#" + @ 2443ea76b0b1 a + ◆ 000000000000 + "#); + let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); + insta::assert_snapshot!(stdout, @r###" + A a + "###); + + // Verify that message of backed out commit follows the template + let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["backout", "-r", "a"]); + insta::assert_snapshot!(stdout, @""); + insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#" + ○ 1db880a5204e Revert commit 2443ea76b0b1 "a" + @ 2443ea76b0b1 a + ◆ 000000000000 + "#); +} + fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String { let template = r#"commit_id.short() ++ " " ++ description"#; test_env.jj_cmd_success(cwd, &["log", "-T", template])