Repository: orhun/git-cliff
Pull request #1138 (2025-04-28 14:24:35)

feat(context): add release commit range
## Description

Adds the used `commit_range` for each release. 
`commit_range` includes the full length commit ids (`commit_range.from` and `commit_range.to`)
Also added related docs (and documentation for the sort_commits default)

## Motivation and Context

See #1041

## How Has This Been Tested?

Via integration_test and three new fixtures.

## 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

## Checklist:

- [ ] 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.

random sideinfo:
`repo::test::propagate_error_if_no_repo_found` fails under windows.
clippy also reports errors under windows.


codecov-commenter: ## [Codecov](https://app.codecov.io/gh/orhun/git-cliff/pull/1138?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 `23.07692%` with `10 lines` in your changes missing coverage. Please review.
> Project coverage is 40.09%. Comparing base [(`451a694`)](https://app.codecov.io/gh/orhun/git-cliff/commit/451a694ad4d9db1f0545ef92bd0c6643b3d26600?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 [(`b384e40`)](https://app.codecov.io/gh/orhun/git-cliff/commit/b384e4057f8d5b89d415a24f916737bf2b66d94e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z).

| [Files with missing lines](https://app.codecov.io/gh/orhun/git-cliff/pull/1138?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/src/lib.rs](https://app.codecov.io/gh/orhun/git-cliff/pull/1138?src=pr&el=tree&filepath=git-cliff%2Fsrc%2Flib.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z#diff-Z2l0LWNsaWZmL3NyYy9saWIucnM=) | 0.00% | [10 Missing :warning: ](https://app.codecov.io/gh/orhun/git-cliff/pull/1138?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) |

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


```diff
@@            Coverage Diff             @@
##             main    #1138      +/-   ##
==========================================
- Coverage   40.16%   40.09%   -0.07%     
==========================================
  Files          21       21              
  Lines        1900     1911      +11     
==========================================
+ Hits          763      766       +3     
- Misses       1137     1145       +8     
```

| [Flag](https://app.codecov.io/gh/orhun/git-cliff/pull/1138/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/1138/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) | `40.09% <23.08%> (-0.07%)` | :arrow_down: |

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/1138?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).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
jdrst: ah interesting. i just updated my toolchain under windows so the clippy errors (and the linting error here) are probably explained by that.

Patch:
diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs
index e085a49..125b18b 100644
--- a/git-cliff-core/src/changelog.rs
+++ b/git-cliff-core/src/changelog.rs
@@ -1001,6 +1001,7 @@ mod test {
 					String::from("misc: use footer\n\nFooter: footer text"),
 				),
 			],
+			commit_range: None,
 			commit_id: Some(String::from("0bc123")),
 			timestamp: 50000000,
 			previous: None,
@@ -1089,6 +1090,7 @@ mod test {
 						String::from("revert(app): skip this commit"),
 					),
 				],
+				commit_range: None,
 				commit_id: None,
 				timestamp: 1000,
 				previous: Some(Box::new(test_release)),
diff --git a/git-cliff-core/src/commit.rs b/git-cliff-core/src/commit.rs
index 692ee6c..130c1b6 100644
--- a/git-cliff-core/src/commit.rs
+++ b/git-cliff-core/src/commit.rs
@@ -98,6 +98,25 @@ impl<'a> From<CommitSignature<'a>> for Signature {
 	}
 }
 
+/// Commit range (from..to)
+#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct Range {
+	/// Full commit SHA the range starts at
+	from: String,
+	/// Full commit SHA the range ends at
+	to:   String,
+}
+
+impl Range {
+	/// Creates a new [`Range`] from [`crate::commit::Commit`].
+	pub fn new(from: &Commit, to: &Commit) -> Self {
+		Self {
+			from: from.id.clone(),
+			to:   to.id.clone(),
+		}
+	}
+}
+
 /// Common commit object that is parsed from a repository.
 #[derive(Debug, Default, Clone, PartialEq, Deserialize)]
 #[serde(rename_all(serialize = "camelCase"))]
diff --git a/git-cliff-core/src/release.rs b/git-cliff-core/src/release.rs
index f382116..4fa1fd2 100644
--- a/git-cliff-core/src/release.rs
+++ b/git-cliff-core/src/release.rs
@@ -3,7 +3,10 @@ use std::collections::HashMap;
 use crate::commit::commits_to_conventional_commits;
 use crate::error::Result;
 use crate::{
-	commit::Commit,
+	commit::{
+		Commit,
+		Range,
+	},
 	config::Bump,
 	config::BumpType,
 };
@@ -48,6 +51,9 @@ pub struct Release<'a> {
 	pub previous:          Option<Box<Release<'a>>>,
 	/// Repository path.
 	pub repository:        Option<String>,
+	/// Commit range.
+	#[serde(rename = "commit_range")]
+	pub commit_range:      Option<Range>,
 	/// Submodule commits.
 	///
 	/// Maps submodule path to a list of commits.
@@ -202,6 +208,7 @@ mod test {
 					.iter()
 					.map(|v| Commit::from(v.to_string()))
 					.collect(),
+				commit_range: None,
 				commit_id: None,
 				timestamp: 0,
 				previous: Some(Box::new(Release {
@@ -419,6 +426,7 @@ mod test {
 					"6c34967147560ea09658776d4901709139b4ad66 should be fine",
 				)),
 			],
+			commit_range: None,
 			commit_id: None,
 			timestamp: 0,
 			previous: Some(Box::new(Release {
@@ -788,6 +796,7 @@ mod test {
 					"6c34967147560ea09658776d4901709139b4ad66 should be fine",
 				)),
 			],
+			commit_range: None,
 			commit_id: None,
 			timestamp: 0,
 			previous: Some(Box::new(Release {
@@ -1179,6 +1188,7 @@ mod test {
 					"6c34967147560ea09658776d4901709139b4ad66 should be fine",
 				)),
 			],
+			commit_range: None,
 			commit_id: None,
 			timestamp: 0,
 			previous: Some(Box::new(Release {
@@ -1538,6 +1548,7 @@ mod test {
 					"6c34967147560ea09658776d4901709139b4ad66 should be fine",
 				)),
 			],
+			commit_range: None,
 			commit_id: None,
 			timestamp: 0,
 			previous: Some(Box::new(Release {
diff --git a/git-cliff-core/src/template.rs b/git-cliff-core/src/template.rs
index 6256aec..186eb19 100644
--- a/git-cliff-core/src/template.rs
+++ b/git-cliff-core/src/template.rs
@@ -215,6 +215,7 @@ mod test {
 			.into_iter()
 			.filter_map(|c| c.into_conventional().ok())
 			.collect(),
+			commit_range: None,
 			commit_id: None,
 			timestamp: 0,
 			previous: None,
diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs
index a4620f4..a18dd5e 100644
--- a/git-cliff/src/lib.rs
+++ b/git-cliff/src/lib.rs
@@ -21,7 +21,10 @@ use args::{
 };
 use clap::ValueEnum;
 use git_cliff_core::changelog::Changelog;
-use git_cliff_core::commit::Commit;
+use git_cliff_core::commit::{
+	Commit,
+	Range,
+};
 use git_cliff_core::config::{
 	CommitParser,
 	Config,
@@ -439,8 +442,21 @@ fn process_repository<'a>(
 		}
 	}
 
-	if recurse_submodules {
-		for release in &mut releases {
+	for release in &mut releases {
+		// Set the commit ranges for all releases
+		if !release.commits.is_empty() {
+			release.commit_range = Some(match args.sort {
+				Sort::Oldest => Range::new(
+					release.commits.first().unwrap(),
+					release.commits.last().unwrap(),
+				),
+				Sort::Newest => Range::new(
+					release.commits.last().unwrap(),
+					release.commits.first().unwrap(),
+				),
+			})
+		}
+		if recurse_submodules {
 			process_submodules(repository, release, config.git.topo_order_commits)?;
 		}
 	}
diff --git a/website/docs/configuration/git.md b/website/docs/configuration/git.md
index c33ad71..b751d4d 100644
--- a/website/docs/configuration/git.md
+++ b/website/docs/configuration/git.md
@@ -275,6 +275,8 @@ Possible values:
 
 This can also be achieved by specifying the `--sort` command line argument.
 
+The default value is `oldest`.
+
 ### link_parsers
 
 An array of link parsers for extracting external references, and turning them into URLs, using regex.
diff --git a/website/docs/templating/context.md b/website/docs/templating/context.md
index 78c631b..d3e9b2a 100644
--- a/website/docs/templating/context.md
+++ b/website/docs/templating/context.md
@@ -64,6 +64,9 @@ following context is generated to use for templating:
   "commit_id": "a440c6eb26404be4877b7e3ad592bfaa5d4eb210 (release commit)",
   "timestamp": 1625169301,
   "repository": "/path/to/repository",
+  "commit_range": {
+      "from": "(id of the first commit used for this release)",
+      "to": "(id of the last commit used for this release)"
   "submodule_commits": {
     "<relative/submodule_path/in/repository>": [
       {
@@ -169,6 +172,10 @@ If [`conventional_commits`](/docs/configuration/git#conventional_commits) is set
   "commit_id": "a440c6eb26404be4877b7e3ad592bfaa5d4eb210 (release commit)",
   "timestamp": 1625169301,
   "repository": "/path/to/repository",
+  "commit_range": {
+      "from": "(id of the first commit used for this release)",
+      "to": "(id of the last commit used for this release)",
+  },
   "previous": {
     "version": "previous release"
   }
diff --git a/website/docs/usage/adding-commits.md b/website/docs/usage/adding-commits.md
index b86f5db..1839eee 100644
--- a/website/docs/usage/adding-commits.md
+++ b/website/docs/usage/adding-commits.md
@@ -27,3 +27,9 @@ 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"`
+
+:::info
+
+Adding custom commits without a SHA will affect the [`commit_range`](/docs/templating/context) template variable.
+
+:::


Test patch:
diff --git a/.github/fixtures/test-commit-range-with-given-range/cliff.toml b/.github/fixtures/test-commit-range-with-given-range/cliff.toml
new file mode 100644
index 00000000..075847e
--- /dev/null
+++ b/.github/fixtures/test-commit-range-with-given-range/cliff.toml
@@ -0,0 +1,29 @@
+# git-cliff ~ configuration file
+# https://git-cliff.org/docs/configuration
+
+[changelog]
+# A Tera template to be rendered for each release in the changelog.
+# See https://keats.github.io/tera/docs/#introduction
+body = """
+{% if version %}\
+    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
+{% else %}\
+    ## [unreleased]
+{% endif %}
+{{ commit_range.from }}..{{ commit_range.to }}
+{% for group, commits in commits | group_by(attribute="group") %}
+    ### {{ group | upper_first }}
+    {% for commit in commits %}
+        - {{ commit.message | upper_first }}\
+    {% endfor %}
+{% endfor %}\n
+"""
+
+[git]
+# An array of regex based parsers for extracting data from the commit message.
+# Assigns commits to groups.
+# Optionally sets the commit's `scope` and can decide to exclude commits from further processing.
+commit_parsers = [
+    { message = "^feat", group = "Features", default_scope = "app" },
+    { message = "^fix", group = "Bug Fixes", scope = "cli" },
+]
diff --git a/.github/fixtures/test-commit-range-with-given-range/commit.sh b/.github/fixtures/test-commit-range-with-given-range/commit.sh
new file mode 100755
index 00000000..0089053
--- /dev/null
+++ b/.github/fixtures/test-commit-range-with-given-range/commit.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -e
+
+git remote add origin https://github.com/orhun/git-cliff-readme-example
+git pull origin master
+git fetch --tags
diff --git a/.github/fixtures/test-commit-range-with-given-range/expected.md b/.github/fixtures/test-commit-range-with-given-range/expected.md
new file mode 100644
index 00000000..e24fe72
--- /dev/null
+++ b/.github/fixtures/test-commit-range-with-given-range/expected.md
@@ -0,0 +1,30 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+## [unreleased]
+
+a140cef0405e0bcbfb5de44ff59e091527d91b38..a9d4050212a18f6b3bd76e2e41fbb9045d268b80
+
+### Bug Fixes
+
+- Rename help argument due to conflict
+
+### Features
+
+- Add ability to parse arrays
+- Support multiple file formats
+
+### Chore
+
+- Add release script
+
+### Docs
+
+- Add tested usage example
+
+### Refactor
+
+- Expose string functions
+
+<!-- generated by git-cliff -->
diff --git a/.github/fixtures/test-commit-range-with-sort-commits/cliff.toml b/.github/fixtures/test-commit-range-with-sort-commits/cliff.toml
new file mode 100644
index 00000000..9f551bc
--- /dev/null
+++ b/.github/fixtures/test-commit-range-with-sort-commits/cliff.toml
@@ -0,0 +1,30 @@
+# git-cliff ~ configuration file
+# https://git-cliff.org/docs/configuration
+
+[changelog]
+# A Tera template to be rendered for each release in the changelog.
+# See https://keats.github.io/tera/docs/#introduction
+body = """
+{% if version %}\
+    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
+{% else %}\
+    ## [unreleased]
+{% endif %}
+{{ commit_range.from }}..{{ commit_range.to }}
+{% for group, commits in commits | group_by(attribute="group") %}
+    ### {{ group | upper_first }}
+    {% for commit in commits %}
+        - {{ commit.message | upper_first }}\
+    {% endfor %}
+{% endfor %}\n
+"""
+
+[git]
+# An array of regex based parsers for extracting data from the commit message.
+# Assigns commits to groups.
+# Optionally sets the commit's `scope` and can decide to exclude commits from further processing.
+commit_parsers = [
+    { message = "^feat", group = "Features", default_scope = "app" },
+    { message = "^fix", group = "Bug Fixes", scope = "cli" },
+]
+sort_commits = "newest"
diff --git a/.github/fixtures/test-commit-range-with-sort-commits/commit.sh b/.github/fixtures/test-commit-range-with-sort-commits/commit.sh
new file mode 100755
index 00000000..0089053
--- /dev/null
+++ b/.github/fixtures/test-commit-range-with-sort-commits/commit.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -e
+
+git remote add origin https://github.com/orhun/git-cliff-readme-example
+git pull origin master
+git fetch --tags
diff --git a/.github/fixtures/test-commit-range-with-sort-commits/expected.md b/.github/fixtures/test-commit-range-with-sort-commits/expected.md
new file mode 100644
index 00000000..ab0ea53
--- /dev/null
+++ b/.github/fixtures/test-commit-range-with-sort-commits/expected.md
@@ -0,0 +1,43 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+## [unreleased]
+
+a9d4050212a18f6b3bd76e2e41fbb9045d268b80..df6aef41292f3ffe5887754232e6ea7831c50ba5
+
+### Features
+
+- Use cache while fetching pages
+- Support multiple file formats
+
+## [1.0.1] - 2021-07-18
+
+e4fd3cf8e2e6f49c0b57f66416e886c37cbb3715..06412ac1dd4071006c465dde6597a21d4367a158
+
+### Chore
+
+- Add release script
+
+### Refactor
+
+- Expose string functions
+
+## [1.0.0] - 2021-07-18
+
+a78bc368e9ee382a3016c0c4bab41f7de4503bcd..ad27b43e8032671afb4809a1a3ecf12f45c60e0e
+
+### Bug Fixes
+
+- Rename help argument due to conflict
+
+### Features
+
+- Add ability to parse arrays
+
+### Docs
+
+- Add tested usage example
+- Add README.md
+
+<!-- generated by git-cliff -->
diff --git a/.github/fixtures/test-commit-range/cliff.toml b/.github/fixtures/test-commit-range/cliff.toml
new file mode 100644
index 00000000..075847e
--- /dev/null
+++ b/.github/fixtures/test-commit-range/cliff.toml
@@ -0,0 +1,29 @@
+# git-cliff ~ configuration file
+# https://git-cliff.org/docs/configuration
+
+[changelog]
+# A Tera template to be rendered for each release in the changelog.
+# See https://keats.github.io/tera/docs/#introduction
+body = """
+{% if version %}\
+    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
+{% else %}\
+    ## [unreleased]
+{% endif %}
+{{ commit_range.from }}..{{ commit_range.to }}
+{% for group, commits in commits | group_by(attribute="group") %}
+    ### {{ group | upper_first }}
+    {% for commit in commits %}
+        - {{ commit.message | upper_first }}\
+    {% endfor %}
+{% endfor %}\n
+"""
+
+[git]
+# An array of regex based parsers for extracting data from the commit message.
+# Assigns commits to groups.
+# Optionally sets the commit's `scope` and can decide to exclude commits from further processing.
+commit_parsers = [
+    { message = "^feat", group = "Features", default_scope = "app" },
+    { message = "^fix", group = "Bug Fixes", scope = "cli" },
+]
diff --git a/.github/fixtures/test-commit-range/commit.sh b/.github/fixtures/test-commit-range/commit.sh
new file mode 100755
index 00000000..0089053
--- /dev/null
+++ b/.github/fixtures/test-commit-range/commit.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -e
+
+git remote add origin https://github.com/orhun/git-cliff-readme-example
+git pull origin master
+git fetch --tags
diff --git a/.github/fixtures/test-commit-range/expected.md b/.github/fixtures/test-commit-range/expected.md
new file mode 100644
index 00000000..9caa12a
--- /dev/null
+++ b/.github/fixtures/test-commit-range/expected.md
@@ -0,0 +1,43 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+## [unreleased]
+
+a9d4050212a18f6b3bd76e2e41fbb9045d268b80..df6aef41292f3ffe5887754232e6ea7831c50ba5
+
+### Features
+
+- Support multiple file formats
+- Use cache while fetching pages
+
+## [1.0.1] - 2021-07-18
+
+e4fd3cf8e2e6f49c0b57f66416e886c37cbb3715..06412ac1dd4071006c465dde6597a21d4367a158
+
+### Chore
+
+- Add release script
+
+### Refactor
+
+- Expose string functions
+
+## [1.0.0] - 2021-07-18
+
+a78bc368e9ee382a3016c0c4bab41f7de4503bcd..ad27b43e8032671afb4809a1a3ecf12f45c60e0e
+
+### Bug Fixes
+
+- Rename help argument due to conflict
+
+### Features
+
+- Add ability to parse arrays
+
+### Docs
+
+- Add README.md
+- Add tested usage example
+
+<!-- generated by git-cliff -->
diff --git a/.github/fixtures/test-from-context-does-not-discard-fields/context.json b/.github/fixtures/test-from-context-does-not-discard-fields/context.json
index 22ccf42..1029b0d 100644
--- a/.github/fixtures/test-from-context-does-not-discard-fields/context.json
+++ b/.github/fixtures/test-from-context-does-not-discard-fields/context.json
@@ -200,7 +200,12 @@
         "message": "fix 1, use scope as group",
         "body": "Body fix 1",
         "footers": [
-          { "token": "Fix", "separator": ":", "value": "#1", "breaking": false }
+          {
+            "token": "Fix",
+            "separator": ":",
+            "value": "#1",
+            "breaking": false
+          }
         ],
         "group": "fix",
         "breaking_description": null,
@@ -314,7 +319,12 @@
             "value": "#3",
             "breaking": false
           },
-          { "token": "Fix", "separator": ":", "value": "#4", "breaking": false }
+          {
+            "token": "Fix",
+            "separator": ":",
+            "value": "#4",
+            "breaking": false
+          }
         ],
         "group": "fix",
         "breaking_description": null,
@@ -375,19 +385,37 @@
       "timestamp": 0,
       "previous": null,
       "repository": null,
+      "commit_range": null,
       "submodule_commits": {},
       "extra": null,
-      "github": { "contributors": [] },
-      "gitlab": { "contributors": [] },
-      "gitea": { "contributors": [] },
-      "bitbucket": { "contributors": [] }
+      "github": {
+        "contributors": []
+      },
+      "gitlab": {
+        "contributors": []
+      },
+      "gitea": {
+        "contributors": []
+      },
+      "bitbucket": {
+        "contributors": []
+      }
     },
     "repository": "/path/to/repository",
+    "commit_range": null,
     "submodule_commits": {},
     "extra": null,
-    "github": { "contributors": [] },
-    "gitlab": { "contributors": [] },
-    "gitea": { "contributors": [] },
-    "bitbucket": { "contributors": [] }
+    "github": {
+      "contributors": []
+    },
+    "gitlab": {
+      "contributors": []
+    },
+    "gitea": {
+      "contributors": []
+    },
+    "bitbucket": {
+      "contributors": []
+    }
   }
-]
+]
\ No newline at end of file
diff --git a/.github/fixtures/test-from-context/context.json b/.github/fixtures/test-from-context/context.json
index a6ac41b..d7dd053 100644
--- a/.github/fixtures/test-from-context/context.json
+++ b/.github/fixtures/test-from-context/context.json
@@ -1,632 +1,638 @@
 [
-    {
-        "version": null,
-        "message": null,
-        "commits": [
-            {
-                "id": "c7541276668616c136d12a9ccd87087314d66fb6",
-                "message": "add tests",
-                "body": null,
-                "footers": [],
-                "group": "test",
-                "breaking_description": null,
-                "breaking": false,
-                "scope": null,
-                "links": [],
-                "author": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1723730666
-                },
-                "committer": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1649201113
-                },
-                "conventional": true,
-                "merge_commit": false,
-                "github": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitlab": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitea": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "bitbucket": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                }
-            }
-        ],
-        "commit_id": null,
-        "timestamp": 0,
-        "previous": {
-            "version": "v0.2.0",
-            "message": null,
-            "commits": [
-                {
-                    "id": "ce8cecc9834e5cbdb5b4e79c8260cace3c0dd949",
-                    "message": "feat(gui): add feature 2\n",
-                    "group": null,
-                    "scope": null,
-                    "links": [],
-                    "author": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1723730666
-                    },
-                    "committer": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1649201111
-                    },
-                    "conventional": false,
-                    "merge_commit": false,
-                    "github": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitlab": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitea": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "bitbucket": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    }
-                },
-                {
-                    "id": "5061081d6272b1da2146fab49d803c193db309d9",
-                    "message": "fix(gui): fix feature 2\n",
-                    "group": null,
-                    "scope": null,
-                    "links": [],
-                    "author": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1723730666
-                    },
-                    "committer": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1649201112
-                    },
-                    "conventional": false,
-                    "merge_commit": false,
-                    "github": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitlab": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitea": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "bitbucket": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    }
-                }
-            ],
-            "commit_id": "5061081d6272b1da2146fab49d803c193db309d9",
-            "timestamp": 1649201112,
-            "previous": null,
-            "repository": "/home/johndoe/repo/",
-            "submodule_commits": {},
-            "github": {
-                "contributors": []
-            },
-            "gitlab": {
-                "contributors": []
-            },
-            "gitea": {
-                "contributors": []
-            },
-            "bitbucket": {
-                "contributors": []
-            }
+  {
+    "version": null,
+    "message": null,
+    "commits": [
+      {
+        "id": "c7541276668616c136d12a9ccd87087314d66fb6",
+        "message": "add tests",
+        "body": null,
+        "footers": [],
+        "group": "test",
+        "breaking_description": null,
+        "breaking": false,
+        "scope": null,
+        "links": [],
+        "author": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1723730666
         },
-        "repository": "/home/johndoe/repo/",
-        "submodule_commits": {},
+        "committer": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1649201113
+        },
+        "conventional": true,
+        "merge_commit": false,
         "github": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "gitlab": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "gitea": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "bitbucket": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        }
+      }
+    ],
+    "commit_id": null,
+    "timestamp": 0,
+    "previous": {
+      "version": "v0.2.0",
+      "message": null,
+      "commits": [
+        {
+          "id": "ce8cecc9834e5cbdb5b4e79c8260cace3c0dd949",
+          "message": "feat(gui): add feature 2\n",
+          "group": null,
+          "scope": null,
+          "links": [],
+          "author": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1723730666
+          },
+          "committer": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1649201111
+          },
+          "conventional": false,
+          "merge_commit": false,
+          "github": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitlab": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitea": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "bitbucket": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          }
+        },
+        {
+          "id": "5061081d6272b1da2146fab49d803c193db309d9",
+          "message": "fix(gui): fix feature 2\n",
+          "group": null,
+          "scope": null,
+          "links": [],
+          "author": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1723730666
+          },
+          "committer": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1649201112
+          },
+          "conventional": false,
+          "merge_commit": false,
+          "github": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitlab": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitea": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "bitbucket": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          }
         }
+      ],
+      "commit_id": "5061081d6272b1da2146fab49d803c193db309d9",
+      "timestamp": 1649201112,
+      "previous": null,
+      "repository": "/home/johndoe/repo/",
+      "commit_range": null,
+      "submodule_commits": {},
+      "github": {
+        "contributors": []
+      },
+      "gitlab": {
+        "contributors": []
+      },
+      "gitea": {
+        "contributors": []
+      },
+      "bitbucket": {
+        "contributors": []
+      }
+    },
+    "repository": "/home/johndoe/repo/",
+    "commit_range": null,
+    "submodule_commits": {},
+    "github": {
+      "contributors": []
+    },
+    "gitlab": {
+      "contributors": []
+    },
+    "gitea": {
+      "contributors": []
     },
-    {
-        "version": "v0.2.0",
-        "message": null,
-        "commits": [
-            {
-                "id": "ce8cecc9834e5cbdb5b4e79c8260cace3c0dd949",
-                "message": "add feature 2",
-                "body": null,
-                "footers": [],
-                "group": "Features",
-                "breaking_description": null,
-                "breaking": false,
-                "scope": "gui",
-                "links": [],
-                "author": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1723730666
-                },
-                "committer": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1649201111
-                },
-                "conventional": true,
-                "merge_commit": false,
-                "github": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitlab": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitea": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "bitbucket": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                }
-            },
-            {
-                "id": "5061081d6272b1da2146fab49d803c193db309d9",
-                "message": "fix feature 2",
-                "body": null,
-                "footers": [],
-                "group": "Bug Fixes",
-                "breaking_description": null,
-                "breaking": false,
-                "scope": "cli",
-                "links": [],
-                "author": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1723730666
-                },
-                "committer": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1649201112
-                },
-                "conventional": true,
-                "merge_commit": false,
-                "extra": {
-                    "note": "that was a tough one"
-                },
-                "github": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitlab": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitea": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "bitbucket": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                }
-            }
-        ],
-        "commit_id": "5061081d6272b1da2146fab49d803c193db309d9",
-        "timestamp": 1649201112,
-        "previous": {
-            "version": "v0.1.0",
-            "message": null,
-            "commits": [
-                {
-                    "id": "c6ba7fe73c3b639ff2f934f3f98e5f50350ab463",
-                    "message": "Initial commit\n",
-                    "group": null,
-                    "scope": null,
-                    "links": [],
-                    "author": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1723730666
-                    },
-                    "committer": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1649201108
-                    },
-                    "conventional": false,
-                    "merge_commit": false,
-                    "github": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitlab": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitea": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "bitbucket": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    }
-                },
-                {
-                    "id": "9c10e506bf103998e303d8164c7b4a1a841e2471",
-                    "message": "feat: add feature 1\n",
-                    "group": null,
-                    "scope": null,
-                    "links": [],
-                    "author": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1723730666
-                    },
-                    "committer": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1649201109
-                    },
-                    "conventional": false,
-                    "merge_commit": false,
-                    "github": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitlab": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitea": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "bitbucket": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    }
-                },
-                {
-                    "id": "98b1464c2f99a0c6812ce6d3ea4a5b953dd35fe2",
-                    "message": "fix: fix feature 1\n",
-                    "group": null,
-                    "scope": null,
-                    "links": [],
-                    "author": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1723730666
-                    },
-                    "committer": {
-                        "name": "John Doe",
-                        "email": "john@doe.com",
-                        "timestamp": 1649201110
-                    },
-                    "conventional": false,
-                    "merge_commit": false,
-                    "github": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitlab": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "gitea": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    },
-                    "bitbucket": {
-                        "username": null,
-                        "pr_title": null,
-                        "pr_number": null,
-                        "pr_labels": [],
-                        "is_first_time": false
-                    }
-                }
-            ],
-            "commit_id": "98b1464c2f99a0c6812ce6d3ea4a5b953dd35fe2",
-            "timestamp": 1649201110,
-            "previous": null,
-            "repository": "/home/johndoe/repo/",
-            "submodule_commits": {},
-            "github": {
-                "contributors": []
-            },
-            "gitlab": {
-                "contributors": []
-            },
-            "gitea": {
-                "contributors": []
-            },
-            "bitbucket": {
-                "contributors": []
-            }
+    "bitbucket": {
+      "contributors": []
+    }
+  },
+  {
+    "version": "v0.2.0",
+    "message": null,
+    "commits": [
+      {
+        "id": "ce8cecc9834e5cbdb5b4e79c8260cace3c0dd949",
+        "message": "add feature 2",
+        "body": null,
+        "footers": [],
+        "group": "Features",
+        "breaking_description": null,
+        "breaking": false,
+        "scope": "gui",
+        "links": [],
+        "author": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1723730666
+        },
+        "committer": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1649201111
         },
+        "conventional": true,
+        "merge_commit": false,
+        "github": {
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        },
+        "gitlab": {
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        },
+        "gitea": {
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        },
+        "bitbucket": {
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        }
+      },
+      {
+        "id": "5061081d6272b1da2146fab49d803c193db309d9",
+        "message": "fix feature 2",
+        "body": null,
+        "footers": [],
+        "group": "Bug Fixes",
+        "breaking_description": null,
+        "breaking": false,
+        "scope": "cli",
+        "links": [],
+        "author": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1723730666
+        },
+        "committer": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1649201112
+        },
+        "conventional": true,
+        "merge_commit": false,
         "extra": {
-            "note": "This is so awesome!"
+          "note": "that was a tough one"
         },
-        "repository": "/home/johndoe/repo/",
-        "submodule_commits": {},
         "github": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "gitlab": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "gitea": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "bitbucket": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        }
+      }
+    ],
+    "commit_id": "5061081d6272b1da2146fab49d803c193db309d9",
+    "timestamp": 1649201112,
+    "previous": {
+      "version": "v0.1.0",
+      "message": null,
+      "commits": [
+        {
+          "id": "c6ba7fe73c3b639ff2f934f3f98e5f50350ab463",
+          "message": "Initial commit\n",
+          "group": null,
+          "scope": null,
+          "links": [],
+          "author": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1723730666
+          },
+          "committer": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1649201108
+          },
+          "conventional": false,
+          "merge_commit": false,
+          "github": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitlab": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitea": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "bitbucket": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          }
+        },
+        {
+          "id": "9c10e506bf103998e303d8164c7b4a1a841e2471",
+          "message": "feat: add feature 1\n",
+          "group": null,
+          "scope": null,
+          "links": [],
+          "author": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1723730666
+          },
+          "committer": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1649201109
+          },
+          "conventional": false,
+          "merge_commit": false,
+          "github": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitlab": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitea": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "bitbucket": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          }
+        },
+        {
+          "id": "98b1464c2f99a0c6812ce6d3ea4a5b953dd35fe2",
+          "message": "fix: fix feature 1\n",
+          "group": null,
+          "scope": null,
+          "links": [],
+          "author": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1723730666
+          },
+          "committer": {
+            "name": "John Doe",
+            "email": "john@doe.com",
+            "timestamp": 1649201110
+          },
+          "conventional": false,
+          "merge_commit": false,
+          "github": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitlab": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "gitea": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          },
+          "bitbucket": {
+            "username": null,
+            "pr_title": null,
+            "pr_number": null,
+            "pr_labels": [],
+            "is_first_time": false
+          }
         }
+      ],
+      "commit_id": "98b1464c2f99a0c6812ce6d3ea4a5b953dd35fe2",
+      "timestamp": 1649201110,
+      "previous": null,
+      "repository": "/home/johndoe/repo/",
+      "commit_range": null,
+      "submodule_commits": {},
+      "github": {
+        "contributors": []
+      },
+      "gitlab": {
+        "contributors": []
+      },
+      "gitea": {
+        "contributors": []
+      },
+      "bitbucket": {
+        "contributors": []
+      }
     },
-    {
-        "version": "v0.1.0",
-        "message": null,
-        "commits": [
-            {
-                "id": "9c10e506bf103998e303d8164c7b4a1a841e2471",
-                "message": "add feature 1",
-                "body": null,
-                "footers": [],
-                "group": "Features",
-                "breaking_description": null,
-                "breaking": false,
-                "scope": "app",
-                "links": [],
-                "author": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1723730666
-                },
-                "committer": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1649201109
-                },
-                "conventional": true,
-                "merge_commit": false,
-                "github": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitlab": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitea": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "bitbucket": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                }
-            },
-            {
-                "id": "98b1464c2f99a0c6812ce6d3ea4a5b953dd35fe2",
-                "message": "fix feature 1",
-                "body": null,
-                "footers": [],
-                "group": "Bug Fixes",
-                "breaking_description": null,
-                "breaking": false,
-                "scope": "cli",
-                "links": [],
-                "author": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1723730666
-                },
-                "committer": {
-                    "name": "John Doe",
-                    "email": "john@doe.com",
-                    "timestamp": 1649201110
-                },
-                "conventional": true,
-                "merge_commit": false,
-                "github": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitlab": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "gitea": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                },
-                "bitbucket": {
-                    "username": null,
-                    "pr_title": null,
-                    "pr_number": null,
-                    "pr_labels": [],
-                    "is_first_time": false
-                }
-            }
-        ],
-        "commit_id": "98b1464c2f99a0c6812ce6d3ea4a5b953dd35fe2",
-        "timestamp": 1649201110,
-        "previous": {
-            "version": null,
-            "message": null,
-            "commits": [],
-            "commit_id": null,
-            "timestamp": 0,
-            "previous": null,
-            "repository": null,
-            "submodule_commits": {},
-            "github": {
-                "contributors": []
-            },
-            "gitlab": {
-                "contributors": []
-            },
-            "gitea": {
-                "contributors": []
-            },
-            "bitbucket": {
-                "contributors": []
-            }
+    "extra": {
+      "note": "This is so awesome!"
+    },
+    "repository": "/home/johndoe/repo/",
+    "commit_range": null,
+    "submodule_commits": {},
+    "github": {
+      "contributors": []
+    },
+    "gitlab": {
+      "contributors": []
+    },
+    "gitea": {
+      "contributors": []
+    },
+    "bitbucket": {
+      "contributors": []
+    }
+  },
+  {
+    "version": "v0.1.0",
+    "message": null,
+    "commits": [
+      {
+        "id": "9c10e506bf103998e303d8164c7b4a1a841e2471",
+        "message": "add feature 1",
+        "body": null,
+        "footers": [],
+        "group": "Features",
+        "breaking_description": null,
+        "breaking": false,
+        "scope": "app",
+        "links": [],
+        "author": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1723730666
+        },
+        "committer": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1649201109
+        },
+        "conventional": true,
+        "merge_commit": false,
+        "github": {
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        },
+        "gitlab": {
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
-        "repository": "/home/johndoe/repo/",
-        "submodule_commits": {},
+        "gitea": {
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        },
+        "bitbucket": {
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
+        }
+      },
+      {
+        "id": "98b1464c2f99a0c6812ce6d3ea4a5b953dd35fe2",
+        "message": "fix feature 1",
+        "body": null,
+        "footers": [],
+        "group": "Bug Fixes",
+        "breaking_description": null,
+        "breaking": false,
+        "scope": "cli",
+        "links": [],
+        "author": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1723730666
+        },
+        "committer": {
+          "name": "John Doe",
+          "email": "john@doe.com",
+          "timestamp": 1649201110
+        },
+        "conventional": true,
+        "merge_commit": false,
         "github": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "gitlab": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "gitea": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         },
         "bitbucket": {
-            "contributors": []
+          "username": null,
+          "pr_title": null,
+          "pr_number": null,
+          "pr_labels": [],
+          "is_first_time": false
         }
+      }
+    ],
+    "commit_id": "98b1464c2f99a0c6812ce6d3ea4a5b953dd35fe2",
+    "timestamp": 1649201110,
+    "previous": {
+      "version": null,
+      "message": null,
+      "commits": [],
+      "commit_id": null,
+      "timestamp": 0,
+      "previous": null,
+      "repository": null,
+      "commit_range": null,
+      "submodule_commits": {},
+      "github": {
+        "contributors": []
+      },
+      "gitlab": {
+        "contributors": []
+      },
+      "gitea": {
+        "contributors": []
+      },
+      "bitbucket": {
+        "contributors": []
+      }
+    },
+    "repository": "/home/johndoe/repo/",
+    "commit_range": null,
+    "submodule_commits": {},
+    "github": {
+      "contributors": []
+    },
+    "gitlab": {
+      "contributors": []
+    },
+    "gitea": {
+      "contributors": []
+    },
+    "bitbucket": {
+      "contributors": []
     }
-]
+  }
+]
\ No newline at end of file
diff --git a/.github/workflows/test-fixtures.yml b/.github/workflows/test-fixtures.yml
index c139813..4ba1ea1 100644
--- a/.github/workflows/test-fixtures.yml
+++ b/.github/workflows/test-fixtures.yml
@@ -124,6 +124,10 @@ jobs:
           - fixtures-name: test-remote-config
             command: --config-url "https://github.com/orhun/git-cliff/blob/main/.github/fixtures/new-fixture-template/cliff.toml?raw=true"
           - fixtures-name: test-topo-order-commits
+          - fixtures-name: test-commit-range
+          - fixtures-name: test-commit-range-with-sort-commits
+          - fixtures-name: test-commit-range-with-given-range
+            command: a140cef^..a9d4050 --ignore-tags "."
 
     steps:
       - name: Checkout
diff --git a/git-cliff-core/tests/integration_test.rs b/git-cliff-core/tests/integration_test.rs
index 5d10f88..96e53c0 100644
--- a/git-cliff-core/tests/integration_test.rs
+++ b/git-cliff-core/tests/integration_test.rs
@@ -1,5 +1,6 @@
 use git_cliff_core::commit::{
 	Commit,
+	Range,
 	Signature,
 };
 use git_cliff_core::config::{
@@ -24,6 +25,8 @@ fn generate_changelog() -> Result<()> {
 		body:           String::from(
 			r#"
 ## Release {{ version }} - <DATE>
+
+{{ commit_range.from }}..{{ commit_range.to }}
 {% for group, commits in commits | group_by(attribute="group") %}
 ### {{ group }}
 {% for commit in commits %}
@@ -153,13 +156,25 @@ fn generate_changelog() -> Result<()> {
 		timestamp: 0x0,
 	};
 
-	let releases = vec![
-		Release {
-			version:   Some(String::from("v2.0.0")),
-			message: None,
-            extra: None,
-			commits:   vec![
+	let release_v1_commits = vec![
+		Commit::new(
+			String::from("0bc123"),
+			String::from("feat: add cool features"),
+		),
+		Commit::new(String::from("0werty"), String::from("fix: fix stuff")),
+		Commit::new(String::from("0w3rty"), String::from("fix: fix more stuff")),
+		Commit::new(String::from("0jkl12"), String::from("chore: do nothing")),
+	]
+	.into_iter()
+	.filter_map(|c| c.into_conventional().ok())
+	.collect::<Vec<Commit>>();
 
+	let release_v1_commit_range = Range::new(
+		release_v1_commits.first().unwrap(),
+		release_v1_commits.last().unwrap(),
+	);
+
+	let release_v2_commits = vec![
 				Commit::new(
 					String::from("000abc"),
 					String::from("Add unconventional commit"),
@@ -198,10 +213,23 @@ fn generate_changelog() -> Result<()> {
 			]
 			.iter()
 			.filter_map(|c| c.process(&git_config).ok())
-			.collect::<Vec<Commit>>(),
+			.collect::<Vec<Commit>>();
+
+	let release_v2_commit_range = Range::new(
+		release_v2_commits.first().unwrap(),
+		release_v2_commits.last().unwrap(),
+	);
+
+	let releases = vec![
+		Release {
+			version: Some(String::from("v2.0.0")),
+			message: None,
+			extra: None,
+			commits: release_v2_commits,
+			commit_range: Some(release_v2_commit_range),
 			commit_id: None,
 			timestamp: 0,
-			previous:  None,
+			previous: None,
 			repository: Some(String::from("/root/repo")),
 			submodule_commits: HashMap::new(),
 			#[cfg(feature = "github")]
@@ -222,30 +250,14 @@ fn generate_changelog() -> Result<()> {
 			},
 		},
 		Release {
-			version:   Some(String::from("v1.0.0")),
+			version: Some(String::from("v1.0.0")),
 			message: None,
-            extra: None,
-			commits:   vec![
-				Commit::new(
-					String::from("0bc123"),
-					String::from("feat: add cool features"),
-				),
-				Commit::new(String::from("0werty"), String::from("fix: fix stuff")),
-				Commit::new(
-					String::from("0w3rty"),
-					String::from("fix: fix more stuff"),
-				),
-				Commit::new(
-					String::from("0jkl12"),
-					String::from("chore: do nothing"),
-				),
-			]
-			.into_iter()
-			.filter_map(|c| c.into_conventional().ok())
-			.collect::<Vec<Commit>>(),
+			extra: None,
+			commits: release_v1_commits,
+			commit_range: Some(release_v1_commit_range),
 			commit_id: None,
 			timestamp: 0,
-			previous:  None,
+			previous: None,
 			repository: Some(String::from("/root/repo")),
 			submodule_commits: HashMap::new(),
 			#[cfg(feature = "github")]
@@ -295,6 +307,8 @@ fn generate_changelog() -> Result<()> {
 
 ## Release v2.0.0 - 2023
 
+abc123..hjdfas32
+
 ### docs
 - *(cool)* testing author filtering
 
@@ -314,6 +328,8 @@ fn generate_changelog() -> Result<()> {
 
 ## Release v1.0.0 - 2023
 
+0bc123..0jkl12
+
 ### chore
 - do nothing