diff --git a/Cargo.lock b/Cargo.lock index ef01725206..9515935270 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,6 +466,7 @@ dependencies = [ "git2", "glob", "indexmap", + "lazy-regex", "pretty_assertions", "regex", "rust-embed", @@ -631,6 +632,29 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy-regex" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b12f2eb6ed7d39405c5eb25a034b4c106a9ad87a6d9be3298de6c5f32fd57d" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2496e5264069bc726ccf37eb76b9cd89406ae110d836c3f76729f99c8a23293" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn", +] + [[package]] name = "lazy_static" version = "1.4.0" diff --git a/README.md b/README.md index 0bdc3e27ba..395235b0a0 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,8 @@ git cliff --with-commit "$commit_msg" -o CHANGELOG.md git add CHANGELOG.md && git commit -m "$commit_msg" ``` +> The commit SHA will be empty as default when `--with-commit` is used. Specify the hash with a message separated by single whitespace for setting the commit SHA. e.g. "--with-commit "8f55e69eba6e6ce811ace32bd84cc82215673cb6 feat: add X"" + Sort the commits inside sections: ```sh diff --git a/git-cliff-core/Cargo.toml b/git-cliff-core/Cargo.toml index a9598904bf..260c773913 100644 --- a/git-cliff-core/Cargo.toml +++ b/git-cliff-core/Cargo.toml @@ -20,6 +20,7 @@ serde_regex = "1.1.0" indexmap = "1.9.1" toml = "0.5.9" glob = "0.3.0" +lazy-regex = "2.3.0" [dependencies.git2] version = "0.14.4" diff --git a/git-cliff-core/src/commit.rs b/git-cliff-core/src/commit.rs index 330edd1fbf..2f3fc1a239 100644 --- a/git-cliff-core/src/commit.rs +++ b/git-cliff-core/src/commit.rs @@ -11,12 +11,21 @@ use crate::error::{ }; use git2::Commit as GitCommit; use git_conventional::Commit as ConventionalCommit; +use lazy_regex::{ + lazy_regex, + Lazy, + Regex, +}; use serde::ser::{ Serialize, SerializeStruct, Serializer, }; +/// Regular expression for matching SHA1 and a following commit message +/// separated by a whitespace. +static SHA1_REGEX: Lazy = lazy_regex!(r#"^\b([a-f0-9]{40})\b (.*)$"#); + /// Common commit object that is parsed from a repository. #[derive(Debug, Clone, PartialEq, serde::Deserialize)] #[serde(rename_all = "camelCase")] @@ -78,6 +87,20 @@ impl<'a> From<&'a git_conventional::Footer<'a>> for Footer<'a> { } } +impl<'a> From for Commit<'a> { + fn from(s: String) -> Self { + if let Some(captures) = SHA1_REGEX.captures(&s) { + if let (Some(id), Some(message)) = ( + captures.get(1).map(|v| v.as_str()), + captures.get(2).map(|v| v.as_str()), + ) { + return Commit::new(id.to_string(), message.to_string()); + } + } + Commit::new(String::new(), s) + } +} + impl<'a> From<&GitCommit<'a>> for Commit<'a> { fn from(commit: &GitCommit<'a>) -> Self { Self::new( @@ -454,4 +477,37 @@ mod test { ); Ok(()) } + + #[test] + fn parse_commit() { + assert_eq!( + Commit::new(String::new(), String::from("test: no sha1 given")), + Commit::from(String::from("test: no sha1 given")) + ); + assert_eq!( + Commit::new( + String::from("8f55e69eba6e6ce811ace32bd84cc82215673cb6"), + String::from("feat: do something") + ), + Commit::from(String::from( + "8f55e69eba6e6ce811ace32bd84cc82215673cb6 feat: do something" + )) + ); + assert_eq!( + Commit::new( + String::from("3bdd0e690c4cd5bd00e5201cc8ef3ce3fb235853"), + String::from("chore: do something") + ), + Commit::from(String::from( + "3bdd0e690c4cd5bd00e5201cc8ef3ce3fb235853 chore: do something" + )) + ); + assert_eq!( + Commit::new( + String::new(), + String::from("thisisinvalidsha1 style: add formatting") + ), + Commit::from(String::from("thisisinvalidsha1 style: add formatting")) + ); + } } diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index 63ba51aa44..467bbaf558 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -261,10 +261,8 @@ pub fn run(mut args: Opt) -> Result<()> { // Add custom commit messages to the latest release. if let Some(custom_commits) = args.with_commit { if let Some(latest_release) = releases.iter_mut().last() { - custom_commits.iter().for_each(|commit_message| { - latest_release - .commits - .push(Commit::new(String::new(), commit_message.to_string())) + custom_commits.into_iter().for_each(|message| { + latest_release.commits.push(Commit::from(message)) }); } }