cli: normalize line ending of edited commit description, ensure last newline

Since CR+LF vs LF things shouldn't matter in commit description, it's probably
better to normalize newline characters.

In Mercurial, ui.edit() and changelog.stripdesc() handle line normalization,
and trailing newlines are stripped. In Git, cleanup_message() handles that,
and the last newline is added after stripping trailing newlines.
This commit is contained in:
Yuya Nishihara 2022-12-21 10:51:44 +09:00
parent 6f8fb09609
commit 300f744e41
5 changed files with 49 additions and 17 deletions

View file

@ -1376,7 +1376,7 @@ impl AsRef<str> for DescriptionArg {
}
}
fn complete_newline(s: &mut String) {
pub fn complete_newline(s: &mut String) {
if !s.is_empty() && !s.ends_with('\n') {
s.push('\n');
}

View file

@ -51,10 +51,10 @@ use maplit::{hashmap, hashset};
use pest::Parser;
use crate::cli_util::{
check_stale_working_copy, print_checkout_stats, print_failed_git_export, resolve_base_revs,
short_commit_description, short_commit_hash, user_error, user_error_with_hint,
write_commit_summary, Args, CommandError, CommandHelper, DescriptionArg, RevisionArg,
WorkspaceCommandHelper,
self, check_stale_working_copy, print_checkout_stats, print_failed_git_export,
resolve_base_revs, short_commit_description, short_commit_hash, user_error,
user_error_with_hint, write_commit_summary, Args, CommandError, CommandHelper, DescriptionArg,
RevisionArg, WorkspaceCommandHelper,
};
use crate::config::FullCommandArgs;
use crate::diff_util::{self, DiffFormat, DiffFormatArgs};
@ -1853,15 +1853,14 @@ fn edit_description(
// Delete the file only if everything went well.
// TODO: Tell the user the name of the file we left behind.
std::fs::remove_file(description_file_path).ok();
let mut lines = description
.split_inclusive('\n')
// Normalize line ending, remove trailing blank lines.
let mut description = description
.lines()
.filter(|line| !line.starts_with("JJ: "))
.collect_vec();
// Remove trailing blank lines
while matches!(lines.last(), Some(&"\n") | Some(&"\r\n")) {
lines.pop().unwrap();
}
Ok(lines.join(""))
.join("\n");
description.truncate(description.trim_end_matches('\n').len());
cli_util::complete_newline(&mut description);
Ok(description)
}
fn cmd_describe(

View file

@ -55,8 +55,8 @@ modified",
.unwrap();
test_env.jj_cmd_success(&workspace_path, &["commit"]);
insta::assert_snapshot!(get_log_output(&test_env, &workspace_path), @r###"
@ 3ea3453a773f (no description set)
o 792a60936c42 modified
@ 3df78bc2b9b5 (no description set)
o 30a8c2b3d6eb modified
o 000000000000 (no description set)
"###);
}

View file

@ -56,7 +56,7 @@ JJ: Lines starting with "JJ: " (like this one) will be removed.
std::fs::write(&edit_script, "write\ndescription from editor").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["describe"]);
insta::assert_snapshot!(stdout, @r###"
Working copy now at: bfdd972f9349 description from editor
Working copy now at: 100943aeee3f description from editor
"###);
// Lines in editor starting with "JJ: " are ignored
@ -70,6 +70,39 @@ JJ: Lines starting with "JJ: " (like this one) will be removed.
Working copy now at: ccefa58bef47 description among comment
"###);
// Multi-line description
std::fs::write(&edit_script, "write\nline1\nline2\n\nline4\n\n").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["describe"]);
insta::assert_snapshot!(stdout, @r###"
Working copy now at: e932ba42cef0 line1
"###);
let stdout =
test_env.jj_cmd_success(&repo_path, &["log", "--no-graph", "-r@", "-Tdescription"]);
insta::assert_snapshot!(stdout, @r###"
line1
line2
line4
"###);
// Multi-line description again with CRLF, which should make no changes
std::fs::write(&edit_script, "write\nline1\r\nline2\r\n\r\nline4\r\n\r\n").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["describe"]);
insta::assert_snapshot!(stdout, @r###"
Nothing changed.
"###);
// Clear description
let stdout = test_env.jj_cmd_success(&repo_path, &["describe", "-m", ""]);
insta::assert_snapshot!(stdout, @r###"
Working copy now at: d6957294acdc (no description set)
"###);
std::fs::write(&edit_script, "write\n").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["describe"]);
insta::assert_snapshot!(stdout, @r###"
Nothing changed.
"###);
// Fails if the editor fails
std::fs::write(&edit_script, "fail").unwrap();
let stderr = test_env.jj_cmd_failure(&repo_path, &["describe"]);

View file

@ -136,7 +136,7 @@ fn test_obslog_squash() {
let stdout = get_log_output(&test_env, &repo_path, &["obslog", "-p", "-r", "@-"]);
insta::assert_snapshot!(stdout, @r###"
o test.user@example.com 2001-02-03 04:05:10.000 +07:00 9b6d4a272a6a
o test.user@example.com 2001-02-03 04:05:10.000 +07:00 27e721a5ba72
|\ squashed
| | Modified regular file file1:
| | 1 1: foo