Skip to content

Commit 0d35aa0

Browse files
committed
fix(ChangeLog): Don't include changes from an earlier pre-release in the change log (#61)
1 parent 82a6bec commit 0d35aa0

File tree

3 files changed

+88
-62
lines changed

3 files changed

+88
-62
lines changed

src/main/kotlin/git/semver/plugin/semver/MutableSemVersion.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,13 @@ internal class MutableSemVersion(
231231
}
232232
}
233233

234-
internal fun mergeChanges(versions: List<MutableSemVersion>) {
234+
internal fun mergeChanges(versions: List<MutableSemVersion>): MutableSemVersion {
235235
this.commitCount = versions.sumOf { it.commitCount }
236236
this.bumpPatch = versions.sumOf { it.bumpPatch }
237237
this.bumpMinor = versions.sumOf { it.bumpMinor }
238238
this.bumpMajor = versions.sumOf { it.bumpMajor }
239239
this.bumpPre = versions.sumOf { it.bumpPre }
240+
return this
240241
}
241242

242243
private val hasPendingChanges

src/main/kotlin/git/semver/plugin/semver/VersionFinder.kt

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ package git.semver.plugin.semver
33
import git.semver.plugin.scm.Commit
44
import git.semver.plugin.scm.IRefInfo
55
import org.slf4j.LoggerFactory
6-
import java.util.ArrayDeque
7-
import java.util.PriorityQueue
6+
import java.util.*
87

98
class 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

src/test/kotlin/git/semver/plugin/scm/GitProviderTest.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,31 @@ class GitProviderTest {
124124
}
125125
}
126126

127+
@Test
128+
fun changeLog_stops_at_prerelease() {
129+
val gitDir = File("build/integrationTest33")
130+
gitDir.mkdirs()
131+
132+
val gitProvider = GitProvider(SemverSettings())
133+
Git.init().setDirectory(gitDir).call().use {
134+
initOrReset(it, gitProvider)
135+
commit(it, "fix: first change", gitProvider)
136+
release(gitProvider, it, "alpha.1")
137+
commit(it, "build: some changes", gitProvider)
138+
commit(it, "feat: another feature", gitProvider)
139+
commit(it, "fix: another change", gitProvider)
140+
release(gitProvider, it, "-")
141+
142+
val actual = gitProvider().changeLog(it)
143+
144+
assertThat(actual.map(Commit::toString))
145+
.contains("feat: another feature")
146+
.contains("fix: another change")
147+
.contains("release: v0.1.0")
148+
.doesNotContain("fix: first change")
149+
}
150+
}
151+
127152
private fun lotsOfCommits(it: Git, gitProvider: GitProvider) {
128153
initOrReset(it, gitProvider)
129154
commit(it, "build: some changes", gitProvider)

0 commit comments

Comments
 (0)