Repository: gembaadvantage/uplift (go)

Issue:
[Feature]: To skip pre-release tags in changelog
### Describe your feature

To have the ability to skip prerelease tags or tags by regexp in changelog generation.

### Your potential solution

Add an option `uplift changelog --skip-tags "\d+.\d+.\d-.*"` to skip some tags by pattern and generate changelog without them.

### Any additional information?

_No response_

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

Pull request description:
feat: support skipping prerelease tags in changelog
closes #318 

Patch:
diff --git a/cmd/uplift/changelog.go b/cmd/uplift/changelog.go
index 7af0977..9a42f53 100644
--- a/cmd/uplift/changelog.go
+++ b/cmd/uplift/changelog.go
@@ -80,16 +80,20 @@ uplift changelog --include "^.*\(scope\)"
 uplift changelog --no-stage
 
 # Generate a changelog with multiline commit messages
-uplift changelog --multiline`
+uplift changelog --multiline
+
+# Generate a changelog with prerelease tags being skipped
+uplift changelog --skip-prerelease`
 )
 
 type changelogOptions struct {
-	DiffOnly  bool
-	Exclude   []string
-	Include   []string
-	All       bool
-	Sort      string
-	Multiline bool
+	DiffOnly       bool
+	Exclude        []string
+	Include        []string
+	All            bool
+	Sort           string
+	Multiline      bool
+	SkipPrerelease bool
 	*globalOptions
 }
 
@@ -131,6 +135,7 @@ func newChangelogCmd(gopts *globalOptions, out io.Writer) *changelogCommand {
 	f.StringSliceVar(&chglogCmd.Opts.Include, "include", []string{}, "a list of regexes to cherry-pick conventional commits for the changelog")
 	f.StringVar(&chglogCmd.Opts.Sort, "sort", "", "the sort order of commits within each changelog entry")
 	f.BoolVar(&chglogCmd.Opts.Multiline, "multiline", false, "include multiline commit messages within changelog (skips truncation)")
+	f.BoolVar(&chglogCmd.Opts.SkipPrerelease, "skip-prerelease", false, "skips the creation of a changelog entry for a prerelease")
 
 	chglogCmd.Cmd = cmd
 	return chglogCmd
@@ -194,6 +199,10 @@ func setupChangelogContext(opts changelogOptions, out io.Writer) (*context.Conte
 	if !ctx.Changelog.Multiline && ctx.Config.Changelog != nil {
 		ctx.Changelog.Multiline = ctx.Config.Changelog.Multiline
 	}
+	ctx.Changelog.SkipPrerelease = opts.SkipPrerelease
+	if !ctx.Changelog.SkipPrerelease && ctx.Config.Changelog != nil {
+		ctx.Changelog.SkipPrerelease = ctx.Config.Changelog.SkipPrerelease
+	}
 
 	// Sort order provided as a command-line flag takes precedence
 	ctx.Changelog.Sort = opts.Sort
diff --git a/cmd/uplift/release.go b/cmd/uplift/release.go
index 4a79e63..fbc9a6d 100644
--- a/cmd/uplift/release.go
+++ b/cmd/uplift/release.go
@@ -83,16 +83,17 @@ uplift release --no-prefix`
 )
 
 type releaseOptions struct {
-	FetchTags     bool
-	Check         bool
-	Prerelease    string
-	SkipChangelog bool
-	SkipBumps     bool
-	NoPrefix      bool
-	Exclude       []string
-	Include       []string
-	Sort          string
-	Multiline     bool
+	FetchTags      bool
+	Check          bool
+	Prerelease     string
+	SkipChangelog  bool
+	SkipBumps      bool
+	NoPrefix       bool
+	Exclude        []string
+	Include        []string
+	Sort           string
+	Multiline      bool
+	SkipPrerelease bool
 	*globalOptions
 }
 
@@ -135,6 +136,7 @@ func newReleaseCmd(gopts *globalOptions, out io.Writer) *releaseCommand {
 	f.StringSliceVar(&relCmd.Opts.Include, "include", []string{}, "a list of regexes to cherry-pick conventional commits for the changelog")
 	f.StringVar(&relCmd.Opts.Sort, "sort", "", "the sort order of commits within each changelog entry")
 	f.BoolVar(&relCmd.Opts.Multiline, "multiline", false, "include multiline commit messages within changelog (skips truncation)")
+	f.BoolVar(&relCmd.Opts.SkipPrerelease, "skip-changelog-prerelease", false, "skips the creation of a changelog entry for a prerelease")
 
 	relCmd.Cmd = cmd
 	return relCmd
@@ -203,6 +205,10 @@ func setupReleaseContext(opts releaseOptions, out io.Writer) (*context.Context,
 		ctx.Changelog.Include = append(ctx.Changelog.Include, ctx.Config.Changelog.Include...)
 		ctx.Changelog.Exclude = append(ctx.Changelog.Exclude, ctx.Config.Changelog.Exclude...)
 	}
+	ctx.Changelog.SkipPrerelease = opts.SkipPrerelease
+	if !ctx.Changelog.SkipPrerelease && ctx.Config.Changelog != nil {
+		ctx.Changelog.SkipPrerelease = ctx.Config.Changelog.SkipPrerelease
+	}
 
 	// By default ensure the ci(uplift): commits are excluded also
 	ctx.Changelog.Exclude = append(ctx.Changelog.Exclude, "ci(uplift):")
diff --git a/docs/changelog.md b/docs/changelog.md
index e6e983d..0369752 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -40,7 +40,7 @@ uplift changelog --diff-only
 
 ## Migrate an Existing Repository
 
-If your repository does not contain a `CHANGELOG.md` file, you can generate one that spans its entire history. A word of warning, this does require a tagging structure to be in place.
+If your repository does not contain a `CHANGELOG.md` file, you can generate one that spans its entire history. A tagging structure must be in place.
 
 ```sh
 uplift changelog --all
@@ -53,3 +53,11 @@ You can configure `uplift` to include multiline commit messages within your chan
 ```sh
 uplift changelog --multiline
 ```
+
+## Skip Prereleases
+
+Prevent any prerelease from being included in your changelog. Upon your next release, `uplift` will include any previous prerelease commits.
+
+```sh
+uplift changelog --skip-prerelease
+```
diff --git a/docs/reference/cli/changelog.md b/docs/reference/cli/changelog.md
index 821651f..260a292 100644
--- a/docs/reference/cli/changelog.md
+++ b/docs/reference/cli/changelog.md
@@ -48,6 +48,9 @@ uplift changelog --no-stage
 
 # Generate a changelog with multiline commit messages
 uplift changelog --multiline
+
+# Generate a changelog with prerelease tags being skipped
+uplift changelog --skip-prerelease
 ```
 
 ## Flags
@@ -63,6 +66,8 @@ uplift changelog --multiline
                         for the changelog
     --multiline         include multiline commit messages within changelog
                         (skips truncation)
+    --skip-prerelease   skips the creation of a changelog entry for a
+                        prerelease
     --sort string       the sort order of commits within each changelog entry
 ```
 
diff --git a/docs/reference/cli/release.md b/docs/reference/cli/release.md
index 98a5d9c..53955f5 100644
--- a/docs/reference/cli/release.md
+++ b/docs/reference/cli/release.md
@@ -43,22 +43,25 @@ uplift release --no-prefix
 ## Flags
 
 ```text
-    --check               check if a release will be triggered
-    --exclude strings     a list of regexes for excluding conventional commits
-                          from the changelog
-    --fetch-all           fetch all tags from the remote repository
--h, --help                help for release
-    --include strings     a list of regexes to cherry-pick conventional commits
-                          for the changelog
-    --multiline           include multiline commit messages within changelog
-                          (skips truncation)
-    --no-prefix           strip the default 'v' prefix from the next calculated
-                          semantic version
-    --prerelease string   append a prerelease suffix to next calculated semantic
-                          version
-    --skip-bumps          skips the bumping of any files
-    --skip-changelog      skips the creation or amendment of a changelog
-    --sort string         the sort order of commits within each changelog entry
+    --check                       check if a release will be triggered
+    --exclude strings             a list of regexes for excluding conventional
+                                  commits from the changelog
+    --fetch-all                   fetch all tags from the remote repository
+-h, --help                        help for release
+    --include strings             a list of regexes to cherry-pick conventional
+                                  commits for the changelog
+    --multiline                   include multiline commit messages within
+                                  changelog (skips truncation)
+    --no-prefix                   strip the default 'v' prefix from the next
+                                  calculated semantic version
+    --prerelease string           append a prerelease suffix to next calculated
+                                  semantic version
+    --skip-bumps                  skips the bumping of any files
+    --skip-changelog              skips the creation or amendment of a changelog
+    --skip-changelog-prerelease   skips the creation of a changelog entry for a
+                                  prerelease
+    --sort string                 the sort order of commits within each
+                                  changelog entry
 ```
 
 ## Global Flags
diff --git a/docs/reference/config.md b/docs/reference/config.md
index 93f5bf7..c33ea20 100644
--- a/docs/reference/config.md
+++ b/docs/reference/config.md
@@ -109,6 +109,11 @@ changelog:
   # Include multiline commit messages within the changelog. Disables
   # default behaviour of truncating a commit message to its first line
   multiline: true
+
+  # Skips generating a changelog for any prerelease. All commits from
+  # a prerelease will be appended to the changelog entry for the next
+  # release
+  skipPrerelease: true
 ```
 
 ## commitAuthor
diff --git a/docs/static/schema.json b/docs/static/schema.json
index 17a19cd..3ec8cbf 100644
--- a/docs/static/schema.json
+++ b/docs/static/schema.json
@@ -160,6 +160,11 @@
           "$comment": "https://upliftci.dev/reference/config#changelog",
           "description": "Include multiline commit messages within the changelog. Disables default behaviour of truncating a commit message to its first line",
           "type": "boolean"
+        },
+        "skipPrerelease": {
+          "$comment": "https://upliftci.dev/reference/config#changelog",
+          "description": "Skips generating a changelog for any prerelease. All commits from a prerelease will be appended to the changelog entry for the next release",
+          "type": "boolean"
         }
       },
       "type": "object",
@@ -184,6 +189,11 @@
           "required": [
             "multiline"
           ]
+        },
+        {
+          "skipPrerelease": [
+            "multiline"
+          ]
         }
       ]
     },
diff --git a/internal/config/uplift.go b/internal/config/uplift.go
index 22bd106..6761d3e 100644
--- a/internal/config/uplift.go
+++ b/internal/config/uplift.go
@@ -82,10 +82,11 @@ type CommitAuthor struct {
 // Changelog defines configuration for generating a changelog of the latest
 // semantic version based release
 type Changelog struct {
-	Sort      string   `yaml:"sort" validate:"required_without_all=Exclude Include,oneof=asc desc ASC DESC"`
-	Exclude   []string `yaml:"exclude" validate:"required_without_all=Sort Include,dive,min=1"`
-	Include   []string `yaml:"include" validate:"required_without_all=Sort Exclude,dive,min=1"`
-	Multiline bool     `yaml:"multiline"`
+	Sort           string   `yaml:"sort" validate:"required_without_all=Exclude Include,oneof=asc desc ASC DESC"`
+	Exclude        []string `yaml:"exclude" validate:"required_without_all=Sort Include,dive,min=1"`
+	Include        []string `yaml:"include" validate:"required_without_all=Sort Exclude,dive,min=1"`
+	Multiline      bool     `yaml:"multiline"`
+	SkipPrerelease bool     `yaml:"skipPrerelease"`
 }
 
 // Git defines configuration for how uplift interacts with git
diff --git a/internal/context/context.go b/internal/context/context.go
index 777f1b1..bb48cae 100644
--- a/internal/context/context.go
+++ b/internal/context/context.go
@@ -83,13 +83,14 @@ type SCM struct {
 // Changelog provides details about how the changelog should be managed
 // for the current repository
 type Changelog struct {
-	All       bool
-	DiffOnly  bool
-	Exclude   []string
-	Include   []string
-	Sort      string
-	PreTag    bool
-	Multiline bool
+	All            bool
+	DiffOnly       bool
+	Exclude        []string
+	Include        []string
+	Sort           string
+	PreTag         bool
+	Multiline      bool
+	SkipPrerelease bool
 }
 
 // New constructs a context that captures both runtime configuration and
diff --git a/internal/task/changelog/changelog.go b/internal/task/changelog/changelog.go
index 1830d1a..d2086a2 100644
--- a/internal/task/changelog/changelog.go
+++ b/internal/task/changelog/changelog.go
@@ -35,6 +35,7 @@ import (
 
 	"github.com/apex/log"
 	"github.com/gembaadvantage/uplift/internal/context"
+	"github.com/gembaadvantage/uplift/internal/semver"
 	git "github.com/purpleclay/gitz"
 )
 
@@ -91,7 +92,15 @@ func (t Task) String() string {
 
 // Skip running the task if no changelog is needed
 func (t Task) Skip(ctx *context.Context) bool {
-	return ctx.NoVersionChanged || ctx.SkipChangelog
+	if ctx.NoVersionChanged || ctx.SkipChangelog {
+		return true
+	}
+
+	if ctx.Changelog.SkipPrerelease && ctx.NextVersion.Prerelease != "" {
+		return true
+	}
+
+	return false
 }
 
 // Run the task
@@ -193,8 +202,32 @@ func (t Task) Run(ctx *context.Context) error {
 }
 
 func changelogRelease(ctx *context.Context) ([]release, error) {
-	log.WithField("tag", ctx.NextVersion.Raw).Info("determine changes for release")
-	glog, err := ctx.GitClient.Log(git.WithRefRange(ctx.NextVersion.Raw, ctx.CurrentVersion.Raw))
+	next := ctx.NextVersion.Raw
+	prev := ctx.CurrentVersion.Raw
+
+	log.WithField("tag", next).Info("determine changes for release")
+	if ctx.Changelog.SkipPrerelease {
+		// Retrieve all tags and filter out any that are prerelease versions
+		tags, _ := ctx.GitClient.Tags(git.WithShellGlob("*.*.*"),
+			git.WithSortBy(git.CreatorDateDesc, git.VersionDesc),
+			git.WithFilters(func(tag string) bool {
+				ver, err := semver.Parse(tag)
+				if err != nil {
+					return false
+				}
+
+				return ver.Prerelease == ""
+			}),
+			git.WithCount(2))
+
+		if len(tags) == 1 {
+			prev = ""
+		} else {
+			prev = tags[1]
+		}
+	}
+
+	glog, err := ctx.GitClient.Log(git.WithRefRange(next, prev))
 	if err != nil {
 		return []release{}, err
 	}
@@ -218,14 +251,14 @@ func changelogRelease(ctx *context.Context) ([]release, error) {
 
 	if len(ents) == 0 {
 		log.WithFields(log.Fields{
-			"tag":  ctx.NextVersion.Raw,
-			"prev": ctx.CurrentVersion.Raw,
+			"tag":  next,
+			"prev": prev,
 		}).Info("no log entries between tags")
 		return []release{}, nil
 	}
 
 	log.WithFields(log.Fields{
-		"tag":     ctx.NextVersion.Raw,
+		"tag":     next,
 		"date":    time.Now().UTC().Format(ChangeDate),
 		"commits": len(ents),
 	}).Info("changeset identified")
@@ -267,7 +300,19 @@ func extractTagEntry(dets git.TagDetails) tagEntry {
 
 func changelogReleases(ctx *context.Context) ([]release, error) {
 	tags, err := ctx.GitClient.Tags(git.WithShellGlob("*.*.*"),
-		git.WithSortBy(git.CreatorDateDesc, git.VersionDesc))
+		git.WithSortBy(git.CreatorDateDesc, git.VersionDesc),
+		git.WithFilters(func(tag string) bool {
+			if !ctx.Changelog.SkipPrerelease {
+				return true
+			}
+
+			ver, err := semver.Parse(tag)
+			if err != nil {
+				return false
+			}
+
+			return ver.Prerelease == ""
+		}))
 	if err != nil {
 		return []release{}, nil
 	}


Test patch:
diff --git a/cmd/uplift/changelog_test.go b/cmd/uplift/changelog_test.go
index 78e26fc..46db04d 100644
--- a/cmd/uplift/changelog_test.go
+++ b/cmd/uplift/changelog_test.go
@@ -311,3 +311,24 @@ this now includes code examples`
 	assert.Contains(t, cl, `docs: update documentation
   this now includes code examples`)
 }
+
+func TestChangelog_SkipPrerelease(t *testing.T) {
+	log := `(tag: 0.1.0) feat: 3
+fix: 1
+(tag: 0.1.0-pre.2) feat: 2
+(tag: 0.1.0-pre.1) feat: 1`
+	gittest.InitRepository(t, gittest.WithLog(log))
+
+	chglogCmd := newChangelogCmd(noChangesPushed(), os.Stdout)
+	chglogCmd.Cmd.SetArgs([]string{"--skip-prerelease"})
+
+	err := chglogCmd.Cmd.Execute()
+	require.NoError(t, err)
+
+	assert.True(t, changelogExists(t))
+
+	cl := readChangelog(t)
+	assert.Contains(t, cl, "## 0.1.0")
+	assert.NotContains(t, cl, "## 0.1.0-pre.2")
+	assert.NotContains(t, cl, "## 0.1.0-pre.1")
+}
diff --git a/cmd/uplift/release_test.go b/cmd/uplift/release_test.go
index 37e1752..b73c0db 100644
--- a/cmd/uplift/release_test.go
+++ b/cmd/uplift/release_test.go
@@ -289,3 +289,24 @@ this now includes code examples`
 	assert.Contains(t, cl, `docs: update documentation
   this now includes code examples`)
 }
+
+func TestRelease_SkipChangelogPrerelease(t *testing.T) {
+	log := `feat: exciting new feature
+(tag: 0.1.0-pre.2) fix: fix another bug
+(tag: 0.1.0-pre.1) fix: fix bug in existing feature
+(tag: 0.1.0) feat: this is a new feature`
+	gittest.InitRepository(t, gittest.WithLog(log))
+
+	relCmd := newReleaseCmd(noChangesPushed(), os.Stdout)
+	relCmd.Cmd.SetArgs([]string{"--skip-changelog-prerelease"})
+
+	err := relCmd.Cmd.Execute()
+	require.NoError(t, err)
+
+	assert.True(t, changelogExists(t))
+
+	cl := readChangelog(t)
+	assert.Contains(t, cl, "## 0.2.0")
+	assert.NotContains(t, cl, "## 0.1.0-pre.2")
+	assert.NotContains(t, cl, "## 0.1.0-pre.1")
+}
diff --git a/internal/task/changelog/changelog_test.go b/internal/task/changelog/changelog_test.go
index 4940eae..321d76e 100644
--- a/internal/task/changelog/changelog_test.go
+++ b/internal/task/changelog/changelog_test.go
@@ -58,6 +58,17 @@ func TestSkip(t *testing.T) {
 				SkipChangelog: true,
 			},
 		},
+		{
+			name: "SkipPrerelease",
+			ctx: &context.Context{
+				Changelog: context.Changelog{
+					SkipPrerelease: true,
+				},
+				NextVersion: semver.Version{
+					Prerelease: "pre.1",
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -770,3 +781,168 @@ With the correct indentation for rendering in markdown
 
 	assert.Equal(t, expected, buf.String())
 }
+
+func TestRun_SkipPrerelease(t *testing.T) {
+	log := `(tag: 0.2.0) feat: 3
+fix: 2
+(tag: 0.2.0-pre.2) feat: 2
+(tag: 0.2.0-pre.1) fix: 1
+(tag: 0.1.0) feat: 1`
+	gittest.InitRepository(t, gittest.WithLog(log))
+	hashes := hashLookup(t, gittest.Log(t))
+
+	cl := fmt.Sprintf(`# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 1.0.0 - 2021-09-17
+
+- %s feat: 1
+- %s %s
+`, hashes["first commit"], hashes[gittest.InitialCommit], gittest.InitialCommit)
+	os.WriteFile(MarkdownFile, []byte(cl), 0o644)
+
+	ctx := &context.Context{
+		Changelog: context.Changelog{
+			SkipPrerelease: true,
+		},
+		CurrentVersion: semver.Version{
+			Prerelease: "pre.2",
+			Raw:        "0.1.0-pre.2",
+		},
+		NextVersion: semver.Version{
+			Raw: "0.2.0",
+		},
+		SCM: context.SCM{
+			Provider: context.Unrecognised,
+		},
+	}
+
+	err := Task{}.Run(ctx)
+	require.NoError(t, err)
+
+	expected := fmt.Sprintf(`# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 0.2.0 - %s
+
+- %s feat: 3
+- %s fix: 2
+- %s feat: 2
+- %s fix: 1
+
+## 1.0.0 - 2021-09-17
+
+- %s feat: 1
+- %s %s
+`, changelogDate(t), hashes["feat: 3"], hashes["fix: 2"], hashes["feat: 2"], hashes["fix: 1"],
+		hashes["feat: first feature"], hashes[gittest.InitialCommit], gittest.InitialCommit)
+
+	assert.Equal(t, expected, readChangelog(t))
+}
+
+func TestRun_SkipPrereleaseToHead(t *testing.T) {
+	log := `(tag: 0.1.0) feat: 3
+fix: 1
+(tag: 0.1.0-pre.2) feat: 2
+(tag: 0.1.0-pre.1) feat: 1`
+	gittest.InitRepository(t, gittest.WithLog(log))
+	hashes := hashLookup(t, gittest.Log(t))
+
+	ctx := &context.Context{
+		Changelog: context.Changelog{
+			SkipPrerelease: true,
+		},
+		CurrentVersion: semver.Version{
+			Prerelease: "pre.2",
+			Raw:        "0.1.0-pre.2",
+		},
+		NextVersion: semver.Version{
+			Raw: "0.1.0",
+		},
+		SCM: context.SCM{
+			Provider: context.Unrecognised,
+		},
+	}
+
+	err := Task{}.Run(ctx)
+	require.NoError(t, err)
+
+	expected := fmt.Sprintf(`# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 0.1.0 - %s
+
+- %s feat: 3
+- %s fix: 1
+- %s feat: 2
+- %s feat: 1
+- %s %s
+`, changelogDate(t), hashes["feat: 3"], hashes["fix: 1"], hashes["feat: 2"], hashes["feat: 1"],
+		hashes[gittest.InitialCommit], gittest.InitialCommit)
+
+	assert.Equal(t, expected, readChangelog(t))
+}
+
+func TestRun_AllSkipPrerelease(t *testing.T) {
+	log := `(tag: 0.2.0) feat: 4
+(tag: 0.2.0-pre.2) feat: 3
+(tag: 0.2.0-pre.1) fix: 2
+(tag: 0.1.0) feat: 2
+(tag: 0.1.0-pre.2) fix: 1
+(tag: 0.1.0-pre.1) feat: 1`
+	gittest.InitRepository(t, gittest.WithLog(log))
+	hashes := hashLookup(t, gittest.Log(t))
+
+	ctx := &context.Context{
+		Changelog: context.Changelog{
+			All:            true,
+			SkipPrerelease: true,
+		},
+		SCM: context.SCM{
+			Provider: context.Unrecognised,
+		},
+	}
+
+	err := Task{}.Run(ctx)
+	require.NoError(t, err)
+
+	expected := fmt.Sprintf(`# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 0.2.0 - %s
+
+- %s feat: 4
+- %s feat: 3
+- %s fix: 2
+
+## 0.1.0 - %s
+
+- %s feat: 2
+- %s fix: 1
+- %s feat: 1
+- %s %s
+`, changelogDate(t), hashes["feat: 4"], hashes["feat: 3"], hashes["fix: 2"], changelogDate(t),
+		hashes["feat: 2"], hashes["fix: 1"], hashes["feat: 1"], hashes[gittest.InitialCommit], gittest.InitialCommit)
+
+	assert.Equal(t, expected, readChangelog(t))
+}