Skip to content

Commit

Permalink
Version is calculated based on HEAD commit, but not on the latest one (
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
orchestr7 authored Jan 21, 2025
1 parent a70a078 commit 77ffabf
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 22 deletions.
48 changes: 36 additions & 12 deletions core/src/main/kotlin/com/akuleshov7/vercraft/core/Branch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<RevCommit> = 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
Expand All @@ -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
Expand Down
6 changes: 4 additions & 2 deletions core/src/main/kotlin/com/akuleshov7/vercraft/core/Releases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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."
)
}
Expand All @@ -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) }
)
}
Expand All @@ -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 }

Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
}
}
4 changes: 3 additions & 1 deletion core/src/test/kotlin/com/akuleshov7/utils/GitTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ 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)
val resultedVer = releases.version.calc()
println(">>++ VerCrafted: $resultedVer")
}
}
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
kotlin.code.style=official
group=com.akuleshov7.vercraft
version=0.2.0
version=0.3.0

0 comments on commit 77ffabf

Please sign in to comment.