From 77ffabf4512d1f62b8158b72e58ec1a125e8f2c5 Mon Sep 17 00:00:00 2001 From: White Rabbit Date: Wed, 22 Jan 2025 00:45:27 +0300 Subject: [PATCH] Version is calculated based on HEAD commit, but not on the latest one (#26) ### What's done: - Due to a bug Vercraft has calculated the version based on the latest commit on the branch, even when the checked-out commit was different --- .../com/akuleshov7/vercraft/core/Branch.kt | 48 ++++++++++++++----- .../com/akuleshov7/vercraft/core/Releases.kt | 6 ++- .../vercraft/core/VersionCalculator.kt | 16 ++++--- .../kotlin/com/akuleshov7/utils/GitTest.kt | 4 +- gradle.properties | 2 +- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/core/src/main/kotlin/com/akuleshov7/vercraft/core/Branch.kt b/core/src/main/kotlin/com/akuleshov7/vercraft/core/Branch.kt index d99b123..aae30a3 100644 --- a/core/src/main/kotlin/com/akuleshov7/vercraft/core/Branch.kt +++ b/core/src/main/kotlin/com/akuleshov7/vercraft/core/Branch.kt @@ -12,26 +12,50 @@ public const val VERCRAFT_BRANCH: String = "VERCRAFT_BRANCH" public class Branch(git: Git, public val ref: Ref) { public val gitLog: List = git.log().add(ref.objectId).call().toList() + /** - * Counts the number of commits (from the end) which were made in branch after the [[startCommit]]. For example: - * 1 -> 2 -> 3 -> 4 -> latest - * + -> + -> v0 -> v1 -> v2 - * commitNumberAfterThis(3) == 2 + * Calculates the number of commits between two commits in a branch. + * The gitLog is expected to be in reverse order (from latest to oldest). + * + * @param startCommit The starting commit. + * @param endCommit The ending commit. + * @return The number of commits between startCommit and endCommit, or -1 if: + * - Either commit is not found. + * - startCommit appears after endCommit in the reversed log (latest -> oldest). */ - public fun numberOfCommitsAfter(startCommit: RevCommit): Int { + public fun distanceBetweenCommits(startCommit: RevCommit, endCommit: RevCommit): Int { var count = 0 - // gitLog is always reverted from the last commit to the first one - gitLog.forEach { commitInBranch -> - if (commitInBranch.id.name != startCommit.id.name) { - ++count - } else { + var endFound = false + + for (commitInBranch in gitLog) { + // check for the endCommit first since the log is reversed + if (commitInBranch.id.name == endCommit.id.name) { + endFound = true + } + + if (commitInBranch.id.name == startCommit.id.name) { + if (!endFound) { + throw IllegalStateException( + "Invalid commit order: Head commit '${endCommit.id.toString().substring(0, 5)}' was found " + + "before the starting commit '${startCommit.id.toString().substring(0, 5)}'. " + ) + } return count } + + if (endFound) { + ++count + } } - return -1 + // If we complete the loop without finding both commits, return -1 + throw IllegalArgumentException( + "Not able to find neither commit ${startCommit.id.toString().substring(0, 5)}, " + + "nor commit ${endCommit.id.toString().substring(0, 5)}" + ) } + /** * Finds the commit in [[sourceBase]] branch when a new sub-branch was created from it. For example: * main: 1 -> 2 -> 3 -> 4 @@ -42,7 +66,7 @@ public class Branch(git: Git, public val ref: Ref) { * * The base commit is 2. */ - public fun findBaseCommitIn(sourceBase: Branch): RevCommit? { + public fun intersectionCommitWithBranch(sourceBase: Branch): RevCommit? { // (!) gitLog is always in a reversed order (from last to first commit): // 4 3 2 1 // 6 5 2 diff --git a/core/src/main/kotlin/com/akuleshov7/vercraft/core/Releases.kt b/core/src/main/kotlin/com/akuleshov7/vercraft/core/Releases.kt index 227fc48..c5019ae 100644 --- a/core/src/main/kotlin/com/akuleshov7/vercraft/core/Releases.kt +++ b/core/src/main/kotlin/com/akuleshov7/vercraft/core/Releases.kt @@ -72,7 +72,7 @@ public class Releases public constructor(private val git: Git, private val confi "to VerCraft by setting ENV variable \$VERCRAFT_BRANCH. " ) throw NullPointerException( - "Current HEAD is detached and CI env variables with the branch name are not set, so" + + "Current HEAD is detached and CI env variables with the branch name are not set, so " + "not able to determine the original branch name." ) } @@ -82,6 +82,7 @@ public class Releases public constructor(private val git: Git, private val confi git.branchList() .setListMode(REMOTE) .call() + // TODO: handle java.util.NoSuchElementException: Collection contains no element matching the predicate .first { it.name.endsWith(branchName) } ) } @@ -90,6 +91,7 @@ public class Releases public constructor(private val git: Git, private val confi public fun isReleaseBranch(branch: Branch): Boolean = releaseBranches.find { it.branch == branch } != null + // TODO: latest release should be calculated relatively to the HEAD commit public fun getLatestReleaseBranch(): ReleaseBranch? = releaseBranches.maxByOrNull { it.version } @@ -112,7 +114,7 @@ public class Releases public constructor(private val git: Git, private val confi "$ERROR_PREFIX ReleaseType PATCH has been selected, so no new release branches " + "will be created as patch releases should be made only in existing release branch." ) - // FIXME: need to switch to latest release branch and latest commit and set release tag there + // TODO: need to switch to latest release branch and latest commit and set release tag there } else { createBranch(newVersion) createTag(newVersion) diff --git a/core/src/main/kotlin/com/akuleshov7/vercraft/core/VersionCalculator.kt b/core/src/main/kotlin/com/akuleshov7/vercraft/core/VersionCalculator.kt index f06905f..cfa4523 100644 --- a/core/src/main/kotlin/com/akuleshov7/vercraft/core/VersionCalculator.kt +++ b/core/src/main/kotlin/com/akuleshov7/vercraft/core/VersionCalculator.kt @@ -15,7 +15,9 @@ public class VersionCalculator( private val currentCheckoutBranch: Branch, ) { private val repo: Repository = git.repository - private val headCommit = repo.resolve("HEAD") + private val headCommit = RevWalk(repo).use { + it.parseCommit(repo.resolve("HEAD")) + } public fun calc(): SemVer = when { @@ -44,10 +46,12 @@ public class VersionCalculator( private fun calcVersionInMain(): SemVer { val latestRelease = releases.getLatestReleaseBranch() // if no releases were made so far, then will calculate version starting from the initial commit + // TODO: latest release should be calculated relatively to the HEAD commit val baseCommit = latestRelease?.branch - ?.findBaseCommitIn(currentCheckoutBranch) ?: currentCheckoutBranch.gitLog.last() + ?.intersectionCommitWithBranch(currentCheckoutBranch) + ?: currentCheckoutBranch.gitLog.last() - val distance = currentCheckoutBranch.numberOfCommitsAfter(baseCommit) + val distance = currentCheckoutBranch.distanceBetweenCommits(baseCommit, headCommit) val shortedHashCode = baseCommit.name.substring(0, 5) @@ -127,19 +131,19 @@ public class VersionCalculator( dateFormat.timeZone = TimeZone.getDefault() val formattedDate = dateFormat.format(Date(commit.commitTime * 1000L)) - return SemVer(NO_MAJOR, NO_MINOR, distance + 1) + return SemVer(NO_MAJOR, NO_MINOR, distance) .setPrefix("$formattedDate-$branch") .setPostFix(commit.name.substring(0, 5)) } } private fun distanceFromMainBranch(): Int { - val baseCommit = currentCheckoutBranch.findBaseCommitIn(releases.mainBranch) + val baseCommit = currentCheckoutBranch.intersectionCommitWithBranch(releases.mainBranch) ?: throw IllegalStateException( "Can't find common ancestor commits between ${config.defaultMainBranch} " + "and ${currentCheckoutBranch.ref.name} branches. Looks like these branches have no relation " + "and that is inconsistent git state." ) - return currentCheckoutBranch.numberOfCommitsAfter(baseCommit) + return currentCheckoutBranch.distanceBetweenCommits(baseCommit, headCommit) } } diff --git a/core/src/test/kotlin/com/akuleshov7/utils/GitTest.kt b/core/src/test/kotlin/com/akuleshov7/utils/GitTest.kt index 695aff9..8411c28 100644 --- a/core/src/test/kotlin/com/akuleshov7/utils/GitTest.kt +++ b/core/src/test/kotlin/com/akuleshov7/utils/GitTest.kt @@ -4,10 +4,12 @@ import com.akuleshov7.vercraft.core.DefaultConfig import com.akuleshov7.vercraft.core.Releases import org.eclipse.jgit.api.Git import java.io.File +import kotlin.test.Ignore import kotlin.test.Test class GitTest { @Test + @Ignore fun smokeTest() { Git.open(File("../")).use { git -> val releases = Releases(git, DefaultConfig) @@ -15,4 +17,4 @@ class GitTest { println(">>++ VerCrafted: $resultedVer") } } -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index d08e9f3..2bb0ec8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ kotlin.code.style=official group=com.akuleshov7.vercraft -version=0.2.0 +version=0.3.0