From 7ee11ec89e0f6cea6dc56260fca6703208203d54 Mon Sep 17 00:00:00 2001
From: Konrad 'ktoso' Malawski describe
command.
- *
- *
*
- * @author Konrad 'ktoso' Malawski
+ * @author Konrad Malawski
*/
public class DescribeCommand extends GitCommand
- * usage: git describe [options]
--always
- *
+ *
* Show uniquely abbreviated commit object as fallback.
- *
+ *
* true
by default.
*/
@NotNull
@@ -167,13 +150,13 @@ public DescribeCommand always(boolean always) {
/**
* --long
- *
+ *
* Always output the long format (the tag, the number of commits and the abbreviated commit name)
* even when it matches a tag. This is useful when you want to see parts of the commit object name
* in "describe" output, even when the commit in question happens to be a tagged version. Instead
* of just emitting the tag name, it will describe such a commit as v1.2-0-gdeadbee (0th commit
* since tag v1.2 that points at object deadbee....).
- *
+ *
* false
by default.
*/
@NotNull
@@ -187,11 +170,11 @@ public DescribeCommand forceLongFormat(@Nullable Boolean forceLongFormat) {
/**
* --abbrev=N
- *
+ *
* Instead of using the default 7 hexadecimal digits as the abbreviated object name,
* use N digits, or as many digits as needed to form a unique object name.
- *
- * An
Searching for lightweight tags is false by default.
- * + * * Example: ** b6a73ed - (HEAD, master) * d37a598 - (v1.0-fixed-stuff) - a lightweight tag (with no message) * 9597545 - (v1.0) - an annotated tag * - * > git describe + * $ git describe * annotated-tag-2-gb6a73ed # the nearest "annotated" tag is found * - * > git describe --tags + * $ git describe --tags * lightweight-tag-1-gb6a73ed # the nearest tag (including lightweights) is found *- * + * *
* Using only annotated tags to mark builds may be useful if you're using tags to help yourself with annotating * things like "i'll get back to that" etc - you don't need such tags to be exposed. But if you want lightweight @@ -249,7 +232,7 @@ public DescribeCommand tags() { } /** - * Apply all configuration options passed in with {@param config}. + * Apply all configuration options passed in with `config`. * If a setting is null, it will not be applied - so for abbrev for example, the default 7 would be used. * * @return itself, after applying the settings @@ -342,7 +325,7 @@ public DescribeResult call() throws GitAPIException { * Prepares the final result of this command. * It tries to put as much information as possible into the result, * and will fallback to a plain commit hash if nothing better is returnable. - *
+ * * The exact logic is following whatgit-describewould do. */ private DescribeResult createDescribeResult(ObjectReader objectReader, ObjectId headCommitId, boolean dirty, @Nullable Pair
git describe
command.
- *
+ *
* See {@link pl.project13.jgit.DescribeResult#toString()} for a detailed information how this result looks like.
*/
public class DescribeResult {
@@ -115,7 +115,7 @@ public DescribeResult withCommitIdAbbrev(int n) {
* | |--------------- the number of commits away from the found tag. So "2414721" is 14 commits ahead of "v1.0.4", in this example.
* |-------------------- the "nearest" tag, to the mentioned commit.
*
- *
+ *
* Other outputs may look like:
* * v1.0.4 -- if the repository is "on a tag" @@ -123,7 +123,7 @@ public DescribeResult withCommitIdAbbrev(int n) { * 2414721 -- a plain commit id hash if not tags were defined (of determined "near" this commit). * It does NOT include the "g" prefix, that is used in the "full" describe output format! *- * + * * For more details (on when what output will be returned etc), see
man git-describe
.
* In general, you can assume it's a "best effort" approach, to give you as much info about the repo state as possible.
*
@@ -170,7 +170,7 @@ public String dirtyMarker() {
* This is following git's behaviour - so any git tooling should be happy with this output.
*
*
- * Notes about the abbriverated object id:
+ * Notes about the abbriverated object id:
* Git will try to use your given abbrev lenght, but when it's to short to guarantee uniqueness -
* a longer one will be used (which WILL guarantee uniqueness).
* If you need the full commit id, it's always available via {@link pl.project13.jgit.DescribeResult#commitObjectId()}.
diff --git a/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java b/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java
index 882330f3..b7787690 100644
--- a/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java
+++ b/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java
@@ -70,6 +70,7 @@ public class GitCommitIdMojo extends AbstractMojo {
public static final String COMMIT_MESSAGE_SHORT = "commit.message.short";
public static final String COMMIT_TIME = "commit.time";
public static final String REMOTE_ORIGIN_URL = "remote.origin.url";
+ public static final String TAGS = "tags";
/**
* The maven project.
@@ -92,7 +93,7 @@ public class GitCommitIdMojo extends AbstractMojo {
/**
* Tell git-commit-id to inject the git properties into all
* reactor projects not just the current one.
- *
git-describecommand. * You can modify the dirty marker, abbrev length and other options here. - * + * * If not specified, default values will be used. * * @parameter @@ -168,12 +169,12 @@ public class GitCommitIdMojo extends AbstractMojo { * Configure the "git.commit.id.abbrev" property to be at least of length N. * N must be in the range of 2 to 40 (inclusive), other values will result in an Exception. * - * + * *
* An Abbreviated commit is a shorter version of the commit id, it is guaranteed to be unique though. * To keep this contract, the plugin may decide to print an abbrev version that is longer than the value specified here. *
- * + * * Example: ** You have a very big repository, yet you set this value to 2. It's very probable that you'll end up getting a 4 or 7 char @@ -224,10 +225,10 @@ public class GitCommitIdMojo extends AbstractMojo { * By default the plugin will fail the build if unable to obtain enough data for a complete run, * if you don't care about this - for example it's not needed during your CI builds and the CI server does weird * things to the repository, you may want to set this value to false. - *
+ * * Setting this value to `false`, causes the plugin to gracefully tell you "I did my best" and abort it's execution * if unable to obtain git meta data - yet the build will continue to run (without failing). - * + * * See https://github.com/ktoso/maven-git-commit-id-plugin/issues/63 for a rationale behing this flag. * * @parameter default-value="true" @@ -304,7 +305,7 @@ public void execute() throws MojoExecutionException { log("dotGitDirectory is null, aborting execution!"); return; } - + try { properties = initProperties(); @@ -332,8 +333,9 @@ public void execute() throws MojoExecutionException { } private void filterNot(Properties properties, @Nullable Listgit-describeto identify your build state, * so think twice before disabeling it. * @@ -113,7 +113,7 @@ public class GitDescribeConfig { * * *
Searching for lightweight tags is false by default.
- * + * * * Example: *@@ -140,13 +140,13 @@ public class GitDescribeConfig { /** *--long- * + * * Always output the long format (the tag, the number of commits and the abbreviated commit name) * even when it matches a tag. This is useful when you want to see parts of the commit object name * in "describe" output, even when the commit in question happens to be a tagged version. Instead * of just emitting the tag name, it will describe such a commit as v1.2-0-gdeadbee (0th commit * since tag v1.2 that points at object deadbee....). - * + * *falseby default. */ private boolean forceLongFormat; diff --git a/src/main/java/pl/project13/maven/git/GitRepositoryState.java b/src/main/java/pl/project13/maven/git/GitRepositoryState.java index e7b6d676..f981ad41 100644 --- a/src/main/java/pl/project13/maven/git/GitRepositoryState.java +++ b/src/main/java/pl/project13/maven/git/GitRepositoryState.java @@ -19,8 +19,12 @@ //import org.codehaus.jackson.annotate.JsonWriteNullProperties; +import com.google.common.base.Joiner; import org.jetbrains.annotations.NotNull; +import java.util.List; +import java.util.Set; + /** * A spring controlled bean that will be injected * with properties about the repository state at build time. @@ -42,6 +46,7 @@ public class GitRepositoryState { String commitMessageFull; // =${git.commit.message.full} String commitMessageShort; // =${git.commit.message.short} String commitTime; // =${git.commit.time} + Settags; // =${git.tags} String mavenProjectVersion; // =${maven.project.version} @@ -144,6 +149,14 @@ public void setMavenProjectVersion(String mavenProjectVersion) { this.mavenProjectVersion = mavenProjectVersion; } + public Set getTags() { + return tags; + } + + public void setTags(Set tags) { + this.tags = tags; + } + /** * If you need it as json but don't have jackson installed etc * @@ -165,6 +178,8 @@ public String toJson() { appendProperty(sb, "buildUserName", buildUserName); appendProperty(sb, "buildUserEmail", buildUserEmail); + appendProperty(sb, "tags", Joiner.on(",").join(tags)); + appendProperty(sb, "mavenProjectVersion", mavenProjectVersion); return sb.append("}").toString(); diff --git a/src/main/java/pl/project13/maven/git/JGitProvider.java b/src/main/java/pl/project13/maven/git/JGitProvider.java index 00363e3d..37a4a086 100644 --- a/src/main/java/pl/project13/maven/git/JGitProvider.java +++ b/src/main/java/pl/project13/maven/git/JGitProvider.java @@ -1,41 +1,32 @@ package pl.project13.maven.git; -import com.fasterxml.jackson.databind.ObjectMapper; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.base.Joiner; import com.google.common.base.Predicate; -import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; import com.google.common.collect.Lists; -import com.google.common.io.Closeables; -import com.google.common.io.Files; -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.project.MavenProject; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.ListTagCommand; import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.lib.AbbreviatedObjectId; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.*; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import pl.project13.jgit.DescribeCommand; import pl.project13.jgit.DescribeResult; import pl.project13.maven.git.log.LoggerBridge; -import pl.project13.maven.git.log.MavenLoggerBridge; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.*; /** -* -* @author Konrad 'ktoso' Malawski -*/ + * @author Konrad 'ktoso' Malawski + */ public class JGitProvider extends GitDataProvider { private File dotGitDirectory; @@ -49,7 +40,7 @@ public static JGitProvider on(@NotNull File dotGitDirectory) { return new JGitProvider(dotGitDirectory); } - JGitProvider(@NotNull File dotGitDirectory){ + JGitProvider(@NotNull File dotGitDirectory) { this.dotGitDirectory = dotGitDirectory; } @@ -81,32 +72,32 @@ public JGitProvider setDateFormat(String dateFormat) { return this; } - public JGitProvider setGitDescribe(GitDescribeConfig gitDescribe){ + public JGitProvider setGitDescribe(GitDescribeConfig gitDescribe) { super.gitDescribe = gitDescribe; return this; } @Override - protected void init() throws MojoExecutionException{ + protected void init() throws MojoExecutionException { git = getGitRepository(); objectReader = git.newObjectReader(); } @Override - protected String getBuildAuthorName(){ + protected String getBuildAuthorName() { String userName = git.getConfig().getString("user", null, "name"); return userName; } @Override - protected String getBuildAuthorEmail(){ + protected String getBuildAuthorEmail() { String userEmail = git.getConfig().getString("user", null, "email"); return userEmail; } @Override - protected void prepareGitToExtractMoreDetailedReproInformation() throws MojoExecutionException{ - try{ + protected void prepareGitToExtractMoreDetailedReproInformation() throws MojoExecutionException { + try { // more details parsed out bellow Ref HEAD = git.getRef(Constants.HEAD); if (HEAD == null) { @@ -115,61 +106,61 @@ protected void prepareGitToExtractMoreDetailedReproInformation() throws MojoExec revWalk = new RevWalk(git); headCommit = revWalk.parseCommit(HEAD.getObjectId()); revWalk.markStart(headCommit); - }catch(Exception e){ + } catch (Exception e) { throw new MojoExecutionException("Error", e); } } @Override - protected String getBranchName() throws IOException{ + protected String getBranchName() throws IOException { String branch = git.getBranch(); return branch; } @Override - protected String getGitDescribe() throws MojoExecutionException{ + protected String getGitDescribe() throws MojoExecutionException { String gitDescribe = getGitDescribe(git); return gitDescribe; } @Override - protected String getCommitId(){ + protected String getCommitId() { String commitId = headCommit.getName(); return commitId; } @Override - protected String getAbbrevCommitId() throws MojoExecutionException{ + protected String getAbbrevCommitId() throws MojoExecutionException { String abbrevCommitId = getAbbrevCommitId(objectReader, headCommit, abbrevLength); return abbrevCommitId; } @Override - protected String getCommitAuthorName(){ + protected String getCommitAuthorName() { String commitAuthor = headCommit.getAuthorIdent().getName(); return commitAuthor; } @Override - protected String getCommitAuthorEmail(){ + protected String getCommitAuthorEmail() { String commitEmail = headCommit.getAuthorIdent().getEmailAddress(); return commitEmail; } @Override - protected String getCommitMessageFull(){ + protected String getCommitMessageFull() { String fullMessage = headCommit.getFullMessage(); return fullMessage; } @Override - protected String getCommitMessageShort(){ + protected String getCommitMessageShort() { String shortMessage = headCommit.getShortMessage(); return shortMessage; } @Override - protected String getCommitTime(){ + protected String getCommitTime() { long timeSinceEpoch = headCommit.getCommitTime(); Date commitDate = new Date(timeSinceEpoch * 1000); // git is "by sec" and java is "by ms" SimpleDateFormat smf = new SimpleDateFormat(dateFormat); @@ -177,26 +168,66 @@ protected String getCommitTime(){ } @Override - protected String getRemoteOriginUrl() throws MojoExecutionException{ + protected String getRemoteOriginUrl() throws MojoExecutionException { String remoteOriginUrl = git.getConfig().getString("remote", "origin", "url"); return remoteOriginUrl; } @Override - protected void finalCleanUp(){ + protected String getTags() throws MojoExecutionException { + RevWalk walk = null; + try { + Repository repo = getGitRepository(); + Git git = Git.wrap(repo); + walk = new RevWalk(repo); + List tagRefs = git.tagList().call(); + + final ObjectId headId = headCommit.toObjectId(); + final RevWalk finalWalk = walk; + Collection tagsForHeadCommit = Collections2.filter(tagRefs, new Predicate() { + @Override public boolean apply(Ref tagRef) { + boolean lightweightTag = tagRef.getObjectId().equals(headId); + + try { + // TODO make this configurable (most users shouldn't really care too much what kind of tag it is though) + return lightweightTag || finalWalk.parseTag(tagRef.getObjectId()).getObject().getId().equals(headId); // or normal tag + } catch (IOException e) { + return false; + } + } + }); + + Collection tags = Collections2.transform(tagsForHeadCommit, new Function() { + @Override public String apply(Ref input) { + return input.getName().replaceAll("refs/tags/", ""); + } + }); + + return Joiner.on(",").join(tags); + } catch (GitAPIException e) { + loggerBridge.error("Unable to extract tags from commit: " + headCommit.getName() + " (" + e.getClass().getName() + ")"); + return ""; + } finally { + if (walk != null) { + walk.dispose(); + } + } + } + + @Override + protected void finalCleanUp() { revWalk.dispose(); } - @VisibleForTesting - String getGitDescribe(@NotNull Repository repository) throws MojoExecutionException { + @VisibleForTesting String getGitDescribe(@NotNull Repository repository) throws MojoExecutionException { try { DescribeResult describeResult = DescribeCommand - .on(repository) - .withLoggerBridge(super.loggerBridge) - .setVerbose(super.verbose) - .apply(super.gitDescribe) - .call(); + .on(repository) + .withLoggerBridge(super.loggerBridge) + .setVerbose(super.verbose) + .apply(super.gitDescribe) + .call(); return describeResult.toString(); } catch (GitAPIException ex) { @@ -211,7 +242,7 @@ private String getAbbrevCommitId(ObjectReader objectReader, RevCommit headCommit return abbreviatedObjectId.name(); } catch (IOException e) { throw new MojoExecutionException("Unable to abbreviate commit id! " + - "You may want to investigate the element in your configuration.", e); + "You may want to investigate the element in your configuration.", e); } } @@ -223,10 +254,10 @@ private Repository getGitRepository() throws MojoExecutionException { FileRepositoryBuilder repositoryBuilder = new FileRepositoryBuilder(); try { repository = repositoryBuilder - .setGitDir(dotGitDirectory) - .readEnvironment() // scan environment GIT_* variables - .findGitDir() // scan up the file system tree - .build(); + .setGitDir(dotGitDirectory) + .readEnvironment() // scan environment GIT_* variables + .findGitDir() // scan up the file system tree + .build(); } catch (IOException e) { throw new MojoExecutionException("Could not initialize repository...", e); } @@ -241,7 +272,7 @@ private Repository getGitRepository() throws MojoExecutionException { // SETTERS FOR TESTS ---------------------------------------------------- @VisibleForTesting - public void setRepository (Repository git){ + public void setRepository(Repository git) { this.git = git; } } diff --git a/src/main/java/pl/project13/maven/git/NativeGitProvider.java b/src/main/java/pl/project13/maven/git/NativeGitProvider.java index af789105..089f494f 100644 --- a/src/main/java/pl/project13/maven/git/NativeGitProvider.java +++ b/src/main/java/pl/project13/maven/git/NativeGitProvider.java @@ -1,11 +1,22 @@ package pl.project13.maven.git; +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Predicate; import com.google.common.base.Splitter; +import com.google.common.collect.Collections2; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import org.apache.maven.plugin.MojoExecutionException; import org.jetbrains.annotations.NotNull; import pl.project13.maven.git.log.LoggerBridge; import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; public class NativeGitProvider extends GitDataProvider { @@ -189,6 +200,37 @@ protected String getCommitTime() { return tryToRunGitCommand(canonical, "log -1 --pretty=format:\"%ci\""); } + @Override + protected String getTags() throws MojoExecutionException { + final String branch = tryToRunGitCommand(canonical, "rev-parse --abbrev-ref HEAD"); + + String out = tryToRunGitCommand(canonical, "log -n 1 --pretty=format:'%d'"); + String[] nms = out + .replaceAll("HEAD", "") + .replaceAll("\\)", "") + .replaceAll("\\(", "") + .replaceAll("'", "") + .replaceAll("tag: ", "") + .replaceAll(",", "") + .trim() + .split(" "); + + + ImmutableList cleanTags = FluentIterable.from(Arrays.asList(nms)). + transform(new Function () { + @Override public String apply(String input) { + return input.trim(); + } + }). + filter(new Predicate () { + @Override public boolean apply(String input) { + return !input.equals(branch); + } + }).toList(); + + return Joiner.on(",").join(cleanTags); + } + @Override protected String getRemoteOriginUrl() throws MojoExecutionException { return getOriginRemote(canonical); diff --git a/src/test/java/pl/project13/maven/git/GitCommitIdMojoIntegrationTest.java b/src/test/java/pl/project13/maven/git/GitCommitIdMojoIntegrationTest.java index a19e16a4..7c0a5f53 100644 --- a/src/test/java/pl/project13/maven/git/GitCommitIdMojoIntegrationTest.java +++ b/src/test/java/pl/project13/maven/git/GitCommitIdMojoIntegrationTest.java @@ -18,12 +18,15 @@ package pl.project13.maven.git; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Splitter; import com.google.common.io.Files; import junitparams.JUnitParamsRunner; import junitparams.Parameters; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.FileUtils; +import org.eclipse.jgit.api.ResetCommand; +import org.eclipse.jgit.lib.Repository; import org.junit.Test; import org.junit.runner.RunWith; import pl.project13.maven.git.FileSystemMavenSandbox.CleanUp; @@ -564,6 +567,38 @@ public void shouldWorkWithNullGitDescribe(boolean useNativeGit) throws Exception assertGitPropertiesPresentInProject(targetProject.getProperties()); } + @Test + @Parameters(method = "useNativeGit") + public void shouldExtractTagsOnGivenCommit(boolean useNativeGit) throws Exception { + // given + mavenSandbox + .withParentProject("my-jar-project", "jar") + .withNoChildProject() + .withGitRepoInParent(AvailableGitTestRepo.WITH_COMMIT_THAT_HAS_TWO_TAGS) + .create(CleanUp.CLEANUP_FIRST); + + git("my-jar-project").reset().setMode(ResetCommand.ResetType.HARD).setRef("d37a598").call(); + + MavenProject targetProject = mavenSandbox.getParentProject(); + setProjectToExecuteMojoIn(targetProject); + + alterMojoSettings("gitDescribe", null); + alterMojoSettings("useNativeGit", useNativeGit); + + // when + mojo.execute(); + + // then + Properties properties = targetProject.getProperties(); + assertGitPropertiesPresentInProject(properties); + + assertThat(properties).satisfies(new ContainsKeyCondition("git.tags")); + assertThat(properties.get("git.tags").toString()).doesNotContain("refs/tags/"); + + assertThat(Splitter.on(",").split(properties.get("git.tags").toString())) + .containsOnly("lightweight-tag", "newest-tag"); + } + private GitDescribeConfig createGitDescribeConfig(boolean forceLongFormat, int abbrev) { GitDescribeConfig gitDescribeConfig = new GitDescribeConfig(); gitDescribeConfig.setTags(true); diff --git a/src/test/java/pl/project13/maven/git/GitIntegrationTest.java b/src/test/java/pl/project13/maven/git/GitIntegrationTest.java index a3f6a0a5..d6753b1e 100644 --- a/src/test/java/pl/project13/maven/git/GitIntegrationTest.java +++ b/src/test/java/pl/project13/maven/git/GitIntegrationTest.java @@ -44,6 +44,10 @@ public void setUp() throws Exception { initializeMojoWithDefaults(mojo); } + protected Git git(String dir) throws IOException, InterruptedException { + return Git.open(dotGitDir(Optional.of(dir))); + } + protected Git git() throws IOException, InterruptedException { return Git.open(dotGitDir(projectDir())); } diff --git a/src/test/java/pl/project13/test/utils/AssertException.java b/src/test/java/pl/project13/test/utils/AssertException.java index 510e5876..9ff3b3a2 100644 --- a/src/test/java/pl/project13/test/utils/AssertException.java +++ b/src/test/java/pl/project13/test/utils/AssertException.java @@ -27,7 +27,7 @@ * Allows expecting and intercepting exceptions in a nice way. * Use it to intercept exceptions in your tests, in a way that allows * sticking to the given/when/then flow, and validate exception throws on - * + * * SoftwareBirr 02.2012 * * @author Konrad Malawski (konrad.malawski@java.pl)