package com.palantir.semver; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.LogCommand; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; public class DescribedTags { private DescribedTags() { // prevents instantiation } public static TagVersionAndCount resolveLatestTagVersionAndCount( Repository repo, TagVersionAndCount curTag, int recur) throws IOException, RefNotFoundException, GitAPIException { Git git = new Git(repo); String described = git.describe().setTarget(curTag.getVersion()).call(); if (described == null) return null; TagVersionAndCount describedTag = parseDescribeOutput(described); if (!SemanticVersions.isValid(GitRepos.stripVFromVersionString(describedTag.getVersion()))) { RevWalk revWalk = new RevWalk(repo); RevCommit describedRev = revWalk.parseCommit(repo.resolve(describedTag.getVersion())); TagVersionAndCount mostRecentParentTag = new TagVersionAndCount("", Integer.MAX_VALUE); for (RevCommit parent : describedRev.getParents()) { TagVersionAndCount parentTag = new TagVersionAndCount(parent.name(), -1); TagVersionAndCount resolvedParentTag = resolveLatestTagVersionAndCount(repo, parentTag, recur + 1); if (resolvedParentTag == null) continue; if (resolvedParentTag.getCount() < mostRecentParentTag.getCount()) { mostRecentParentTag = resolvedParentTag; } } if (mostRecentParentTag.getCount() == Integer.MAX_VALUE) { return null; } return mostRecentParentTag; } else { if (recur != 0) return new TagVersionAndCount(describedTag.getVersion(), -1); return describedTag; } } private static TagVersionAndCount parseDescribeOutput(String describe) { String tagOffsetRegex = "^(.+)-([0-9]*)-g[0-9a-f]+$"; // Form 1: <tag>-<count>-<hash> Pattern tagOffsetPattern = Pattern.compile(tagOffsetRegex); Matcher m = tagOffsetPattern.matcher(describe); if (m.matches()) { return new TagVersionAndCount(m.group(1), Integer.parseInt(m.group(2))); } // Form 2: return new TagVersionAndCount(describe, 0); } private static TagVersionAndCount fixCommitCount(TagVersionAndCount resolved, Repository repo) throws RefNotFoundException, GitAPIException { Git git = new Git(repo); ObjectId target, head; LogCommand logCommand; try { target = repo.getRef(resolved.getVersion()).getPeeledObjectId(); logCommand = git.log(); logCommand.add(target); } catch (IOException e) { throw new SemverGitflowPlugin.VersionApplicationException(e); } int count = 0; for (RevCommit commit : logCommand.call()) { count ++; } return new TagVersionAndCount(resolved.getVersion(), count); } public static TagVersionAndCount getLatestTagVersionAndCount(Repository repo) throws IOException, RefNotFoundException, GitAPIException { TagVersionAndCount tac = resolveLatestTagVersionAndCount(repo, new TagVersionAndCount("HEAD", 0), 0); if (tac.getCount() == -1) return fixCommitCount(tac, repo); return tac; } }