From 1c89edd87bf53c8e34091ff5f86fcf907dd8bfe5 Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Sat, 6 Jan 2024 12:59:07 +0100
Subject: [PATCH] fix: the tags are now processed branch-related #15

---
 README.md             |   1 -
 source/commandline.go |   7 ++
 source/git.go         | 147 ++++++++++++++++++++++--------------------
 3 files changed, 84 insertions(+), 71 deletions(-)

diff --git a/README.md b/README.md
index ba4bb36..4fa95e6 100644
--- a/README.md
+++ b/README.md
@@ -101,7 +101,6 @@ version predict
 **Makefile**
 
 ```makefile
-
 build:
     version patch --path $(PROJECT_ROOT)version.json --selector "version"
     $(eval VERSION := $(shell cat version.json | jq -r .version))
diff --git a/source/commandline.go b/source/commandline.go
index 612357e..b778a9e 100644
--- a/source/commandline.go
+++ b/source/commandline.go
@@ -264,6 +264,13 @@ func executeCommand() {
 
 	err = writeVersion(newVersion)
 	if err != nil {
+
+		if activeCommand.Name == "auto" || activeCommand.Name == "predict" {
+			if arguments.Auto.ExitCode || arguments.Predict.ExitCode {
+				os.Exit(10)
+			}
+		}
+
 		_, err := fmt.Fprintf(os.Stderr, "Error: %s\n", err)
 		if err != nil {
 			fmt.Printf("Error: %s\n", err)
diff --git a/source/git.go b/source/git.go
index d4c8404..aa4c303 100644
--- a/source/git.go
+++ b/source/git.go
@@ -90,7 +90,7 @@ func GetCommitType() (CommitType, error) {
 		fmt.Println("tag commit:", tagCommit)
 	}
 
-	commitType, err := getCommitTypeSinceTag(tagCommit)
+	commitType, err := findNextTagType(gitRepo, tagCommit, verbosity)
 	if err != nil {
 		return OtherCommit, err
 	}
@@ -103,7 +103,7 @@ func GetCommitType() (CommitType, error) {
 }
 
 func getLatestSemanticTag() (SemanticVersion, error) {
-	tagList, err := getSemanticTags()
+	tagList, err := getSemanticTags(gitRepo)
 	if err != nil {
 		return SemanticVersion{}, err
 	}
@@ -124,51 +124,66 @@ func getTagCommit(tag string) (*object.Commit, error) {
 		return nil, fmt.Errorf("failed to get tags: %v", err)
 	}
 
-	var tagCommit *object.Commit
+	var commitHash *object.Commit
 	err = tags.ForEach(func(t *plumbing.Reference) error {
-		if t.Name().Short() == tag {
+		if t.Name().Short() != tag {
+			return nil
+		}
 
-			tagObj, err := r.TagObject(t.Hash())
-			if err == plumbing.ErrObjectNotFound {
-				commit, err := r.CommitObject(t.Hash())
-				if err != nil {
-					return err
-				}
-				tagCommit = commit
-			} else if err != nil {
-				return err
-			} else {
-
-				tagCommit, err = tagObj.Commit()
-				if err != nil {
-					return err
-				}
-			}
-			return ErrStopIteration
+		// Resolve the tag to a commit
+		obj, err := r.TagObject(t.Hash())
+		var hash plumbing.Hash
+		if err == nil {
+			// This is an annotated tag
+			hash = obj.Target
+		} else {
+			// This is a lightweight tag or an error occurred
+			hash = t.Hash()
 		}
-		return nil
+
+		commitHash, err = r.CommitObject(hash)
+
+		return ErrStopIteration
+
 	})
 
 	if err != nil && err != ErrStopIteration {
 		return nil, fmt.Errorf("failed to iterate over tags: %v", err)
 	}
 
-	if tagCommit == nil {
+	if commitHash == nil {
 		return nil, fmt.Errorf("tag '%s' not found in commit log", tag)
 	}
 
-	return tagCommit, nil
+	return commitHash, nil
 }
 
-func getSemanticTags() ([]SemanticVersion, error) {
-
-	r := gitRepo
-
+func getSemanticTags(r *git.Repository) ([]SemanticVersion, error) {
 	tags, err := r.Tags()
 	if err != nil {
 		return nil, fmt.Errorf("failed to get tags: %v", err)
 	}
 
+	headRef, err := r.Head()
+	if err != nil {
+		return nil, fmt.Errorf("failed to get branch: %v", err)
+	}
+
+	commitIter, err := r.Log(&git.LogOptions{From: headRef.Hash()})
+	if err != nil {
+		return nil, fmt.Errorf("failed to get commit history: %v", err)
+	}
+
+	commitHistory := make(map[plumbing.Hash]bool)
+	err = commitIter.ForEach(func(c *object.Commit) error {
+		commitHistory[c.Hash] = true
+		return nil
+	})
+
+	if err != nil {
+		return nil, fmt.Errorf("failed to iterate over commits: %v", err)
+	}
+
 	var tagList []SemanticVersion
 	err = tags.ForEach(func(tag *plumbing.Reference) error {
 		tagName := tag.Name().Short()
@@ -177,6 +192,26 @@ func getSemanticTags() ([]SemanticVersion, error) {
 			fmt.Println("found tag: ", tagName)
 		}
 
+		// Resolve the tag to a commit
+		var commitHash plumbing.Hash
+		obj, err := r.TagObject(tag.Hash())
+		if err == nil {
+			// This is an annotated tag
+			commitHash = obj.Target
+		} else {
+			// This is a lightweight tag or an error occurred
+			commitHash = tag.Hash()
+
+			if verbosity {
+				fmt.Println("tag " + tagName + " is a lightweight tag, you should use an annotated tag")
+			}
+
+		}
+
+		if _, exists := commitHistory[commitHash]; !exists {
+			return nil
+		}
+
 		if versionRegex.MatchString(tagName) {
 			tagList = append(tagList, ParseSemanticVersion(tagName))
 		}
@@ -199,69 +234,45 @@ func getSemanticTags() ([]SemanticVersion, error) {
 	return tagList, nil
 }
 
-func getCommitTypeSinceTag(tagCommit *object.Commit) (CommitType, error) {
-
-	r := gitRepo
+func findNextTagType(r *git.Repository, tagCommit *object.Commit, verbosity bool) (CommitType, error) {
 
 	cIter, err := r.Log(&git.LogOptions{})
+
 	if err != nil {
-		return OtherCommit, fmt.Errorf("failed to get commit log: %v", err)
+		return OtherCommit, fmt.Errorf("failed to get commit log from tag: %v", err)
 	}
 
-	var commitType CommitType
-	found := false
-
+	var commitType CommitType = OtherCommit
 	counter := 0
 
 	err = cIter.ForEach(func(commit *object.Commit) error {
-
 		counter++
 
 		if commit.Hash == tagCommit.Hash {
-			found = true
-
-			if verbosity {
-				fmt.Println("found tag commit after", counter, "commits")
-			}
-
-			return storer.ErrStop // stop iteration
+			return storer.ErrStop
 		}
 
 		message := strings.TrimSpace(commit.Message)
-
 		if strings.HasPrefix(message, "feat") {
-
 			commitType = FeatCommit
-			found = true
-
 			if verbosity {
-				fmt.Println("found feat commit after", counter, "commits")
+				fmt.Printf("Found 'feat' commit after %d commits\n", counter)
 			}
-
+			return storer.ErrStop
 		} else if strings.HasPrefix(message, "fix") {
-
-			// if we already found a feat or breaking commit, we don't care about fix commits
-			if commitType < FixCommit && commitType > OtherCommit {
-				return nil
-			}
-
-			commitType = FixCommit
-
-			if verbosity {
-				fmt.Println("found fix commit after", counter, "commits")
+			if commitType < FixCommit {
+				commitType = FixCommit
+				if verbosity {
+					fmt.Printf("Found 'fix' commit after %d commits\n", counter)
+				}
 			}
-
 		} else if containsBreakingChangeFooter(message) {
 			commitType = BreakingCommit
-			found = true
-
 			if verbosity {
-				fmt.Println("found breaking commit after", counter, "commits")
+				fmt.Printf("Found breaking change after %d commits\n", counter)
 			}
-
-			return storer.ErrStop // stop iteration
+			return storer.ErrStop
 		}
-
 		return nil
 	})
 
@@ -269,10 +280,6 @@ func getCommitTypeSinceTag(tagCommit *object.Commit) (CommitType, error) {
 		return OtherCommit, fmt.Errorf("failed to iterate over commit log: %v", err)
 	}
 
-	if !found {
-		return OtherCommit, fmt.Errorf("tag commit not found in commit log")
-	}
-
 	return commitType, nil
 }
 
-- 
GitLab