如何使用JGit找到提交距离?

huangapple go评论91阅读模式
英文:

How to find commit distance using JGit?

问题

我有一个小的 Kotlin 工具类,使用 JGit 查找以下信息:

branch, latestCommit, lastTag, lastTagCommit, lastReleaseTag, lastReleaseTagCommit, commitDistance

其中 lastReleaseTag 通过匹配给定的前缀来找到。

所有这些都在工作,除了 commitDistance,它表示在 latestCommit 和一个标签之间的提交次数。我正在使用 RevWalkUtils.count,但它总是返回零。

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
        }
    }
}

通过命令行调用 git rev-list --count start...end 返回 33。

JGit 版本为 5.9.0.202009080501-r。

英文:

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.

答案1

得分: 2

感谢 @fredrik,只需简单地在对 RevWalkUtils.count 的调用中交换提交即可。然而,事实证明 RevWalkUtils.count 返回的数量比 git rev-list --count start...end 大,可能是因为这个原因:

计算从起始点到达一个从终点可达的提交之间的提交数量。

我最终按以下方式更改了我的实现:

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)
    }
}
英文:

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)
}
}

huangapple
  • 本文由 发表于 2020年10月22日 16:53:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/64478667.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定