Reputation: 24617
I've a little Kotlin utility class that uses JGit to find the following information:
branch, latestCommit, lastTag, lastTagCommit, lastReleaseTag, lastReleaseTagCommit, commitDistance
where lastReleaseTag
is found by matching a given prefix.
All that is working, except for commitDistance
, which is the number of commits between the latestCommit
and a tag. I'm using RevWalkUtils.count, but it always returns zero.
class GitRepo(dir: File) {
private val log = LoggerFactory.getLogger(GitRepo::class.java)
constructor(dir: String) : this(File(dir))
private val repository = FileRepositoryBuilder()
.setGitDir(File(dir, ".git"))
.readEnvironment()
.findGitDir()
.setMustExist(true)
.build()
@JvmOverloads
fun info(releaseTagPrefix: String = "release/"): RepoInfo {
repository.use { repo ->
RevWalk(repo).use { walk ->
val latestCommit: RevCommit? = Git(repository).use {
try {
it.log().setMaxCount(1).call().iterator().next()
} catch (ex: NoHeadException) {
log.warn("Repository has no HEAD")
null
}
}
val tags = repo.refDatabase.getRefsByPrefix("refs/tags/")
.groupBy { it.name.startsWith("refs/tags/$releaseTagPrefix") }
.mapValues { entry ->
entry.value.maxByOrNull { it.name }
}
val lastReleaseTag = tags[true]
val lastTag = tags[false]
val lastTagCommit = lastTag?.toRevCommit(walk)
val commitDistance = if (latestCommit == null || lastTagCommit == null) 0
else RevWalkUtils.count(walk, latestCommit, lastTagCommit)
return RepoInfo(
repo.branch,
latestCommit?.toObjectId()?.shorten(),
lastTag?.tagName(),
lastTag?.objectId?.shorten(),
lastReleaseTag?.tagName(),
lastReleaseTag?.objectId?.shorten(),
commitDistance
)
}
}
}
private fun ObjectId.shorten(): String {
return name.take(8)
}
private fun Ref.tagName(): String? {
return "refs\\/tags\\/(.*)".toRegex().find(this.name)?.groupValues?.get(1)
}
private fun Ref.toRevCommit(revWalk: RevWalk): RevCommit? {
val id = repository.refDatabase.peel(this)?.peeledObjectId ?: objectId
return try {
revWalk.parseCommit(id)
} catch (ex: MissingObjectException) {
log.warn("Tag: {} points to a non-existing commit", tagName())
null
}
}
}
A command line invocation of git rev-list --count start...end
returns 33.
JGit 5.9.0.202009080501-r.
Upvotes: 1
Views: 397
Reputation: 24617
Thanks to @fredrik, it's just a simple matter of swapping the commits in the call to RevWalkUtils.count
. However, it turns out that RevWalkUtils.count
is returning a greater number than git rev-list --count start...end
, perhaps because of this:
count the number of commits that are reachable from start until a commit that is reachable from end is encountered
I ended up changing my implementation as follows:
class GitRepo(dir: File) {
constructor(dir: String) : this(File(dir))
private val log = LoggerFactory.getLogger(GitRepo::class.java)
private val repository = FileRepositoryBuilder()
.setGitDir(File(dir, ".git"))
.readEnvironment()
.findGitDir()
.setMustExist(true)
.build()
@JvmOverloads
fun info(tagPrefix: String = ".*"): RepoInfo {
repository.use { repo ->
val lastTag: Ref? = repo.refDatabase.getRefsByPrefix("refs/tags/")
.filter { it.name.matches("refs/tags/$tagPrefix".toRegex()) }
.maxByOrNull { it.name }
var latestCommit: RevCommit? = null
var lastTagCommit: RevCommit?
var commitDistance = 0
Git(repo).use { git ->
try {
latestCommit = git.log().setMaxCount(1).call().firstOrNull()
lastTagCommit = lastTag?.let {
val id = repo.refDatabase.peel(it)?.peeledObjectId ?: it.objectId
git.log().add(id).call().firstOrNull()
}
if (latestCommit != null && lastTagCommit != null) {
commitDistance = git.log().addRange(lastTagCommit, latestCommit).call().count()
}
} catch (ex: NoHeadException) {
log.warn("Repository has no HEAD")
} catch (ex: MissingObjectException) {
log.warn("Tag: {} points to a non-existing commit: ", lastTag?.tagName(), ex.objectId.shorten())
}
return RepoInfo(
repo.branch,
latestCommit?.toObjectId()?.shorten(),
lastTag?.tagName(),
lastTag?.objectId?.shorten(),
commitDistance
)
}
}
}
private fun ObjectId.shorten(): String {
return name.take(8)
}
private fun Ref.tagName(): String? {
return "refs\\/tags\\/(.*)".toRegex().find(name)?.groupValues?.get(1)
}
}
Upvotes: 2