Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ignore tags from other branches #100

Merged
merged 2 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 75 additions & 44 deletions init.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package chglog

import (
"errors"
"fmt"
"os"
"sort"
Expand All @@ -10,83 +9,115 @@ import (
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer"
)

// InitChangelog create a new ChangeLogEntries from a git repo.
func InitChangelog(gitRepo *git.Repository, owner string, notes *ChangeLogNotes, deb *ChangelogDeb, useConventionalCommits bool) (cle ChangeLogEntries, err error) {
var (
tagRefs storer.ReferenceIter
tags []*semver.Version
start, end plumbing.Hash
)

cle = make(ChangeLogEntries, 0)
end = plumbing.ZeroHash

if tagRefs, err = gitRepo.Tags(); err != nil {
return nil, fmt.Errorf("unable to fetch tags: %w", err)
func versionsInRepo(gitRepo *git.Repository) (map[plumbing.Hash]*semver.Version, error) {
tagRefs, err := gitRepo.Tags()
if err != nil {
return nil, err
}

defer tagRefs.Close()

if err = tagRefs.ForEach(func(t *plumbing.Reference) error {
var version *semver.Version
tags := make(map[plumbing.Hash]*semver.Version)

err = tagRefs.ForEach(func(t *plumbing.Reference) error {
var (
version *semver.Version
tag *object.Tag
)

tagName := t.Name().Short()
hash := t.Hash()

if version, err = semver.NewVersion(tagName); err != nil || version == nil {
fmt.Fprintf(os.Stderr, "Warning: unable to parse version from tag: %s : %v\n", tagName, err)
return nil
}

tags = append(tags, version)
// If this is an annotated tag look up the hash of the commit and use that.
if tag, err = gitRepo.TagObject(t.Hash()); err == nil {
var c *object.Commit

if c, err = tag.Commit(); err != nil {
return fmt.Errorf("cannot dereference annotated tag: %s : %w", tagName, err)
}
hash = c.Hash
}

tags[hash] = version

return nil
}); err != nil {
})

if err != nil {
return nil, err
}

sort.Slice(tags, func(i, j int) bool { return tags[i].LessThan(tags[j]) })
return tags, nil
}

for _, version := range tags {
tagName := version.Original()
func versionsOnBranch(gitRepo *git.Repository) (map[*semver.Version]plumbing.Hash, error) {
repoVersions, err := versionsInRepo(gitRepo)
if err != nil {
return nil, err
}

refs, err := gitRepo.Log(&git.LogOptions{})
if err != nil {
return nil, err
}

defer refs.Close()

t, err := gitRepo.Tag(tagName)
if err != nil {
return nil, err
versions := make(map[*semver.Version]plumbing.Hash)

err = refs.ForEach(func(c *object.Commit) error {
if v, ok := repoVersions[c.Hash]; ok {
versions[v] = c.Hash
}
return nil
})

return versions, err
}

// InitChangelog create a new ChangeLogEntries from a git repo.
func InitChangelog(gitRepo *git.Repository, owner string, notes *ChangeLogNotes, deb *ChangelogDeb, useConventionalCommits bool) (cle ChangeLogEntries, err error) {
var start, end plumbing.Hash

cle = make(ChangeLogEntries, 0)
end = plumbing.ZeroHash

versions, err := versionsOnBranch(gitRepo)
if err != nil {
return nil, err
}

tags := make([]*semver.Version, 0, len(versions))
for v := range versions {
tags = append(tags, v)
}

sort.Slice(tags, func(i, j int) bool { return tags[i].LessThan(tags[j]) })

for _, version := range tags {
var (
commits []*object.Commit
commitObject *object.Commit
tag *object.Tag
)

if version.Prerelease() != "" {
// Do not need change logs for pre-release entries
continue
}

if start, err = GitHashFotTag(gitRepo, tagName); err != nil {
return nil, fmt.Errorf("unable to find hash for tag: %s : %w", tagName, err)
}

// If this is an annotated tag look up the hash of the commit and use that.
if tag, err = gitRepo.TagObject(t.Hash()); err == nil {
var c *object.Commit

if c, err = tag.Commit(); err != nil {
return nil, fmt.Errorf("cannot dereference annotated tag: %s : %w", tagName, err)
}
start = c.Hash
}
start = versions[version]

if commitObject, err = gitRepo.CommitObject(start); err != nil {
// This ignores objects that are off branch which happens when tagging on multiple branches happens.
if errors.Is(err, plumbing.ErrObjectNotFound) {
continue
}
return nil, fmt.Errorf("unable to fetch commit from tag %v: %w", tagName, err)
return nil, fmt.Errorf("unable to fetch commit from tag %v: %w", version.Original(), err)
}

if owner == "" {
owner = fmt.Sprintf("%s <%s>", commitObject.Committer.Name, commitObject.Committer.Email)
}
Expand Down
92 changes: 92 additions & 0 deletions order_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,98 @@ func TestOrderChangelog(t *testing.T) {
})
}

func TestOffBranchTags(t *testing.T) {
cle, err := Parse("./testdata/gold-order-changelog.yml")
if err != nil {
t.Fatal(err)
}

// remove odd entries

goldCLE := make(ChangeLogEntries, len(cle)/2+1)
for i := range cle {
if i%2 == 0 {
goldCLE[i/2] = cle[i]
}
}

repo := newTestRepo()
tree, err := repo.Git.Worktree()
if err != nil {
t.Fatal(err)
}

// initial commit on master

hash := repo.modifyAndCommit("file", defCommitOptions())
if _, err = repo.Git.CreateTag("v0.0.0", hash, nil); err != nil {
t.Fatal(err)
}

// second commit on develop

err = tree.Checkout(&git.CheckoutOptions{
Branch: plumbing.NewBranchReferenceName("develop"),
Create: true,
})
if err != nil {
t.Fatal(err)
}

hash = repo.modifyAndCommit("file", defCommitOptions())
if _, err = repo.Git.CreateTag("v0.1.0", hash, nil); err != nil {
t.Fatal(err)
}

// alternate branches for commits

master := plumbing.NewBranchReferenceName("master")
develop := plumbing.NewBranchReferenceName("develop")

for i := 2; i <= 10; i++ {
branch := master
if i%2 != 0 {
branch = develop
}

t.Logf("%v branch=%v\n", i, branch)

err = tree.Checkout(&git.CheckoutOptions{Branch: branch})
if err != nil {
t.Fatal(err)
}

hash := repo.modifyAndCommit("file", defCommitOptions())

if _, err = repo.Git.CreateTag(fmt.Sprintf("v0.%d.0", i), hash, nil); err != nil {
t.Fatal(err)
}
}

testCLE, err := InitChangelog(repo.Git, "", nil, nil, false)
if err != nil {
t.Fatal(err)
}

// zero all commit hashes (don't care about these)

for i := range testCLE {
for j := range testCLE[i].Changes {
testCLE[i].Changes[j].Commit = ""
}
}
for i := range goldCLE {
for j := range goldCLE[i].Changes {
goldCLE[i].Changes[j].Commit = ""
}
}

Convey("Generated entry should be the same as the golden entry", t, func() {
So(testCLE, ShouldResemble, goldCLE)
})

}

func TestSemverTag(t *testing.T) {
repo := newTestRepo()
tag := "1.0.0"
Expand Down