Repository: orhun/git-cliff
Pull request #901 (2024-10-02 14:16:43)

fix(changelog): include the root commit when `--latest` is used with one tag
## Description, Motivation and Context

This is a proof of concept implementation for the fix of including the `root` commit of a repository when `git-cliff` is called with the `--latest` flag and only ONE tag exists in the repository.

Ranges passed to to `Repository::commits` are left-exclusive. This works well when we have two or more tags:

```
- sha0 (old_tag)
- sha1
- sha2
- sha3 (new_tag)
```

We'll get the range argument `sha0..sha3`, which will make the `::commits` call capture only the `sha1`, `sha2` and `sha3` commits, which is both expected and great.

However, when dealing with a _first_ release, and a _single_ tag, we'd still like the root commit of the repository to be included in the report. Think of it as having a tag _before_ the root commit itself. 

In this case, with the current implementation of `::commits` we get the range `root_sha..single_tag_sha` which, being left-exclusive, will exclude the root commit from the report.

My implementation addresses this by adding a flag to `::commits` to tell it to include the root commit explicitly. When this boolean flag is present, we chop the range and include everything up to the `..single_tag_sha`.

## How Has This Been Tested?

Added both a unit test and adapted the `test-latest-with-one-tag` fixture for the integration tests.

## Types of Changes

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation (no code change)
- [ ] Refactor (refactoring production code)
- [ ] Other <!--- (provide information) -->

## Checklist:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] My code follows the code style of this project.
- [ ] I have updated the documentation accordingly.  _NOTE: unsure where to update this, but would gladly do._
- [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! ⛰️  -->


welcome[bot]: Thanks for opening this pull request! Please check out our contributing guidelines! ⛰️

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/orhun/git-cliff/pull/901?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 `0%` with `8 lines` in your changes missing coverage. Please review.
> Project coverage is 39.90%. Comparing base [(`f5c39a2`)](https://app.codecov.io/gh/orhun/git-cliff/commit/f5c39a2782580326a51e79c192bf5f8e384e4bab?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 [(`6ec995c`)](https://app.codecov.io/gh/orhun/git-cliff/commit/6ec995c39e135448998a2a455335b15baed96d0e?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/901?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/repo.rs](https://app.codecov.io/gh/orhun/git-cliff/pull/901?src=pr&el=tree&filepath=git-cliff-core%2Fsrc%2Frepo.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z#diff-Z2l0LWNsaWZmLWNvcmUvc3JjL3JlcG8ucnM=) | 0.00% | [4 Missing :warning: ](https://app.codecov.io/gh/orhun/git-cliff/pull/901?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) |
| [git-cliff/src/lib.rs](https://app.codecov.io/gh/orhun/git-cliff/pull/901?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% | [4 Missing :warning: ](https://app.codecov.io/gh/orhun/git-cliff/pull/901?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     #901      +/-   ##
==========================================
- Coverage   40.10%   39.90%   -0.20%     
==========================================
  Files          21       21              
  Lines        1671     1677       +6     
==========================================
- Hits          670      669       -1     
- Misses       1001     1008       +7     
```

| [Flag](https://app.codecov.io/gh/orhun/git-cliff/pull/901/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/901/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Orhun+Parmaks%C4%B1z) | `39.90% <0.00%> (-0.20%)` | :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/901?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: btw can you revert 90e0202bcc69984a047904ccb41e28724c003da2 and fa85e9569d19179f1f990a750fe129bef5795dff

those are not in the scope of this PR
rarescosma: Hi, I think the `main` linting configuration drifted a bit, so I included an extra commit to make the "pedantic" clippy lints pass. Also, not sure what's going on with the "Check NodeJS tarball" check. Everything else seems green. 🥳 
rarescosma: > btw can you revert [fa85e95](https://github.com/orhun/git-cliff/commit/fa85e9569d19179f1f990a750fe129bef5795dff)
> 
> those are not in the scope of this PR

Sure, np

Patch:
diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs
index 8fe1f08..1438098 100644
--- a/git-cliff-core/src/repo.rs
+++ b/git-cliff-core/src/repo.rs
@@ -78,7 +78,12 @@ impl Repository {
 		let mut revwalk = self.inner.revwalk()?;
 		revwalk.set_sorting(Sort::TOPOLOGICAL)?;
 		if let Some(range) = range {
-			revwalk.push_range(range)?;
+			if range.contains("..") {
+				revwalk.push_range(range)?;
+			} else {
+				// When a single SHA is provided as the "range", start from the root.
+				revwalk.push(Oid::from_str(range)?)?;
+			}
 		} else {
 			revwalk.push_head()?;
 		}
@@ -464,6 +469,18 @@ mod test {
 		.to_string())
 	}
 
+	fn get_root_commit_hash() -> Result<String> {
+		Ok(str::from_utf8(
+			Command::new("git")
+				.args(["rev-list", "--max-parents=0", "HEAD"])
+				.output()?
+				.stdout
+				.as_ref(),
+		)?
+		.trim_ascii_end()
+		.to_string())
+	}
+
 	fn get_last_tag() -> Result<String> {
 		Ok(str::from_utf8(
 			Command::new("git")
@@ -586,6 +603,18 @@ mod test {
 		Ok(())
 	}
 
+	#[test]
+	fn includes_root_commit() -> Result<()> {
+		let repository = get_repository()?;
+		// a close descendant of the root commit
+		let range = Some("eea3914c7ab07472841aa85c36d11bdb2589a234");
+		let commits = repository.commits(range, None, None)?;
+		let root_commit =
+			AppCommit::from(&commits.last().expect("no commits found").clone());
+		assert_eq!(get_root_commit_hash()?, root_commit.id);
+		Ok(())
+	}
+
 	fn create_temp_repo() -> (Repository, TempDir) {
 		let temp_dir =
 			TempDir::with_prefix("git-cliff-").expect("failed to create temp dir");
diff --git a/git-cliff/src/lib.rs b/git-cliff/src/lib.rs
index b2c29f9..95d37b7 100644
--- a/git-cliff/src/lib.rs
+++ b/git-cliff/src/lib.rs
@@ -171,7 +171,11 @@ fn process_repository<'a>(
 				commits.last().map(|c| c.id().to_string()),
 				tags.get_index(0).map(|(k, _)| k),
 			) {
-				commit_range = Some(format!("{tag1}..{tag2}"));
+				if tags.len() == 1 {
+					commit_range = Some(tag2.to_owned());
+				} else {
+					commit_range = Some(format!("{tag1}..{tag2}"));
+				}
 			}
 		} else {
 			let mut tag_index = tags.len() - 2;


Test patch:
diff --git a/.github/fixtures/test-latest-with-one-tag/commit.sh b/.github/fixtures/test-latest-with-one-tag/commit.sh
index 6c5c90c..361943d 100755
--- a/.github/fixtures/test-latest-with-one-tag/commit.sh
+++ b/.github/fixtures/test-latest-with-one-tag/commit.sh
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 set -e
 
-GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "Initial commit"
+GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "feat: initial commit"
 
 GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "feat: add feature 1"
 git tag v0.1.0
diff --git a/.github/fixtures/test-latest-with-one-tag/expected.md b/.github/fixtures/test-latest-with-one-tag/expected.md
index 1b15d95..e7fdd52 100644
--- a/.github/fixtures/test-latest-with-one-tag/expected.md
+++ b/.github/fixtures/test-latest-with-one-tag/expected.md
@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
 
 ### Feat
 
+- Initial commit
 - Add feature 1
 
 <!-- generated by git-cliff -->