Skip to content
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,14 @@ git.commit.user.email=${git.commit.user.email}
git.commit.message.full=${git.commit.message.full}
git.commit.message.short=${git.commit.message.short}
git.commit.time=${git.commit.time}
git.closest.tag.name=${git.closest.tag.name}
git.closest.tag.commit.count=${git.closest.tag.commit.count}

git.build.user.name=${git.build.user.name}
git.build.user.email=${git.build.user.email}
git.build.time=${git.build.time}
git.build.host=${git.build.host}
git.build.version=${git.build.version}
```

The `git` prefix may be configured in the plugin declaration above.
Expand Down Expand Up @@ -341,11 +344,14 @@ Start out with with adding the above steps to your project, next paste this **gi
<property name="commitMessageFull" value="${git.commit.message.full}"/>
<property name="commitMessageShort" value="${git.commit.message.short}"/>
<property name="commitTime" value="${git.commit.time}"/>
<property name="closestTagName" value="${git.closest.tag.name}"/>
<property name="closestTagCommitCount" value="${git.closest.tag.commit.count}"/>

<property name="buildUserName" value="${git.build.user.name}"/>
<property name="buildUserEmail" value="${git.build.user.email}"/>
<property name="buildTime" value="${git.build.time}"/>
<property name="buildHost" value="${git.build.host}"/>
<porperty name="buildVersion" value="${git.build.version}"/>
</bean>
</beans>
```
Expand Down Expand Up @@ -378,12 +384,15 @@ public class GitRepositoryState {
String commitMessageFull; // =${git.commit.message.full}
String commitMessageShort; // =${git.commit.message.short}
String commitTime; // =${git.commit.time}
String closestTagName; // =${git.closest.tag.name}
String closestTagCommitCount; // =${git.closest.tag.commit.count}

String buildUserName; // =${git.build.user.name}
String buildUserEmail; // =${git.build.user.email}
String buildTime; // =${git.build.time}
String buildHost; // =${git.build.host}

String buildVersion // =${git.build.version}

public GitRepositoryState() {
}
/* Generate setters and getters here */
Expand Down Expand Up @@ -438,11 +447,14 @@ In the end *this is what this service would return*:
+ added license etc",
"commitMessageShort" : "releasing my fun plugin :-)",
"commitTime" : "06.01.1970 @ 16:16:26 CET",
"closestTagName" : "v2.1.0",
"closestTagCommitCount" : "2",

"buildUserName" : "Konrad Malawski",
"buildUserEmail" : "konrad.malawski@java.pl",
"buildTime" : "06.01.1970 @ 16:17:53 CET",
"buildHost" : "github.com"
"buildHost" : "github.com",
"buildVersion" : "v2.1.0-SNAPSHOT"
}
```

Expand Down Expand Up @@ -503,11 +515,14 @@ public GitRepositoryState(Properties properties)
this.commitMessageFull = properties.get("git.commit.message.full").toString();
this.commitMessageShort = properties.get("git.commit.message.short").toString();
this.commitTime = properties.get("git.commit.time").toString();
this.closestTagName = properties.get("git.closest.tag.name").toString();
this.closestTagCommitCount = properties.get("git.closest.tag.commit.count").toString();

this.buildUserName = properties.get("git.build.user.name").toString();
this.buildUserEmail = properties.get("git.build.user.email").toString();
this.buildTime = properties.get("git.build.time").toString();
this.buildHost = properties.get("git.build.host").toString();
this.buildVersion = properties.get("git.build.version").toString();
}
```

Expand Down
234 changes: 11 additions & 223 deletions src/main/java/pl/project13/jgit/DescribeCommand.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* This file is part of git-commit-id-plugin by Konrad Malawski <konrad.malawski@java.pl>
* This file is part of git-commit-id-plugin by Konrad 'ktoso' Malawski <konrad.malawski@java.pl>
*
* git-commit-id-plugin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
Expand All @@ -18,25 +18,21 @@
package pl.project13.jgit;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import pl.project13.jgit.dummy.DatedRevTag;
import pl.project13.maven.git.GitDescribeConfig;
import pl.project13.maven.git.log.LoggerBridge;
Expand All @@ -45,23 +41,16 @@

import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newLinkedList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;

/**
* Implements git's <pre>describe</pre> command.
*
* @author Konrad Malawski
*/
public class DescribeCommand extends GitCommand<DescribeResult> {

private LoggerBridge loggerBridge;
private JGitCommon jGitCommon;

// todo not yet implemented options:
// TODO not yet implemented options:
// private boolean containsFlag = false;
// private boolean allFlag = false;
// private boolean tagsFlag = false;
Expand Down Expand Up @@ -116,6 +105,7 @@ private DescribeCommand(Repository repo, boolean verbose) {
super(repo);
initDefaultLoggerBridge(verbose);
setVerbose(verbose);
this.jGitCommon = new JGitCommon();
}

private void initDefaultLoggerBridge(boolean verbose) {
Expand Down Expand Up @@ -305,7 +295,7 @@ public DescribeResult call() throws GitAPIException {
}

// get commits, up until the nearest tag
List<RevCommit> commits = findCommitsUntilSomeTag(repo, headCommit, tagObjectIdToName);
List<RevCommit> commits = jGitCommon.findCommitsUntilSomeTag(repo, headCommit, tagObjectIdToName);

// if there is no tags or any tag is not on that branch then return generic describe
if (foundZeroTags(tagObjectIdToName) || commits.isEmpty()) {
Expand All @@ -315,7 +305,7 @@ public DescribeResult call() throws GitAPIException {

// check how far away from a tag we are

int distance = distanceBetween(repo, headCommit, commits.get(0));
int distance = jGitCommon.distanceBetween(repo, headCommit, commits.get(0));
String tagName = tagObjectIdToName.get(commits.get(0)).iterator().next();
Pair<Integer, String> howFarFromWhichTag = Pair.of(distance, tagName);

Expand Down Expand Up @@ -396,206 +386,14 @@ RevCommit findHeadObjectId(@NotNull Repository repo) throws RuntimeException {
}
}

private List<RevCommit> findCommitsUntilSomeTag(Repository repo, RevCommit head, @NotNull Map<ObjectId, List<String>> tagObjectIdToName) {
RevWalk revWalk = new RevWalk(repo);
try {
revWalk.markStart(head);

for (RevCommit commit : revWalk) {
ObjectId objId = commit.getId();
if (tagObjectIdToName.size() > 0) {
List<String> maybeList = tagObjectIdToName.get(objId);
if (maybeList != null && maybeList.get(0) != null) {
return Collections.singletonList(commit);
}
}
}

return Collections.emptyList();
} catch (Exception e) {
throw new RuntimeException("Unable to find commits until some tag", e);
}
}

/**
* Calculates the distance (number of commits) between the given parent and child commits.
*
* @return distance (number of commits) between the given commits
* @see <a href="https://github.com/mdonoughe/jgit-describe/blob/master/src/org/mdonoughe/JGitDescribeTask.java">mdonoughe/jgit-describe/blob/master/src/org/mdonoughe/JGitDescribeTask.java</a>
*/
private int distanceBetween(@NotNull Repository repo, @NotNull RevCommit child, @NotNull RevCommit parent) {
RevWalk revWalk = new RevWalk(repo);

try {
revWalk.markStart(child);

Set<ObjectId> seena = newHashSet();
Set<ObjectId> seenb = newHashSet();
Queue<RevCommit> q = newLinkedList();

q.add(revWalk.parseCommit(child));
int distance = 0;
ObjectId parentId = parent.getId();

while (q.size() > 0) {
RevCommit commit = q.remove();
ObjectId commitId = commit.getId();

if (seena.contains(commitId)) {
continue;
}
seena.add(commitId);

if (parentId.equals(commitId)) {
// don't consider commits that are included in this commit
seeAllParents(revWalk, commit, seenb);
// remove things we shouldn't have included
for (ObjectId oid : seenb) {
if (seena.contains(oid)) {
distance--;
}
}
seena.addAll(seenb);
continue;
}

for (ObjectId oid : commit.getParents()) {
if (!seena.contains(oid)) {
q.add(revWalk.parseCommit(oid));
}
}
distance++;
}

return distance;

} catch (Exception e) {
throw new RuntimeException(String.format("Unable to calculate distance between [%s] and [%s]", child, parent), e);
} finally {
revWalk.dispose();
}
}

private static void seeAllParents(@NotNull RevWalk revWalk, RevCommit child, @NotNull Set<ObjectId> seen) throws IOException {
Queue<RevCommit> q = newLinkedList();
q.add(child);

while (q.size() > 0) {
RevCommit commit = q.remove();
for (ObjectId oid : commit.getParents()) {
if (seen.contains(oid)) {
continue;
}
seen.add(oid);
q.add(revWalk.parseCommit(oid));
}
}
}

// git commit id -> its tag (or tags)
private Map<ObjectId, List<String>> findTagObjectIds(@NotNull Repository repo, boolean tagsFlag) {
Map<ObjectId, List<DatedRevTag>> commitIdsToTags = newHashMap();

RevWalk walk = new RevWalk(repo);
try {
walk.markStart(walk.parseCommit(repo.resolve("HEAD")));

List<Ref> tagRefs = Git.wrap(repo).tagList().call();
String matchPattern = createMatchPattern();
Pattern regex = Pattern.compile(matchPattern);
log("Tag refs [", tagRefs, "]");

for (Ref tagRef : tagRefs) {
walk.reset();
String name = tagRef.getName();
if (!regex.matcher(name).matches()) {
log("Skipping tagRef with name [", name, "] as it doesn't match [", matchPattern, "]");
continue;
}
ObjectId resolvedCommitId = repo.resolve(name);

// todo that's a bit of a hack...
try {
final RevTag revTag = walk.parseTag(resolvedCommitId);
ObjectId taggedCommitId = revTag.getObject().getId();
log("Resolved tag [",revTag.getTagName(),"] [",revTag.getTaggerIdent(),"], points at [",taggedCommitId,"] ");

// sometimes a tag, may point to another tag, so we need to unpack it
while (isTagId(taggedCommitId)) {
taggedCommitId = walk.parseTag(taggedCommitId).getObject().getId();
}

if (commitIdsToTags.containsKey(taggedCommitId)) {
commitIdsToTags.get(taggedCommitId).add(new DatedRevTag(revTag));
} else {
commitIdsToTags.put(taggedCommitId, newArrayList(new DatedRevTag(revTag)));
}

} catch (IncorrectObjectTypeException ex) {
// it's an lightweight tag! (yeah, really)
if (tagsFlag) {
// --tags means "include lightweight tags"
log("Including lightweight tag [", name, "]");

DatedRevTag datedRevTag = new DatedRevTag(resolvedCommitId, name);

if (commitIdsToTags.containsKey(resolvedCommitId)) {
commitIdsToTags.get(resolvedCommitId).add(datedRevTag);
} else {
commitIdsToTags.put(resolvedCommitId, newArrayList(datedRevTag));
}
}
} catch (Exception ignored) {
error("Failed while parsing [",tagRef,"] -- ", Throwables.getStackTraceAsString(ignored));
}
}

for (Map.Entry<ObjectId, List<DatedRevTag>> entry : commitIdsToTags.entrySet()) {
log("key [",entry.getKey(),"], tags => [",entry.getValue(),"] ");
}

Map<ObjectId, List<String>> commitIdsToTagNames = transformRevTagsMapToDateSortedTagNames(commitIdsToTags);

String matchPattern = createMatchPattern();
Map<ObjectId, List<DatedRevTag>> commitIdsToTags = jGitCommon.getCommitIdsToTags(loggerBridge, repo, tagsFlag, matchPattern);
Map<ObjectId, List<String>> commitIdsToTagNames = jGitCommon.transformRevTagsMapToDateSortedTagNames(commitIdsToTags);
log("Created map: [",commitIdsToTagNames,"] ");

return commitIdsToTagNames;
} catch (Exception e) {
log("Unable to locate tags\n[",Throwables.getStackTraceAsString(e),"]");
} finally {
walk.release();
}

return Collections.emptyMap();
}

/** Checks if the given object id resolved to a tag object */
private boolean isTagId(ObjectId objectId) {
return objectId.toString().startsWith("tag ");
}

private HashMap<ObjectId, List<String>> transformRevTagsMapToDateSortedTagNames(Map<ObjectId, List<DatedRevTag>> commitIdsToTags) {
HashMap<ObjectId, List<String>> commitIdsToTagNames = newHashMap();
for (Map.Entry<ObjectId, List<DatedRevTag>> objectIdListEntry : commitIdsToTags.entrySet()) {
List<DatedRevTag> tags = objectIdListEntry.getValue();

List<DatedRevTag> newTags = newArrayList(tags);
Collections.sort(newTags, new Comparator<DatedRevTag>() {
@Override
public int compare(DatedRevTag revTag, DatedRevTag revTag2) {
return revTag2.date.compareTo(revTag.date);
}
});

List<String> tagNames = Lists.transform(newTags, new Function<DatedRevTag, String>() {
@Override
public String apply(DatedRevTag input) {
return trimFullTagName(input.tagName);
}
});

commitIdsToTagNames.put(objectIdListEntry.getKey(), tagNames);
}
return commitIdsToTagNames;
}

private String createMatchPattern() {
Expand All @@ -610,18 +408,8 @@ private String createMatchPattern() {
return buf.toString();
}

@VisibleForTesting
static String trimFullTagName(@NotNull String tagName) {
return tagName.replaceFirst("refs/tags/", "");
}

private void log(Object... parts) {
loggerBridge.log(parts);
}

private void error(Object... parts) {
loggerBridge.error(parts);
}

}

Loading