Skip to content

Commit

Permalink
(+semver: feature) Handle merge commits in master (#12)
Browse files Browse the repository at this point in the history
Signed-off-by: Joshua Benjamin <[email protected]>
  • Loading branch information
annymsMthd authored Oct 17, 2018
1 parent b941e75 commit f1b2729
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 111 deletions.
223 changes: 223 additions & 0 deletions pkg/git/branchWalker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
package git

import (
"regexp"

"gopkg.in/src-d/go-git.v4/plumbing"

"github.com/coreos/go-semver/semver"
"github.com/pkg/errors"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing/object"
)

type branchWalker struct {
repository *git.Repository
head *object.Commit
tagMap map[string]string
settings *Settings
isMaster bool
endHash string

visited map[string]bool
commitsToReconcile map[string]*gitVersion
}

type versionHolder struct {
versionMap []*gitVersion
}

func newBranchWalker(repository *git.Repository, head *object.Commit, tagMap map[string]string, settings *Settings, isMaster bool, endHash string) *branchWalker {
return &branchWalker{
repository: repository,
head: head,
settings: settings,
tagMap: tagMap,
isMaster: isMaster,
endHash: endHash,
visited: make(map[string]bool),
commitsToReconcile: make(map[string]*gitVersion),
}
}

func (b *branchWalker) GetVersion() (*semver.Version, error) {
versionMap, err := b.GetVersionMap()
if err != nil {
return nil, err
}

var baseVersion *semver.Version
index := len(versionMap) - 1
if versionMap[index].IsSolid {
baseVersion = versionMap[index].Name
index--
} else {
baseVersion, err = semver.NewVersion("0.0.0")
if err != nil {
return nil, err
}
}

if index < 0 {
return baseVersion, nil
}

for ; index >= 0; index-- {
v := versionMap[index]
switch {
case v.MajorBump:
baseVersion.BumpMajor()
case v.MinorBump:
baseVersion.BumpMinor()
case v.PatchBump:
baseVersion.BumpPatch()
default: // every commit in master has at least a patch bump
baseVersion.BumpPatch()
}
}

return baseVersion, nil
}

func (b *branchWalker) GetVersionMap() ([]*gitVersion, error) {
versionMap := versionHolder{
versionMap: []*gitVersion{},
}

err := b.walkVersion(b.head, &versionMap, false)
if err != nil {
return nil, err
}

if b.isMaster {
for hash, version := range b.commitsToReconcile {
err = b.reconcileCommit(hash, version)
if err != nil {
return nil, err
}
}
}

return versionMap.versionMap, nil
}

func (b *branchWalker) walkVersion(ref *object.Commit, version *versionHolder, tilVisited bool) error {
if _, visited := b.visited[ref.Hash.String()]; tilVisited && visited {
return nil
}

b.visited[ref.Hash.String()] = true

tag, ok := b.tagMap[ref.Hash.String()]
if ok {
tagVersion, err := semver.NewVersion(tag)
if err != nil {
return err
}
version.versionMap = append(version.versionMap, &gitVersion{IsSolid: true, Name: tagVersion})
return nil
}

parents := ref.NumParents()
if parents > 1 {
versionToReconcile := gitVersion{IsSolid: false}
version.versionMap = append(version.versionMap, &versionToReconcile)

b.commitsToReconcile[ref.Hash.String()] = &versionToReconcile
return b.checkWalkParent(ref, version, tilVisited)
}

matched, err := regexp.MatchString(b.settings.MajorPattern, ref.Message)
if err != nil {
return err
}
if matched {
version.versionMap = append(version.versionMap, &gitVersion{IsSolid: false, MajorBump: true})
return b.checkWalkParent(ref, version, tilVisited)
}

matched, err = regexp.MatchString(b.settings.MinorPattern, ref.Message)
if err != nil {
return err
}
if matched {
version.versionMap = append(version.versionMap, &gitVersion{IsSolid: false, MinorBump: true})
return b.checkWalkParent(ref, version, tilVisited)
}

matched, err = regexp.MatchString(b.settings.PatchPattern, ref.Message)
if err != nil {
return err
}
if matched {
version.versionMap = append(version.versionMap, &gitVersion{IsSolid: false, PatchBump: true})
return b.checkWalkParent(ref, version, tilVisited)
}

version.versionMap = append(version.versionMap, &gitVersion{IsSolid: false})

return b.checkWalkParent(ref, version, tilVisited)
}

func (b *branchWalker) checkWalkParent(ref *object.Commit, version *versionHolder, tilVisited bool) error {
parents := ref.NumParents()
if parents == 0 {
return nil
}

parent, err := ref.Parent(0)
if err != nil {
return nil
}

if parent.Hash.String() == b.endHash {
return nil
}

return b.walkVersion(parent, version, tilVisited)
}

func (b *branchWalker) reconcileCommit(hash string, version *gitVersion) error {
versionMap := versionHolder{
versionMap: []*gitVersion{},
}

commit, err := b.repository.CommitObject(plumbing.NewHash(hash))
if err != nil {
return errors.Wrap(err, "failed to get commit in reconcile")
}

if commit.NumParents() <= 1 {
return nil
}

parentToWalk, err := commit.Parent(1)
if err != nil {
return errors.Wrap(err, "failed to get parent in reconcile")
}

err = b.walkVersion(parentToWalk, &versionMap, true)
if err != nil {
return err
}

var hasMajor, hasMinor bool
for _, bump := range versionMap.versionMap {
if bump.MajorBump {
hasMajor = true
}
if bump.MinorBump {
hasMinor = true
}
}

if hasMajor {
version.MajorBump = true
} else if hasMinor {
version.MinorBump = true
} else {
version.PatchBump = true
}

return nil
}
122 changes: 11 additions & 111 deletions pkg/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package git
import (
"fmt"
"os"
"regexp"
"strings"

"github.com/coreos/go-semver/semver"
Expand Down Expand Up @@ -90,9 +89,15 @@ func getVersion(r *git.Repository, h *plumbing.Reference, tagMap map[string]stri
return nil, errors.Wrap(err, "getVersion failed")
}

masterVersion, err := getMasterVersion(r, masterHead, tagMap, settings)
masterCommit, err := r.CommitObject(masterHead.Hash())
if err != nil {
return nil, errors.Wrap(err, "getVersion failed")
return nil, errors.Wrap(err, "failed to get master commit from reference")
}

masterWalker := newBranchWalker(r, masterCommit, tagMap, settings, true, "")
masterVersion, err := masterWalker.GetVersion()
if err != nil {
return nil, err
}

if h.Hash() == masterHead.Hash() {
Expand All @@ -104,10 +109,10 @@ func getVersion(r *git.Repository, h *plumbing.Reference, tagMap map[string]stri
return nil, errors.Wrap(err, "getVersion failed")
}

versionMap := []gitVersion{}
err = walkVersion(r, c, tagMap, &versionMap, masterHead.Hash().String(), settings)
walker := newBranchWalker(r, c, tagMap, settings, false, masterHead.Hash().String())
versionMap, err := walker.GetVersionMap()
if err != nil {
return nil, errors.Wrap(err, "getVersion failed")
return nil, err
}

var baseVersion *semver.Version
Expand Down Expand Up @@ -149,111 +154,6 @@ func getVersion(r *git.Repository, h *plumbing.Reference, tagMap map[string]stri
return baseVersion, nil
}

func getMasterVersion(r *git.Repository, masterHead *plumbing.Reference, tagMap map[string]string, settings *Settings) (version *semver.Version, err error) {
versionMap := []gitVersion{}

c, err := r.CommitObject(masterHead.Hash())
if err != nil {
return nil, err
}
err = walkVersion(r, c, tagMap, &versionMap, "", settings)
if err != nil {
return nil, err
}

var baseVersion *semver.Version
index := len(versionMap) - 1
if versionMap[index].IsSolid {
baseVersion = versionMap[index].Name
index--
} else {
baseVersion, err = semver.NewVersion("0.0.0")
if err != nil {
return nil, err
}
}

if index < 0 {
return baseVersion, nil
}

for ; index >= 0; index-- {
v := versionMap[index]
switch {
case v.MajorBump:
baseVersion.BumpMajor()
case v.MinorBump:
baseVersion.BumpMinor()
case v.PatchBump:
baseVersion.BumpPatch()
default: // every commit in master has at least a patch bump
baseVersion.BumpPatch()
}
}

return baseVersion, nil
}

func walkVersion(r *git.Repository, ref *object.Commit, tagMap map[string]string, versionMap *[]gitVersion, endHash string, settings *Settings) error {
tag, ok := tagMap[ref.Hash.String()]
if ok {
tagVersion, err := semver.NewVersion(tag)
if err != nil {
return err
}
*versionMap = append(*versionMap, gitVersion{IsSolid: true, Name: tagVersion})
return nil
}

matched, err := regexp.MatchString(settings.MajorPattern, ref.Message)
if err != nil {
return err
}
if matched {
*versionMap = append(*versionMap, gitVersion{IsSolid: false, MajorBump: true})
return checkWalkParent(r, ref, tagMap, versionMap, endHash, settings)
}

matched, err = regexp.MatchString(settings.MinorPattern, ref.Message)
if err != nil {
return err
}
if matched {
*versionMap = append(*versionMap, gitVersion{IsSolid: false, MinorBump: true})
return checkWalkParent(r, ref, tagMap, versionMap, endHash, settings)
}

matched, err = regexp.MatchString(settings.PatchPattern, ref.Message)
if err != nil {
return err
}
if matched {
*versionMap = append(*versionMap, gitVersion{IsSolid: false, PatchBump: true})
return checkWalkParent(r, ref, tagMap, versionMap, endHash, settings)
}

*versionMap = append(*versionMap, gitVersion{IsSolid: false})

return checkWalkParent(r, ref, tagMap, versionMap, endHash, settings)
}

func checkWalkParent(r *git.Repository, ref *object.Commit, tagMap map[string]string, versionMap *[]gitVersion, endHash string, settings *Settings) error {
if ref.NumParents() == 0 {
return nil
}

parent, err := ref.Parent(0)
if err != nil {
return nil
}

if parent.Hash.String() == endHash {
return nil
}

return walkVersion(r, parent, tagMap, versionMap, endHash, settings)
}

func getCurrentBranch(r *git.Repository, h *plumbing.Reference) (name string, err error) {
branchName := ""

Expand Down

0 comments on commit f1b2729

Please sign in to comment.