Skip to content
This repository has been archived by the owner on Oct 5, 2018. It is now read-only.

Commit

Permalink
Improve NearestVersionLocator performance
Browse files Browse the repository at this point in the history
Wasn't excluding enough commits from walking before. This new
approach avoids walking over any tags that are reachable from
another tag.
  • Loading branch information
ajoberstar committed Oct 17, 2015
1 parent 773c6fb commit 9686285
Showing 1 changed file with 12 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class NearestVersionLocator {
NearestVersion locate(Grgit grgit) {
logger.debug('Locate beginning on branch: {}', grgit.branch.current.fullName)

// Reuse a single walk to make use of caching.
RevWalk walk = new RevWalk(grgit.repository.jgit.repo)
try {
walk.retainBody = false
Expand All @@ -97,7 +98,6 @@ class NearestVersionLocator {
List normalTags = tags.findAll { !it.version.preReleaseVersion }
RevCommit head = toRev(grgit.head())

// Normals need to be handled first, since the anys would exclude them.
def normal = findNearestVersion(walk, head, normalTags)
def any = findNearestVersion(walk, head, tags)

Expand All @@ -109,34 +109,21 @@ class NearestVersionLocator {
}

private Map findNearestVersion(RevWalk walk, RevCommit head, List versionTags) {
/*
* By excluding the parents of any tagged versions, we avoid walking back
* too far in the history.
*/
versionTags.collectMany {
it.rev.parents as List
}.each {
walk.markUninteresting(it)
}
walk.reset()
walk.markStart(head)
Map versionTagsByRev = versionTags.groupBy { it.rev }

/*
* Filter down to tags that are reachable from the head. This will generally
* only leave multiple results in two scenarios:
* - Single commit with multiple version tags
* - Multiple version tags in parallel branches
*/
def reachableVersionTags = versionTags.findAll { versionTag ->
walk.isMergedInto(versionTag.rev, head)
}.collect { versionTag ->
def reachableVersionTags = walk.collectMany { rev ->
def matches = versionTagsByRev[rev]
if (matches) {
// Parents can't be "nearer". Exclude them to avoid extra walking.
rev.parents.each { walk.markUninteresting(it) }
}
matches ?: []
}.each { versionTag ->
versionTag.distance = RevWalkUtils.count(walk, head, versionTag.rev)
versionTag
}

/*
* If any were found, make sure we pick the smallest distance or, if there
* is a match, the one with the highest version precedence. If none were
* found, use a base value.
*/
if (reachableVersionTags) {
return reachableVersionTags.min { a, b ->
def distanceCompare = a.distance <=> b.distance
Expand Down

0 comments on commit 9686285

Please # to comment.