diff --git a/git-cliff-core/src/config.rs b/git-cliff-core/src/config.rs index 6909ad9f8f..31d5601d32 100644 --- a/git-cliff-core/src/config.rs +++ b/git-cliff-core/src/config.rs @@ -11,7 +11,8 @@ use serde::{Deserialize, Serialize}; use crate::embed::EmbeddedConfig; use crate::error::Result; -use crate::{CONFIG_FILES, DEFAULT_CONFIG, command, error}; +use crate::template::Template; +use crate::{CONFIG_FILES, DEFAULT_CONFIG, command, error, statistics}; /// Default initial tag. const DEFAULT_INITIAL_TAG: &str = "0.1.0"; @@ -560,6 +561,29 @@ impl Config { if path.is_file() { Some(path) } else { None } }) } + + /// Returns whether any configured changelog template references per-commit + /// diff statistics. + pub fn uses_commit_statistics(&self) -> Result { + let trim = self.changelog.trim; + let body_template = Template::new("body", self.changelog.body.clone(), trim)?; + if body_template.contains_variable(statistics::TEMPLATE_VARIABLES) { + return Ok(true); + } + if let Some(header) = &self.changelog.header { + let header_template = Template::new("header", header.clone(), trim)?; + if header_template.contains_variable(statistics::TEMPLATE_VARIABLES) { + return Ok(true); + } + } + if let Some(footer) = &self.changelog.footer { + let footer_template = Template::new("footer", footer.clone(), trim)?; + if footer_template.contains_variable(statistics::TEMPLATE_VARIABLES) { + return Ok(true); + } + } + Ok(false) + } } impl FromStr for Config { @@ -668,4 +692,21 @@ mod test { Ok(()) } + + #[test] + fn detects_commit_statistics_usage_in_templates() -> Result<()> { + let mut config = EmbeddedConfig::parse()?; + assert!(!config.uses_commit_statistics()?); + + config.changelog.body = String::from( + "{% for commit in commits %}{{ commit.statistics.files_changed }}{% endfor %}", + ); + assert!(config.uses_commit_statistics()?); + + config.changelog.body = String::from("{{ version }}"); + config.changelog.footer = Some(String::from("{{ commit.statistics.additions }}")); + assert!(config.uses_commit_statistics()?); + + Ok(()) + } } diff --git a/git-cliff-core/src/statistics.rs b/git-cliff-core/src/statistics.rs index 756f8ab99f..35409f79e3 100644 --- a/git-cliff-core/src/statistics.rs +++ b/git-cliff-core/src/statistics.rs @@ -5,6 +5,9 @@ use serde::{Deserialize, Serialize}; use crate::release::Release; +/// Template variables related to statistics context. +pub(crate) const TEMPLATE_VARIABLES: &[&str] = &["commit.statistics"]; + /// Aggregated information about how many times a specific link appeared in /// commit messages. #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs index bcb5b62bbf..20198eb6c5 100644 --- a/git-cliff/src/lib.rs +++ b/git-cliff/src/lib.rs @@ -202,6 +202,7 @@ fn process_repository<'a>( let ignore_regex = config.git.ignore_tags.as_ref(); let count_tags = config.git.count_tags.as_ref(); let recurse_submodules = config.git.recurse_submodules.unwrap_or(false); + let compute_commit_statistics = config.uses_commit_statistics()?; tags.retain(|_, tag| { let name = &tag.name; @@ -353,22 +354,25 @@ fn process_repository<'a>( for git_commit in commits.iter().rev() { let release = releases.last_mut().unwrap(); let mut commit = Commit::from(git_commit); - commit.statistics = match repository.commit_statistics(git_commit) { - Ok(statistics) => statistics, - Err(err) - if matches!( - &err, - Error::GitError(git_err) if git_err.message().contains("object not found") - ) => - { - tracing::warn!( - "Skipping diff statistics for commit {} because a Git object is missing: {err}", - commit.id, - ); - CommitStatistics::default() + if compute_commit_statistics { + commit.statistics = match repository.commit_statistics(git_commit) { + Ok(statistics) => statistics, + Err(err) + if matches!( + &err, + Error::GitError(git_err) if git_err.message().contains("object not found") + ) => + { + tracing::warn!( + "Skipping diff statistics for commit {} because a Git object is missing: \ + {err}", + commit.id, + ); + CommitStatistics::default() + } + Err(err) => return Err(err), } - Err(err) => return Err(err), - }; + } let commit_id = commit.id.clone(); release.commits.push(commit); release.repository = Some(repository_path.clone());