@@ -3,8 +3,7 @@ package git.semver.plugin.semver
33import git.semver.plugin.scm.Commit
44import git.semver.plugin.scm.IRefInfo
55import org.slf4j.LoggerFactory
6- import java.util.ArrayDeque
7- import java.util.PriorityQueue
6+ import java.util.*
87
98class VersionFinder (private val settings : SemverSettings , private val tags : Map <String , List <IRefInfo >>) {
109 private val logger = LoggerFactory .getLogger(javaClass)
@@ -32,51 +31,74 @@ class VersionFinder(private val settings: SemverSettings, private val tags: Map<
3231 }
3332
3433 fun getChangeLog (commit : Commit ): List <Commit > {
35- val changeLog = mutableListOf<Commit >()
36-
3734 val release = getReleaseSemVersionFromCommit(commit)
38- if (isRelease(release)) {
39- findVersion(commit.parents, changeLog)
35+ return if (isRelease(release, true )) {
36+ // If this is a release commit, find all changes from the parent commits
37+ val changeLog = getChangeLog(getIncludedCommits(commit.parents, true ).includedCommits)
4038 changeLog.add(commit)
39+ changeLog
4140 } else {
42- findVersion( commit, changeLog )
41+ getChangeLog(getIncludedCommits(sequenceOf( commit), true ).includedCommits )
4342 }
44- return changeLog
4543 }
4644
47- private fun findVersion (startCommit : Commit , changeLog : MutableList <Commit >? = null): MutableSemVersion {
45+ private fun getChangeLog (commitData : List <CommitData >): MutableList <Commit > = commitData.asReversed()
46+ .filter { it.parents.size <= 1 }
47+ .map { it.commit }
48+ .toMutableList()
49+
50+ private fun findVersion (startCommit : Commit ): MutableSemVersion {
4851 if (startCommit.sha.isBlank()) {
4952 // This is a fake commit created when there exists no real commits
5053 return versionZero()
5154 }
52- return findVersion(sequenceOf(startCommit), changeLog)
55+
56+ val result = getIncludedCommits(sequenceOf(startCommit), false )
57+ var lastFoundVersion = result.lastFoundVersion
58+ for (commitData in result.includedCommits.asReversed()) {
59+ // Second time we visit this commit after visiting parent commits
60+
61+ // Check if we found a preRelease version first time we visited this commit
62+ val preReleaseVersion = result.visitedCommits[commitData.commit.sha]
63+ // Get and clear the semVersions for the parents so that they are not counted twice
64+ val parentSemVersions = commitData.parents
65+ .mapNotNull { result.visitedCommits.remove(it) }
66+ .toList()
67+ val maxVersionFromParents = getCombinedParentVersion(parentSemVersions)
68+ maxVersionFromParents.updateFromCommit(commitData.commit, settings, preReleaseVersion)
69+ result.visitedCommits[commitData.commit.sha] = maxVersionFromParents
70+ lastFoundVersion = maxVersionFromParents
71+ }
72+ return lastFoundVersion
5373 }
5474
55- data class CommitData (
75+ private class CommitData (
5676 val commit : Commit ,
57- val parents : MutableList <String >,
58- val isParentOfReleaseCommit : Boolean
77+ val parents : MutableList <String > = ArrayList (1) ,
78+ val isParentOfReleaseCommit : Boolean = false
5979 ) : Comparable<CommitData> {
6080
6181 override fun compareTo (other : CommitData ): Int {
6282 return other.commit.commitTime.compareTo(commit.commitTime)
6383 }
6484 }
6585
66- private fun findVersion (
67- commitsList : Sequence <Commit >,
68- changeLog : MutableList <Commit >?
69- ): MutableSemVersion {
86+ private class IncludedCommits (
87+ val lastFoundVersion : MutableSemVersion ,
88+ val visitedCommits : MutableMap <String , MutableSemVersion ?>,
89+ val includedCommits : List <CommitData >
90+ )
7091
71- var liveBranchCount = 1 ;
92+ private fun getIncludedCommits (commitsList : Sequence <Commit >, stopAtPreRelease : Boolean ): IncludedCommits {
93+ var liveBranchCount = 1
7294 var lastFoundVersion = versionZero()
7395
7496 // This code is a recursive algoritm rewritten as iterative to avoid stack overflow exception.
7597 // Unfortunately that makes it hard to understand.
7698
77- val commits = PriorityQueue (commitsList.map { CommitData (it, ArrayList ( 1 ), false ) }.toList())
99+ val commits = PriorityQueue (commitsList.map { CommitData (it) }.toList())
78100 val visitedCommits = mutableMapOf<String , MutableSemVersion ?>()
79- val includedCommits = ArrayDeque <CommitData >()
101+ val includedCommits = mutableListOf <CommitData >()
80102
81103 while (commits.isNotEmpty()) {
82104 val commitData = commits.remove()
@@ -92,56 +114,46 @@ class VersionFinder(private val settings: SemverSettings, private val tags: Map<
92114 val releaseVersion = getReleaseSemVersionFromCommit(currentCommit)
93115 visitedCommits[currentCommit.sha] = releaseVersion
94116
95- if (isRelease(releaseVersion)) {
117+ if (isRelease(releaseVersion, stopAtPreRelease )) {
96118 logger.debug(" Release version found: {}" , releaseVersion)
97119 // Release fond so no need to visit this commit again
98120 lastFoundVersion = releaseVersion!!
99-
100121 liveBranchCount - = 1
101122
102123 markParentCommitsAsVisited(liveBranchCount, currentCommit, visitedCommits, commits)
103124 } else {
104125 // This is a normal commit or a pre-release. We will visit this again in the second phase.
105- includedCommits.push (commitData)
126+ includedCommits.add (commitData)
106127
107128 currentCommit.parents.forEach {
108129 commitData.parents.add(it.sha)
109130 if (! visitedCommits.containsKey(it.sha)) {
110131 // prepare to visit parent commit
111- commits.add(CommitData (it, ArrayList ( 1 ), false ))
132+ commits.add(CommitData (it))
112133 }
113134 }
114135 liveBranchCount + = commitData.parents.size - 1
115136 }
116137 }
117138 }
139+ return IncludedCommits (lastFoundVersion, visitedCommits, includedCommits)
140+ }
118141
119- while (includedCommits.isNotEmpty()) {
120-
121- val commitData = includedCommits.pop()
122- val currentCommit = commitData.commit
123- val currentParentList = commitData.parents
124-
125- // Second time we visit this commit after visiting parent commits
126- addToChangeLog(currentCommit, changeLog, currentParentList.size > 1 )
127-
128- // Check if we found a preRelease version first time we visited this commit
129- val preReleaseVersion = visitedCommits[currentCommit.sha]
130-
131- // Get and clear the semVersions for the parents so that they are not counted twice
132- val parentSemVersions = currentParentList
133- .mapNotNull { visitedCommits.put(it, null ) }
134- .toList()
135-
136- val maxVersionFromParents = parentSemVersions.maxOrNull() ? : versionZero()
137- maxVersionFromParents.mergeChanges(parentSemVersions)
138- maxVersionFromParents.updateFromCommit(currentCommit, settings, preReleaseVersion)
139- visitedCommits[currentCommit.sha] = maxVersionFromParents
142+ private fun getCombinedParentVersion (parentSemVersions : List <MutableSemVersion >): MutableSemVersion {
143+ return when {
144+ parentSemVersions.isEmpty() -> {
145+ versionZero()
146+ }
140147
141- lastFoundVersion = maxVersionFromParents
148+ parentSemVersions.size == 1 -> {
149+ parentSemVersions[0 ]
150+ }
142151
152+ else -> {
153+ parentSemVersions.max()
154+ .mergeChanges(parentSemVersions)
155+ }
143156 }
144- return lastFoundVersion
145157 }
146158
147159 private fun markParentCommitsAsVisited (
@@ -159,20 +171,8 @@ class VersionFinder(private val settings: SemverSettings, private val tags: Map<
159171 }
160172 }
161173
162- private fun addToChangeLog (
163- currentCommit : Commit ,
164- changeLog : MutableList <Commit >? ,
165- isMergeCommit : Boolean
166- ) {
167- if (isMergeCommit) {
168- // Ignore merge commits
169- return
170- }
171- changeLog?.add(currentCommit)
172- }
173-
174- private fun isRelease (releaseVersion : MutableSemVersion ? ) =
175- releaseVersion != null && ! releaseVersion.isPreRelease
174+ private fun isRelease (releaseVersion : MutableSemVersion ? , stopAtPreRelease : Boolean ) =
175+ releaseVersion != null && (stopAtPreRelease || ! releaseVersion.isPreRelease)
176176
177177 private fun versionZero () = MutableSemVersion ()
178178
0 commit comments