-
Notifications
You must be signed in to change notification settings - Fork 353
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cli: add
--author
argument for commit
and describe
- Loading branch information
Showing
7 changed files
with
189 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ use crate::command_error::CommandError; | |
use crate::description_util::description_template; | ||
use crate::description_util::edit_description; | ||
use crate::description_util::join_message_paragraphs; | ||
use crate::description_util::parse_user_name_email; | ||
use crate::ui::Ui; | ||
|
||
/// Update the description and create a new change on top. | ||
|
@@ -50,6 +51,11 @@ pub(crate) struct CommitArgs { | |
/// $ JJ_USER='Foo Bar' [email protected] jj commit --reset-author | ||
#[arg(long)] | ||
reset_author: bool, | ||
/// Set author to the provided string | ||
/// | ||
/// This changes author name and email. | ||
#[arg(long, conflicts_with = "reset_author")] | ||
author: Option<String>, | ||
} | ||
|
||
#[instrument(skip_all)] | ||
|
@@ -106,6 +112,15 @@ new working-copy commit. | |
if args.reset_author { | ||
commit_builder.set_author(commit_builder.committer().clone()); | ||
} | ||
if let Some(author) = &args.author { | ||
let (name, email) = parse_user_name_email(author)?; | ||
let new_author = jj_lib::backend::Signature { | ||
name, | ||
email, | ||
timestamp: commit_builder.committer().timestamp.clone(), | ||
}; | ||
commit_builder.set_author(new_author); | ||
} | ||
|
||
let description = if !args.message_paragraphs.is_empty() { | ||
join_message_paragraphs(&args.message_paragraphs) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ use std::io; | |
use std::io::Read; | ||
|
||
use itertools::Itertools; | ||
use jj_lib::backend::Signature; | ||
use jj_lib::commit::CommitIteratorExt; | ||
use jj_lib::object_id::ObjectId; | ||
use tracing::instrument; | ||
|
@@ -29,6 +30,7 @@ use crate::description_util::description_template; | |
use crate::description_util::edit_description; | ||
use crate::description_util::edit_multiple_descriptions; | ||
use crate::description_util::join_message_paragraphs; | ||
use crate::description_util::parse_user_name_email; | ||
use crate::description_util::ParsedBulkEditMessage; | ||
use crate::ui::Ui; | ||
|
||
|
@@ -72,6 +74,11 @@ pub(crate) struct DescribeArgs { | |
/// $ JJ_USER='Foo Bar' [email protected] jj describe --reset-author | ||
#[arg(long)] | ||
reset_author: bool, | ||
/// Set author to the provided string | ||
/// | ||
/// This changes author name and email while retaining timestamp. | ||
#[arg(long, conflicts_with = "reset_author")] | ||
author: Option<String>, | ||
} | ||
|
||
#[instrument(skip_all)] | ||
|
@@ -112,6 +119,12 @@ pub(crate) fn cmd_describe( | |
None | ||
}; | ||
|
||
let author_arg = args | ||
.author | ||
.as_deref() | ||
.map(parse_user_name_email) | ||
.transpose()?; | ||
|
||
let commit_descriptions: Vec<(_, _)> = if args.no_edit || shared_description.is_some() { | ||
commits | ||
.iter() | ||
|
@@ -139,6 +152,14 @@ pub(crate) fn cmd_describe( | |
let new_author = commit_builder.committer().clone(); | ||
commit_builder.set_author(new_author); | ||
} | ||
if let Some((name, email)) = author_arg.clone() { | ||
let new_author = Signature { | ||
name, | ||
email, | ||
timestamp: commit.author().timestamp.clone(), | ||
}; | ||
commit_builder.set_author(new_author); | ||
} | ||
let temp_commit = commit_builder.write_hidden()?; | ||
Ok((commit.id(), temp_commit)) | ||
}) | ||
|
@@ -195,7 +216,10 @@ pub(crate) fn cmd_describe( | |
let commit_descriptions: HashMap<_, _> = commit_descriptions | ||
.into_iter() | ||
.filter_map(|(commit, new_description)| { | ||
if *new_description == *commit.description() && !args.reset_author { | ||
if *new_description == *commit.description() | ||
&& !args.reset_author | ||
&& author_arg.is_none() | ||
{ | ||
None | ||
} else { | ||
Some((commit.id(), new_description)) | ||
|
@@ -218,13 +242,21 @@ pub(crate) fn cmd_describe( | |
.collect_vec(), | ||
|rewriter| { | ||
let old_commit_id = rewriter.old_commit().id().clone(); | ||
let new_author = author_arg.clone().map(|(name, email)| Signature { | ||
name, | ||
email, | ||
timestamp: rewriter.old_commit().author().timestamp.clone(), | ||
}); | ||
let mut commit_builder = rewriter.rebase(command.settings())?; | ||
if let Some(description) = commit_descriptions.get(&old_commit_id) { | ||
commit_builder = commit_builder.set_description(description); | ||
if args.reset_author { | ||
let new_author = commit_builder.committer().clone(); | ||
commit_builder = commit_builder.set_author(new_author); | ||
} | ||
if let Some(new_author) = new_author { | ||
commit_builder = commit_builder.set_author(new_author); | ||
} | ||
num_described += 1; | ||
} else { | ||
num_rebased += 1; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ use crate::cli_util::edit_temp_file; | |
use crate::cli_util::short_commit_hash; | ||
use crate::cli_util::WorkspaceCommandHelper; | ||
use crate::cli_util::WorkspaceCommandTransaction; | ||
use crate::command_error::cli_error; | ||
use crate::command_error::CommandError; | ||
use crate::formatter::PlainTextFormatter; | ||
use crate::text_util; | ||
|
@@ -251,13 +252,22 @@ pub fn description_template( | |
Ok(output.into_string_lossy()) | ||
} | ||
|
||
pub fn parse_user_name_email(author: &str) -> Result<(String, String), CommandError> { | ||
let re = regex::Regex::new(r"(?<name>.*?)\s*<(?<email>.+)>").unwrap(); | ||
let captures = re | ||
.captures(author) | ||
.ok_or_else(|| cli_error("Invalid author string"))?; | ||
Ok((captures["name"].to_string(), captures["email"].to_string())) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use indexmap::indexmap; | ||
use indoc::indoc; | ||
use maplit::hashmap; | ||
|
||
use super::parse_bulk_edit_message; | ||
use super::parse_user_name_email; | ||
use crate::description_util::ParseBulkEditMessageError; | ||
|
||
#[test] | ||
|
@@ -410,4 +420,33 @@ mod tests { | |
assert!(result.duplicates.is_empty()); | ||
assert!(result.unexpected.is_empty()); | ||
} | ||
|
||
#[test] | ||
fn test_parse_author() { | ||
let expected_name = "Example"; | ||
let expected_email = "[email protected]"; | ||
let parsed = parse_user_name_email(&format!("{expected_name} <{expected_email}>")).unwrap(); | ||
assert_eq!( | ||
(expected_name.to_string(), expected_email.to_string()), | ||
parsed | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_parse_author_with_utf8() { | ||
let expected_name = "Ąćęłńóśżź"; | ||
let expected_email = "[email protected]"; | ||
let parsed = parse_user_name_email(&format!("{expected_name} <{expected_email}>")).unwrap(); | ||
assert_eq!( | ||
(expected_name.to_string(), expected_email.to_string()), | ||
parsed | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_parse_author_without_name() { | ||
let expected_email = "[email protected]"; | ||
let parsed = parse_user_name_email(&format!("<{expected_email}>")).unwrap(); | ||
assert_eq!(("".to_string(), expected_email.to_string()), parsed); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -446,6 +446,9 @@ Update the description and create a new change on top | |
You can use it in combination with the JJ_USER and JJ_EMAIL environment variables to set a different author: | ||
$ JJ_USER='Foo Bar' [email protected] jj commit --reset-author | ||
* `--author <AUTHOR>` — Set author to the provided string | ||
This changes author name and email. | ||
|
@@ -599,6 +602,9 @@ Starts an editor to let you edit the description of changes. The editor will be | |
You can use it in combination with the JJ_USER and JJ_EMAIL environment variables to set a different author: | ||
$ JJ_USER='Foo Bar' [email protected] jj describe --reset-author | ||
* `--author <AUTHOR>` — Set author to the provided string | ||
This changes author name and email while retaining timestamp. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -215,6 +215,7 @@ fn test_commit_with_description_template() { | |
|
||
std::fs::write(workspace_path.join("file1"), "foo\n").unwrap(); | ||
std::fs::write(workspace_path.join("file2"), "bar\n").unwrap(); | ||
std::fs::write(workspace_path.join("file3"), "foobar\n").unwrap(); | ||
|
||
// Only file1 should be included in the diff | ||
test_env.jj_cmd_ok(&workspace_path, &["commit", "file1"]); | ||
|
@@ -230,19 +231,41 @@ fn test_commit_with_description_template() { | |
JJ: Lines starting with "JJ: " (like this one) will be removed. | ||
"###); | ||
|
||
// Timestamp after the reset should be available to the template | ||
test_env.jj_cmd_ok(&workspace_path, &["commit", "--reset-author"]); | ||
// Only file2 with modified author should be included in the diff | ||
test_env.jj_cmd_ok( | ||
&workspace_path, | ||
&[ | ||
"commit", | ||
"--author", | ||
r#""Another User <[email protected]>""#, | ||
"file2", | ||
], | ||
); | ||
insta::assert_snapshot!( | ||
std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r###" | ||
std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#" | ||
JJ: Author: Test User <test[email protected]> (2001-02-03 08:05:09) | ||
JJ: Author: "Another User <another[email protected]> (2001-02-03 08:05:09) | ||
JJ: Committer: Test User <[email protected]> (2001-02-03 08:05:09) | ||
JJ: file2 | 1 + | ||
JJ: 1 file changed, 1 insertion(+), 0 deletions(-) | ||
JJ: Lines starting with "JJ: " (like this one) will be removed. | ||
"###); | ||
"#); | ||
|
||
// Timestamp after the reset should be available to the template | ||
test_env.jj_cmd_ok(&workspace_path, &["commit", "--reset-author"]); | ||
insta::assert_snapshot!( | ||
std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#" | ||
JJ: Author: Test User <[email protected]> (2001-02-03 08:05:10) | ||
JJ: Committer: Test User <[email protected]> (2001-02-03 08:05:10) | ||
JJ: file3 | 1 + | ||
JJ: 1 file changed, 1 insertion(+), 0 deletions(-) | ||
JJ: Lines starting with "JJ: " (like this one) will be removed. | ||
"#); | ||
} | ||
|
||
#[test] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -525,29 +525,75 @@ fn test_describe_author() { | |
~ | ||
"###); | ||
|
||
// Reset the author for the latest commit (the committer is always reset) | ||
// Change the author for the latest commit (the committer is always reset) | ||
test_env.jj_cmd_ok( | ||
&repo_path, | ||
&[ | ||
"describe", | ||
"--config-toml", | ||
r#"user.name = "Ove Ridder" | ||
user.email = "[email protected]""#, | ||
"--no-edit", | ||
"--reset-author", | ||
"--author", | ||
r#""Super Seeder <[email protected]>""#, | ||
], | ||
); | ||
insta::assert_snapshot!(get_signatures(), @r###" | ||
@ Ove Ridder ove.ridder@example.com 2001-02-03 04:05:12.000 +07:00 | ||
│ Ove Ridder ove.ridder@example.com 2001-02-03 04:05:12.000 +07:00 | ||
insta::assert_snapshot!(get_signatures(), @r#" | ||
@ "Super Seeder super.seeder@example.com 2001-02-03 04:05:10.000 +07:00 | ||
│ Test User test.user@example.com 2001-02-03 04:05:12.000 +07:00 | ||
○ Test User [email protected] 2001-02-03 04:05:09.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:09.000 +07:00 | ||
○ Test User [email protected] 2001-02-03 04:05:08.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:08.000 +07:00 | ||
○ Test User [email protected] 2001-02-03 04:05:07.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:07.000 +07:00 | ||
~ | ||
"###); | ||
"#); | ||
|
||
// Change the author for multiple commits (the committer is always reset) | ||
test_env.jj_cmd_ok( | ||
&repo_path, | ||
&[ | ||
"describe", | ||
"@---", | ||
"@-", | ||
"--no-edit", | ||
"--author", | ||
r#""Super Seeder <[email protected]>""#, | ||
], | ||
); | ||
insta::assert_snapshot!(get_signatures(), @r#" | ||
@ "Super Seeder [email protected] 2001-02-03 04:05:10.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
○ "Super Seeder [email protected] 2001-02-03 04:05:09.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
○ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
○ "Super Seeder [email protected] 2001-02-03 04:05:07.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
~ | ||
"#); | ||
|
||
// Reset the author for the latest commit (the committer is always reset) | ||
test_env.jj_cmd_ok( | ||
&repo_path, | ||
&[ | ||
"describe", | ||
"--config-toml", | ||
r#"user.name = "Ove Ridder" | ||
user.email = "[email protected]""#, | ||
"--no-edit", | ||
"--reset-author", | ||
], | ||
); | ||
insta::assert_snapshot!(get_signatures(), @r#" | ||
@ Ove Ridder [email protected] 2001-02-03 04:05:16.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:16.000 +07:00 | ||
○ "Super Seeder [email protected] 2001-02-03 04:05:09.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
○ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
○ "Super Seeder [email protected] 2001-02-03 04:05:07.000 +07:00 | ||
│ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
~ | ||
"#); | ||
|
||
// Reset the author for multiple commits (the committer is always reset) | ||
test_env.jj_cmd_ok( | ||
|
@@ -563,17 +609,17 @@ fn test_describe_author() { | |
"--reset-author", | ||
], | ||
); | ||
insta::assert_snapshot!(get_signatures(), @r###" | ||
@ Ove Ridder [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
○ Ove Ridder [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
○ Test User [email protected] 2001-02-03 04:05:08.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
○ Ove Ridder [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
insta::assert_snapshot!(get_signatures(), @r#" | ||
@ Ove Ridder [email protected] 2001-02-03 04:05:18.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:18.000 +07:00 | ||
○ Ove Ridder [email protected] 2001-02-03 04:05:18.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:18.000 +07:00 | ||
○ Test User [email protected] 2001-02-03 04:05:14.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:18.000 +07:00 | ||
○ Ove Ridder [email protected] 2001-02-03 04:05:18.000 +07:00 | ||
│ Ove Ridder [email protected] 2001-02-03 04:05:18.000 +07:00 | ||
~ | ||
"###); | ||
"#); | ||
} | ||
|
||
#[test] | ||
|