Repository: orhun/git-cliff
Pull request #698 (2024-06-14 14:27:29)

feat(changelog): make `changelog.header` a template
Made changelog.header into a template like changelog.body and changelog.footer. Added test test-header-template.

## Description

Implement #697.

## Motivation and Context

Create parity between `changelog.header`, `changelog.body` and `changelog.footer`.

## How Has This Been Tested?

- Added new test fixture "test-header-template".

## Types of Changes

- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [X] Documentation (no code change)
- [ ] Refactor (refactoring production code)
- [ ] Other <!--- (provide information) -->

## Checklist:

- [X] My code follows the code style of this project.
- [X] I have updated the documentation accordingly.
- [X] I have formatted the code with [rustfmt](https://github.com/rust-lang/rustfmt).
- [x] I checked the lints with [clippy](https://github.com/rust-lang/rust-clippy).
- [X] I have added tests to cover my changes.
- [x] All new and existing tests passed.

<!--- Thank you for contributing to git-cliff! ⛰️  -->


Cyclonit: Hi @orhun,
functionally this is a very small change. I was able to copy the logic from the body and footer template processing 1-to-1. However, I do fear that we will have to flag this as a breaking change regardless. All of the tests and examples contain explicit `\n` in the header. These are now obsolete. Without removing them, we would end up with an extra empty line between the header and the first release.
Cyclonit: I am  not quite sure what to do about the allegedly wrong formatting in `changelog.rs`. When I run `cargo fmt` locally, it formats the code the way it is in this PR. I don't know why the pipeline demands it to be formatted differently. See https://github.com/orhun/git-cliff/actions/runs/9518131251/job/26238303168?pr=698

Similarly I am having trouble figuring out why the test suite fails: https://github.com/orhun/git-cliff/actions/runs/9518131251/job/26238304948?pr=698
Actual and expected look identical to me.
Cyclonit: And this is another problem: https://github.com/orhun/git-cliff/actions/runs/9518517603/job/26239610418?pr=698#step:3:842

I have no idea why the template behaves differently in test-header-template compared to test-footer-template. They are identical, but in test-header-template there is indentation, in test-footer-template there is not. I am done for today, maybe someone else could take a look into this.
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/orhun/git-cliff/pull/698?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) Report
Attention: Patch coverage is `69.23077%` with `4 lines` in your changes missing coverage. Please review.
> Project coverage is 37.19%. Comparing base [(`eac2195`)](https://app.codecov.io/gh/orhun/git-cliff/commit/eac2195963e1688cd865969a01cc944d6a4af7b4?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) to head [(`4033895`)](https://app.codecov.io/gh/orhun/git-cliff/commit/4033895b3e4f2467fde81efb4fcb418fa40446e7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z).

| [Files](https://app.codecov.io/gh/orhun/git-cliff/pull/698?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) | Patch % | Lines |
|---|---|---|
| [git-cliff-core/src/changelog.rs](https://app.codecov.io/gh/orhun/git-cliff/pull/698?src=pr&el=tree&filepath=git-cliff-core%2Fsrc%2Fchangelog.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z#diff-Z2l0LWNsaWZmLWNvcmUvc3JjL2NoYW5nZWxvZy5ycw==) | 69.24% | [4 Missing :warning: ](https://app.codecov.io/gh/orhun/git-cliff/pull/698?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) |

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #698      +/-   ##
==========================================
+ Coverage   36.89%   37.19%   +0.30%     
==========================================
  Files          18       18              
  Lines        1350     1358       +8     
==========================================
+ Hits          498      505       +7     
- Misses        852      853       +1     
```

| [Flag](https://app.codecov.io/gh/orhun/git-cliff/pull/698/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) | Coverage Δ | |
|---|---|---|
| [unit-tests](https://app.codecov.io/gh/orhun/git-cliff/pull/698/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) | `37.19% <69.24%> (+0.30%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/orhun/git-cliff/pull/698?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z).

orhun: Can you resolve the conflicts? 😊 
Cyclonit: I've fixed the tests and added the minimalist documentation update. How do we deal with the formatting? I cannot get my local rustfmt to format the code the way the pipeline wants it to be.
orhun: > I've fixed the tests and added the minimalist documentation update. How do we deal with the formatting? I cannot get my local rustfmt to format the code the way the pipeline wants it to be.

Thanks! The formatting is already fixed in `main`, so if you merge those changes it will be fine I think.

I can also handle it before merging the PR, no biggie.
Cyclonit: > > I've fixed the tests and added the minimalist documentation update. How do we deal with the formatting? I cannot get my local rustfmt to format the code the way the pipeline wants it to be.
> 
> Thanks! The formatting is already fixed in `main`, so if you merge those changes it will be fine I think.
> 
> I can also handle it before merging the PR, no biggie.

I've tried improving the documentation by specifying the context of each template. Is this okay?

Patch:
diff --git a/.github/fixtures/new-fixture-template/cliff.toml b/.github/fixtures/new-fixture-template/cliff.toml
index 9a9d380..b26308c 100644
--- a/.github/fixtures/new-fixture-template/cliff.toml
+++ b/.github/fixtures/new-fixture-template/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/cliff.toml b/cliff.toml
index 81f90a0..09596b8 100644
--- a/cliff.toml
+++ b/cliff.toml
@@ -6,7 +6,7 @@
 # See documentation for more information on available options.
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 [![animation](https://raw.githubusercontent.com/orhun/git-cliff/main/website/static/img/git-cliff-anim.gif)](https://git-cliff.org)\n
 """
diff --git a/config/cliff.toml b/config/cliff.toml
index b0f88b2..ba12c0b 100644
--- a/config/cliff.toml
+++ b/config/cliff.toml
@@ -6,7 +6,7 @@
 # See documentation for more information on available options.
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/examples/cocogitto.toml b/examples/cocogitto.toml
index d6f47d6..6b30b74 100644
--- a/examples/cocogitto.toml
+++ b/examples/cocogitto.toml
@@ -2,7 +2,7 @@
 # https://git-cliff.org/docs/configuration
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.\n
diff --git a/examples/detailed.toml b/examples/detailed.toml
index 7648691..70ca6a4 100644
--- a/examples/detailed.toml
+++ b/examples/detailed.toml
@@ -2,7 +2,7 @@
 # https://git-cliff.org/docs/configuration
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/examples/github-keepachangelog.toml b/examples/github-keepachangelog.toml
index c50807f..7c08e34 100644
--- a/examples/github-keepachangelog.toml
+++ b/examples/github-keepachangelog.toml
@@ -2,7 +2,7 @@
 # https://git-cliff.org/docs/configuration
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/examples/keepachangelog.toml b/examples/keepachangelog.toml
index fdcbea2..abbedc1 100644
--- a/examples/keepachangelog.toml
+++ b/examples/keepachangelog.toml
@@ -2,7 +2,7 @@
 # https://git-cliff.org/docs/configuration
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/examples/scoped.toml b/examples/scoped.toml
index 46c06e3..966a1e8 100644
--- a/examples/scoped.toml
+++ b/examples/scoped.toml
@@ -2,7 +2,7 @@
 # https://git-cliff.org/docs/configuration
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/examples/scopesorted.toml b/examples/scopesorted.toml
index ce13531..e7515b5 100644
--- a/examples/scopesorted.toml
+++ b/examples/scopesorted.toml
@@ -2,7 +2,7 @@
 # https://git-cliff.org/docs/configuration
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/examples/unconventional.toml b/examples/unconventional.toml
index 0a255cf..40f0a08 100644
--- a/examples/unconventional.toml
+++ b/examples/unconventional.toml
@@ -2,7 +2,7 @@
 # https://git-cliff.org/docs/configuration
 
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs
index 1b6676b..870f633 100644
--- a/git-cliff-core/src/changelog.rs
+++ b/git-cliff-core/src/changelog.rs
@@ -29,6 +29,7 @@ use std::time::{
 pub struct Changelog<'a> {
 	/// Releases that the changelog will contain.
 	pub releases:       Vec<Release<'a>>,
+	header_template:    Option<Template>,
 	body_template:      Template,
 	footer_template:    Option<Template>,
 	config:             &'a Config,
@@ -41,6 +42,10 @@ impl<'a> Changelog<'a> {
 		let trim = config.changelog.trim.unwrap_or(true);
 		let mut changelog = Self {
 			releases,
+			header_template: match &config.changelog.header {
+				Some(header) => Some(Template::new(header.to_string(), trim)?),
+				None => None,
+			},
 			body_template: Template::new(
 				config
 					.changelog
@@ -502,16 +507,27 @@ impl<'a> Changelog<'a> {
 			.postprocessors
 			.clone()
 			.unwrap_or_default();
-		if let Some(header) = &self.config.changelog.header {
-			let write_result = write!(out, "{header}");
+
+		if let Some(header_template) = &self.header_template {
+			let write_result = writeln!(
+				out,
+				"{}",
+				header_template.render(
+					&Releases {
+						releases: &self.releases,
+					},
+					Some(&self.additional_context),
+					&postprocessors,
+				)?
+			);
 			if let Err(e) = write_result {
 				if e.kind() != std::io::ErrorKind::BrokenPipe {
 					return Err(e.into());
 				}
 			}
 		}
-		let mut releases = self.releases.clone();
-		for release in releases.iter_mut() {
+
+		for release in &self.releases {
 			let write_result = write!(
 				out,
 				"{}",
@@ -527,13 +543,14 @@ impl<'a> Changelog<'a> {
 				}
 			}
 		}
+
 		if let Some(footer_template) = &self.footer_template {
 			let write_result = writeln!(
 				out,
 				"{}",
 				footer_template.render(
 					&Releases {
-						releases: &releases,
+						releases: &self.releases,
 					},
 					Some(&self.additional_context),
 					&postprocessors,
@@ -545,6 +562,7 @@ impl<'a> Changelog<'a> {
 				}
 			}
 		}
+
 		Ok(())
 	}
 
@@ -953,6 +971,7 @@ mod test {
 		assert_eq!(
 			String::from(
 				r#"# Changelog
+
 			## Release [v1.1.0] - 1970-01-01
 
 
@@ -1069,6 +1088,7 @@ chore(deps): fix broken deps
 		assert_eq!(
 			String::from(
 				r#"# Changelog
+
 			## Unreleased
 
 			### Bug Fixes
@@ -1172,6 +1192,7 @@ chore(deps): fix broken deps
 		changelog.generate(&mut out)?;
 		expect_test::expect![[r#"
     # Changelog
+
     ## Unreleased
 
     ### Bug Fixes
diff --git a/website/docs/configuration/changelog.md b/website/docs/configuration/changelog.md
index 22fae40..90387eb 100644
--- a/website/docs/configuration/changelog.md
+++ b/website/docs/configuration/changelog.md
@@ -22,23 +22,25 @@ postprocessors = [{ pattern = "foo", replace = "bar"}]
 
 <!-- {% endraw %} -->
 
+See [templating](/docs/category/templating) for more detail.
+
 ### header
 
-Header text that will be added to the beginning of the changelog.
+Header template that will be rendered and added to the beginning of the changelog.
+
+The template context contains the full list of releases in the variable `releases`. See [templating](/docs/category/templating) for more details.
 
 ### body
 
-Body template that represents a single release in the changelog.
+Body template that will be rendered for each release in the changelog. For example, if the changelog contains 3 releases, it will be rendered 3 times, once for each release.
 
-See [templating](/docs/category/templating) for more detail.
+The template context contains one release in the variable `release`. See [templating](/docs/category/templating) for more detail.
 
 ### footer
 
 Footer template that will be rendered and added to the end of the changelog.
 
-The template context is the same as [`body`](#body) and contains all the releases instead of a single release.
-
-For example, to get the list of releases, use the `{{ releases }}` variable in the template. To get information about a single release, iterate over this array and access the fields similar to [`body`](#body).
+The template context contains the full list of releases in the variable `releases`. See [templating](/docs/category/templating) for more details.
 
 See [Keep a Changelog configuration](/docs/templating/examples#keep-a-changelog) for seeing the example of adding links to the end of the changelog.
 


Test patch:
diff --git a/.github/fixtures/test-bump-initial-tag-cli-arg/cliff.toml b/.github/fixtures/test-bump-initial-tag-cli-arg/cliff.toml
index 4eb8097..8670e6d 100644
--- a/.github/fixtures/test-bump-initial-tag-cli-arg/cliff.toml
+++ b/.github/fixtures/test-bump-initial-tag-cli-arg/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-bump-initial-tag-default/cliff.toml b/.github/fixtures/test-bump-initial-tag-default/cliff.toml
index 5da431c..091fd6b 100644
--- a/.github/fixtures/test-bump-initial-tag-default/cliff.toml
+++ b/.github/fixtures/test-bump-initial-tag-default/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-bump-initial-tag/cliff.toml b/.github/fixtures/test-bump-initial-tag/cliff.toml
index 38080e4..e584e66 100644
--- a/.github/fixtures/test-bump-initial-tag/cliff.toml
+++ b/.github/fixtures/test-bump-initial-tag/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-bump-version-keep-zerover/cliff.toml b/.github/fixtures/test-bump-version-keep-zerover/cliff.toml
index 1541430..dad5dcf 100644
--- a/.github/fixtures/test-bump-version-keep-zerover/cliff.toml
+++ b/.github/fixtures/test-bump-version-keep-zerover/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-bump-version/cliff.toml b/.github/fixtures/test-bump-version/cliff.toml
index ec2fc39..c48526d 100644
--- a/.github/fixtures/test-bump-version/cliff.toml
+++ b/.github/fixtures/test-bump-version/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-bumped-version/cliff.toml b/.github/fixtures/test-bumped-version/cliff.toml
index ec2fc39..c48526d 100644
--- a/.github/fixtures/test-bumped-version/cliff.toml
+++ b/.github/fixtures/test-bumped-version/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-commit-footers/cliff.toml b/.github/fixtures/test-commit-footers/cliff.toml
index e143f3b..94920a9 100644
--- a/.github/fixtures/test-commit-footers/cliff.toml
+++ b/.github/fixtures/test-commit-footers/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-commit-preprocessors/cliff.toml b/.github/fixtures/test-commit-preprocessors/cliff.toml
index 2d86f4c..63f6af2 100644
--- a/.github/fixtures/test-commit-preprocessors/cliff.toml
+++ b/.github/fixtures/test-commit-preprocessors/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-configure-from-cargo-toml/Cargo.toml b/.github/fixtures/test-configure-from-cargo-toml/Cargo.toml
index d44e73b..2cbf7a5 100644
--- a/.github/fixtures/test-configure-from-cargo-toml/Cargo.toml
+++ b/.github/fixtures/test-configure-from-cargo-toml/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.1.0"
 edition = "2021"
 
 [package.metadata.git-cliff.changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-custom-scope/cliff.toml b/.github/fixtures/test-custom-scope/cliff.toml
index fe30b90..d4dfdda 100644
--- a/.github/fixtures/test-custom-scope/cliff.toml
+++ b/.github/fixtures/test-custom-scope/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-custom-tag-pattern/cliff.toml b/.github/fixtures/test-custom-tag-pattern/cliff.toml
index fc3d5db..a3d1e0f 100644
--- a/.github/fixtures/test-custom-tag-pattern/cliff.toml
+++ b/.github/fixtures/test-custom-tag-pattern/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-date-order/cliff.toml b/.github/fixtures/test-date-order/cliff.toml
index 5504810..61d862d 100644
--- a/.github/fixtures/test-date-order/cliff.toml
+++ b/.github/fixtures/test-date-order/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-footer-template/cliff.toml b/.github/fixtures/test-footer-template/cliff.toml
index ebfa371..8b8de92 100644
--- a/.github/fixtures/test-footer-template/cliff.toml
+++ b/.github/fixtures/test-footer-template/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-header-template/cliff.toml b/.github/fixtures/test-header-template/cliff.toml
new file mode 100644
index 00000000..91a9f81
--- /dev/null
+++ b/.github/fixtures/test-header-template/cliff.toml
@@ -0,0 +1,31 @@
+[changelog]
+# template for the changelog footer
+header = """
+# Changelog
+{% for release in releases %}\
+    {% if release.version %}\
+        {% if release.previous.version %}\
+            <!--{{ release.previous.version }}..{{ release.version }}-->
+        {% endif %}\
+    {% else %}\
+        <!--{{ release.previous.version }}..HEAD-->
+    {% endif %}\
+{% endfor %}\
+"""
+# template for the changelog body
+# https://keats.github.io/tera/docs/#introduction
+body = """
+{% if version %}\
+    ## [{{ version | trim_start_matches(pat="v") }}]
+{% else %}\
+    ## [unreleased]
+{% endif %}\
+{% for group, commits in commits | group_by(attribute="group") %}
+    ### {{ group | upper_first }}
+    {% for commit in commits %}
+        - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\
+    {% endfor %}
+{% endfor %}\n
+"""
+# remove the leading and trailing whitespace from the templates
+trim = true
diff --git a/.github/fixtures/test-header-template/commit.sh b/.github/fixtures/test-header-template/commit.sh
new file mode 100644
index 00000000..ed35b19
--- /dev/null
+++ b/.github/fixtures/test-header-template/commit.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -e
+
+GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "feat: add feature 1"
+GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "feat: add feature 2"
+git tag v0.1.0
+
+GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "fix: fix feature 1"
+GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "fix: fix feature 2"
+git tag v0.2.0
+
+GIT_COMMITTER_DATE="2021-01-23 01:23:47" git commit --allow-empty -m "feat: add footer"
+git tag v3.0.0
+
+GIT_COMMITTER_DATE="2021-01-23 01:23:47" git commit --allow-empty -m "test: footer"
+GIT_COMMITTER_DATE="2021-01-23 01:23:47" git commit --allow-empty -m "perf: footer"
diff --git a/.github/fixtures/test-header-template/expected.md b/.github/fixtures/test-header-template/expected.md
new file mode 100644
index 00000000..f9b52bf
--- /dev/null
+++ b/.github/fixtures/test-header-template/expected.md
@@ -0,0 +1,33 @@
+# Changelog
+<!--v3.0.0..HEAD-->
+<!--v0.2.0..v3.0.0-->
+<!--v0.1.0..v0.2.0-->
+## [unreleased]
+
+### Perf
+
+- Footer
+
+### Test
+
+- Footer
+
+## [3.0.0]
+
+### Feat
+
+- Add footer
+
+## [0.2.0]
+
+### Fix
+
+- Fix feature 1
+- Fix feature 2
+
+## [0.1.0]
+
+### Feat
+
+- Add feature 1
+- Add feature 2
diff --git a/.github/fixtures/test-ignore-tags/cliff.toml b/.github/fixtures/test-ignore-tags/cliff.toml
index 62a6561..705f8fc 100644
--- a/.github/fixtures/test-ignore-tags/cliff.toml
+++ b/.github/fixtures/test-ignore-tags/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-keep-a-changelog-links-current-arg/cliff.toml b/.github/fixtures/test-keep-a-changelog-links-current-arg/cliff.toml
index c96c373..e6d5eaf 100644
--- a/.github/fixtures/test-keep-a-changelog-links-current-arg/cliff.toml
+++ b/.github/fixtures/test-keep-a-changelog-links-current-arg/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/.github/fixtures/test-keep-a-changelog-links-latest-arg/cliff.toml b/.github/fixtures/test-keep-a-changelog-links-latest-arg/cliff.toml
index c96c373..e6d5eaf 100644
--- a/.github/fixtures/test-keep-a-changelog-links-latest-arg/cliff.toml
+++ b/.github/fixtures/test-keep-a-changelog-links-latest-arg/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/.github/fixtures/test-keep-a-changelog-links-no-tags/cliff.toml b/.github/fixtures/test-keep-a-changelog-links-no-tags/cliff.toml
index c96c373..e6d5eaf 100644
--- a/.github/fixtures/test-keep-a-changelog-links-no-tags/cliff.toml
+++ b/.github/fixtures/test-keep-a-changelog-links-no-tags/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/.github/fixtures/test-keep-a-changelog-links-one-tag-bump-arg/cliff.toml b/.github/fixtures/test-keep-a-changelog-links-one-tag-bump-arg/cliff.toml
index c96c373..e6d5eaf 100644
--- a/.github/fixtures/test-keep-a-changelog-links-one-tag-bump-arg/cliff.toml
+++ b/.github/fixtures/test-keep-a-changelog-links-one-tag-bump-arg/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/.github/fixtures/test-keep-a-changelog-links-one-tag/cliff.toml b/.github/fixtures/test-keep-a-changelog-links-one-tag/cliff.toml
index c96c373..e6d5eaf 100644
--- a/.github/fixtures/test-keep-a-changelog-links-one-tag/cliff.toml
+++ b/.github/fixtures/test-keep-a-changelog-links-one-tag/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/.github/fixtures/test-keep-a-changelog-links-tag-arg/cliff.toml b/.github/fixtures/test-keep-a-changelog-links-tag-arg/cliff.toml
index c96c373..e6d5eaf 100644
--- a/.github/fixtures/test-keep-a-changelog-links-tag-arg/cliff.toml
+++ b/.github/fixtures/test-keep-a-changelog-links-tag-arg/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/.github/fixtures/test-keep-a-changelog-links-unreleased-arg/cliff.toml b/.github/fixtures/test-keep-a-changelog-links-unreleased-arg/cliff.toml
index c96c373..e6d5eaf 100644
--- a/.github/fixtures/test-keep-a-changelog-links-unreleased-arg/cliff.toml
+++ b/.github/fixtures/test-keep-a-changelog-links-unreleased-arg/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/.github/fixtures/test-keep-a-changelog-links/cliff.toml b/.github/fixtures/test-keep-a-changelog-links/cliff.toml
index 59ef355..f02b8cb 100644
--- a/.github/fixtures/test-keep-a-changelog-links/cliff.toml
+++ b/.github/fixtures/test-keep-a-changelog-links/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.
diff --git a/.github/fixtures/test-latest-with-one-tag/cliff.toml b/.github/fixtures/test-latest-with-one-tag/cliff.toml
index 5504810..61d862d 100644
--- a/.github/fixtures/test-latest-with-one-tag/cliff.toml
+++ b/.github/fixtures/test-latest-with-one-tag/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-limit-commits/cliff.toml b/.github/fixtures/test-limit-commits/cliff.toml
index f053d36..bac91dc 100644
--- a/.github/fixtures/test-limit-commits/cliff.toml
+++ b/.github/fixtures/test-limit-commits/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-no-exec/cliff.toml b/.github/fixtures/test-no-exec/cliff.toml
index 7a8024d..fd7d92d 100644
--- a/.github/fixtures/test-no-exec/cliff.toml
+++ b/.github/fixtures/test-no-exec/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-regex-replace-parser/cliff.toml b/.github/fixtures/test-regex-replace-parser/cliff.toml
index b464add..8b05dbf 100644
--- a/.github/fixtures/test-regex-replace-parser/cliff.toml
+++ b/.github/fixtures/test-regex-replace-parser/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-skip-breaking-changes/cliff.toml b/.github/fixtures/test-skip-breaking-changes/cliff.toml
index d057483..fdf5144 100644
--- a/.github/fixtures/test-skip-breaking-changes/cliff.toml
+++ b/.github/fixtures/test-skip-breaking-changes/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-skip-commits/cliff.toml b/.github/fixtures/test-skip-commits/cliff.toml
index 9a9d380..b26308c 100644
--- a/.github/fixtures/test-skip-commits/cliff.toml
+++ b/.github/fixtures/test-skip-commits/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-split-commits/cliff.toml b/.github/fixtures/test-split-commits/cliff.toml
index ec20df8..08d2919 100644
--- a/.github/fixtures/test-split-commits/cliff.toml
+++ b/.github/fixtures/test-split-commits/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-topo-order-arg/cliff.toml b/.github/fixtures/test-topo-order-arg/cliff.toml
index 5504810..61d862d 100644
--- a/.github/fixtures/test-topo-order-arg/cliff.toml
+++ b/.github/fixtures/test-topo-order-arg/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n
diff --git a/.github/fixtures/test-topo-order/cliff.toml b/.github/fixtures/test-topo-order/cliff.toml
index f4bbb0a..28911d2 100644
--- a/.github/fixtures/test-topo-order/cliff.toml
+++ b/.github/fixtures/test-topo-order/cliff.toml
@@ -1,5 +1,5 @@
 [changelog]
-# changelog header
+# template for the changelog footer
 header = """
 # Changelog\n
 All notable changes to this project will be documented in this file.\n