diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs index 29569c34e1..6731ba34f0 100644 --- a/git-cliff-core/src/changelog.rs +++ b/git-cliff-core/src/changelog.rs @@ -249,6 +249,40 @@ mod test { }]), commit_parsers: Some(vec![ CommitParser { + sha: Some(String::from("tea")), + message: None, + body: None, + group: Some(String::from("I love tea")), + default_scope: None, + scope: None, + skip: None, + field: None, + pattern: None, + }, + CommitParser { + sha: Some(String::from("coffee")), + message: None, + body: None, + group: None, + default_scope: None, + scope: None, + skip: Some(true), + field: None, + pattern: None, + }, + CommitParser { + sha: Some(String::from("coffee2")), + message: None, + body: None, + group: None, + default_scope: None, + scope: None, + skip: Some(true), + field: None, + pattern: None, + }, + CommitParser { + sha: None, message: Regex::new(r".*merge.*").ok(), body: None, group: None, @@ -259,6 +293,7 @@ mod test { pattern: None, }, CommitParser { + sha: None, message: Regex::new("feat*").ok(), body: None, group: Some(String::from("New features")), @@ -269,6 +304,7 @@ mod test { pattern: None, }, CommitParser { + sha: None, message: Regex::new("^fix*").ok(), body: None, group: Some(String::from("Bug Fixes")), @@ -279,6 +315,7 @@ mod test { pattern: None, }, CommitParser { + sha: None, message: Regex::new("doc:").ok(), body: None, group: Some(String::from("Documentation")), @@ -289,6 +326,7 @@ mod test { pattern: None, }, CommitParser { + sha: None, message: Regex::new("docs:").ok(), body: None, group: Some(String::from("Documentation")), @@ -299,6 +337,7 @@ mod test { pattern: None, }, CommitParser { + sha: None, message: Regex::new(r"match\((.*)\):.*").ok(), body: None, group: Some(String::from("Matched ($1)")), @@ -309,6 +348,7 @@ mod test { pattern: None, }, CommitParser { + sha: None, message: Regex::new(".*").ok(), body: None, group: Some(String::from("Other")), @@ -333,6 +373,14 @@ mod test { let test_release = Release { version: Some(String::from("v1.0.0")), commits: vec![ + Commit::new( + String::from("coffee"), + String::from("revert(app): skip this commit"), + ), + Commit::new( + String::from("tea"), + String::from("feat(app): damn right"), + ), Commit::new( String::from("0bc123"), String::from("feat(app): add cool features"), @@ -377,6 +425,10 @@ mod test { String::from("qwert0"), String::from("match(group): support regex-replace for groups"), ), + Commit::new( + String::from("coffee"), + String::from("revert(app): skip this commit"), + ), ], commit_id: Some(String::from("0bc123")), timestamp: 50000000, @@ -416,6 +468,10 @@ mod test { String::from("hjkl12"), String::from("chore(ui): do boring stuff"), ), + Commit::new( + String::from("coffee2"), + String::from("revert(app): skip this commit"), + ), ], commit_id: None, timestamp: 1000, @@ -463,6 +519,10 @@ mod test { - update docs - add some documentation + ### I love tea + #### app + - damn right + ### Matched (group) #### group - support regex-replace for groups @@ -574,6 +634,10 @@ chore(deps): fix broken deps - update docs - add some documentation + ### I love tea + #### app + - damn right + ### Matched (group) #### group - support regex-replace for groups diff --git a/git-cliff-core/src/commit.rs b/git-cliff-core/src/commit.rs index 5d72df516c..06e0fb3193 100644 --- a/git-cliff-core/src/commit.rs +++ b/git-cliff-core/src/commit.rs @@ -288,6 +288,21 @@ impl Commit<'_> { })?, )); } + if parser.sha.clone().map(|v| v.to_lowercase()).as_deref() == + Some(&self.id) + { + if self.skip_commit(parser, protect_breaking) { + return Err(AppError::GroupError(String::from( + "Skipping commit", + ))); + } else { + self.group = parser.group.clone().or(self.group); + self.scope = parser.scope.clone().or(self.scope); + self.default_scope = + parser.default_scope.clone().or(self.default_scope); + return Ok(self); + } + } for (regex, text) in regex_checks { if regex.is_match(&text) { if self.skip_commit(parser, protect_breaking) { @@ -446,6 +461,7 @@ mod test { } let commit = test_cases[0].0.clone().parse( &[CommitParser { + sha: None, message: Regex::new("test*").ok(), body: None, group: Some(String::from("test_group")), @@ -618,6 +634,7 @@ mod test { let parsed_commit = commit.parse( &[CommitParser { + sha: None, message: None, body: None, group: Some(String::from("Test group")), @@ -634,4 +651,51 @@ mod test { assert_eq!(Some(String::from("Test group")), parsed_commit.group); Ok(()) } + + #[test] + fn commit_sha() -> Result<()> { + let commit = Commit::new( + String::from("8f55e69eba6e6ce811ace32bd84cc82215673cb6"), + String::from("feat: do something"), + ); + let parsed_commit = commit.clone().parse( + &[CommitParser { + sha: Some(String::from( + "8f55e69eba6e6ce811ace32bd84cc82215673cb6", + )), + message: None, + body: None, + group: None, + default_scope: None, + scope: None, + skip: Some(true), + field: None, + pattern: None, + }], + false, + false, + ); + assert!(parsed_commit.is_err()); + + let parsed_commit = commit.parse( + &[CommitParser { + sha: Some(String::from( + "8f55e69eba6e6ce811ace32bd84cc82215673cb6", + )), + message: None, + body: None, + group: Some(String::from("Test group")), + default_scope: None, + scope: None, + skip: None, + field: None, + pattern: None, + }], + false, + false, + )?; + assert_eq!(Some(String::from("Test group")), parsed_commit.group); + + Ok(()) + } } diff --git a/git-cliff-core/src/config.rs b/git-cliff-core/src/config.rs index cfbd926535..c9f6a6c406 100644 --- a/git-cliff-core/src/config.rs +++ b/git-cliff-core/src/config.rs @@ -87,6 +87,8 @@ pub struct GitConfig { /// Parser for grouping commits. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitParser { + /// SHA1 of the commit. + pub sha: Option, /// Regex for matching the commit message. #[serde(with = "serde_regex", default)] pub message: Option, diff --git a/git-cliff-core/tests/integration_test.rs b/git-cliff-core/tests/integration_test.rs index 31096f62f8..b0bead6924 100644 --- a/git-cliff-core/tests/integration_test.rs +++ b/git-cliff-core/tests/integration_test.rs @@ -52,6 +52,18 @@ fn generate_changelog() -> Result<()> { }]), commit_parsers: Some(vec![ CommitParser { + sha: Some(String::from("coffee")), + message: None, + body: None, + group: Some(String::from("I love coffee")), + default_scope: None, + scope: None, + skip: None, + field: None, + pattern: None, + }, + CommitParser { + sha: None, message: Regex::new("^feat").ok(), body: None, group: Some(String::from("shiny features")), @@ -62,6 +74,7 @@ fn generate_changelog() -> Result<()> { pattern: None, }, CommitParser { + sha: None, message: Regex::new("^fix").ok(), body: None, group: Some(String::from("fix bugs")), @@ -72,6 +85,7 @@ fn generate_changelog() -> Result<()> { pattern: None, }, CommitParser { + sha: None, message: Regex::new("^test").ok(), body: None, group: None, @@ -82,6 +96,7 @@ fn generate_changelog() -> Result<()> { pattern: None, }, CommitParser { + sha: None, message: None, body: None, group: Some(String::from("docs")), @@ -129,6 +144,7 @@ fn generate_changelog() -> Result<()> { Release { version: Some(String::from("v2.0.0")), commits: vec![ + Commit::new( String::from("000abc"), String::from("Add unconventional commit"), diff --git a/website/docs/configuration.md b/website/docs/configuration.md index 0d7286782d..a670e110b6 100644 --- a/website/docs/configuration.md +++ b/website/docs/configuration.md @@ -1,6 +1,7 @@ --- sidebar_position: 4 --- + # Configuration **git-cliff** configuration file supports [TOML](https://github.com/toml-lang/toml) (preferred) and [YAML](https://yaml.org) formats. @@ -213,21 +214,25 @@ An array of commit parsers for determining the commit groups by using regex. Examples: -- `{ message = "^feat", group = "Features"}` +- `{ message = "^feat", group = "Features" }` - Group the commit as "Features" if the commit message (description) starts with "feat". -- `{ body = ".*security", group = "Security"}` +- `{ body = ".*security", group = "Security" }` - Group the commit as "Security" if the commit body contains "security". - `{ message = '^fix\((.*)\)', group = 'Fix (${1})' }` - Use the matched scope value from the commit message in the group name. -- `{ message = ".*deprecated", body = ".*deprecated", group = "Deprecation"}` +- `{ message = ".*deprecated", body = ".*deprecated", group = "Deprecation" }` - Group the commit as "Deprecation" if the commit body and message contains "deprecated". -- `{ message = "^revert", skip = true}` +- `{ message = "^revert", skip = true }` - Skip processing the commit if the commit message (description) starts with "revert". -- `{ message = "^doc", group = "Documentation", default_scope = "other"},` +- `{ message = "^doc", group = "Documentation", default_scope = "other" },` - If the commit starts with "doc", group the commit as "Documentation" and set the default scope to "other". (e.g. `docs: xyz` will be processed as `docs(other): xyz`) -- `{ message = "(www)", scope = "Application"}` +- `{ message = "(www)", scope = "Application" }` - If the commit contains "(www)", override the scope with "Application". Scoping order is: scope specification, conventional commit's scope and default scope. -- `{ field = "author.name", pattern = "John Doe", group = "John's stuff"}` +- `{ sha = "f6f2472bdf0bbb5f9fcaf2d72c1fa9f98f772bb2", skip = true }` + - Skip a specific commit by using its SHA1. +- `{ sha = "f6f2472bdf0bbb5f9fcaf2d72c1fa9f98f772bb2", group = "Stuff" }` + - Set the group of the commit by using its SHA1. +- `{ field = "author.name", pattern = "John Doe", group = "John's stuff" }` - If the author's name attribute of the commit matches the pattern "John Doe" (as a regex), override the scope with "John' stuff". Supported commit attributes are: - `id` - `message`