diff --git a/pom.xml b/pom.xml index 00ae706d05..e4194b42ff 100644 --- a/pom.xml +++ b/pom.xml @@ -439,7 +439,7 @@ 1.2.3 - 3.5.3.201412180710-r + 3.5.3.201412180710-r-scm1 1.8.5-scm2 diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java index 7ba8b7106b..471f2450c9 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java @@ -49,6 +49,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.util.IOUtil; +import sonia.scm.web.CollectingPackParserListener; //~--- JDK imports ------------------------------------------------------------ @@ -56,6 +57,7 @@ import java.io.IOException; import java.util.List; + /** * * @author Sebastian Sdorra @@ -83,6 +85,7 @@ public class GitHookChangesetCollector { this.rpack = rpack; this.receiveCommands = receiveCommands; + this.listener = CollectingPackParserListener.get(rpack); } //~--- methods -------------------------------------------------------------- @@ -110,6 +113,16 @@ public class GitHookChangesetCollector for (ReceiveCommand rc : receiveCommands) { + //J- + logger.trace("handle receive command, type={}, ref={}, result={}", + new Object[] { + rc.getType(), + rc.getRefName(), + rc.getResult() + } + ); + //J+ + if (rc.getType() != ReceiveCommand.Type.DELETE) { try @@ -163,16 +176,6 @@ public class GitHookChangesetCollector GitChangesetConverter converter, RevWalk walk, ReceiveCommand rc) throws IncorrectObjectTypeException, IOException { - //J- - logger.trace("handle receive command, type={}, ref={}, result={}", - new Object[] { - rc.getType(), - rc.getRefName(), - rc.getResult() - } - ); - //J+ - ObjectId newId = rc.getNewId(); String branch = GitUtil.getBranch(rc.getRefName()); @@ -201,14 +204,23 @@ public class GitHookChangesetCollector while (commit != null) { - // parse commit body to avoid npe - walk.parseBody(commit); + // only append new commits + if (listener.isNew(commit)) + { - Changeset changeset = converter.createChangeset(commit, branches); + // parse commit body to avoid npe + walk.parseBody(commit); - logger.trace("retrive commit {} for hook", changeset.getId()); + Changeset changeset = converter.createChangeset(commit, branches); - changesets.add(changeset); + logger.trace("retrieve commit {} for hook", changeset.getId()); + + changesets.add(changeset); + } + else + { + logger.trace("commit {} was already received", commit.getId()); + } commit = walk.next(); } @@ -216,6 +228,9 @@ public class GitHookChangesetCollector //~--- fields --------------------------------------------------------------- + /** listener to track new objects */ + private final CollectingPackParserListener listener; + /** Field description */ private final List receiveCommands; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/CollectingPackParserListener.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/CollectingPackParserListener.java new file mode 100644 index 0000000000..fdb96d18d7 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/CollectingPackParserListener.java @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2014, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.web; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectIdSubclassMap; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.transport.BaseReceivePack; +import org.eclipse.jgit.transport.BaseReceivePack.PackParserListener; +import org.eclipse.jgit.transport.PackParser; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Set; + +/** + * Implementation of {@link PackParserListener} to collect every object which is + * pushed with the reveive pack. The listener is used to find out which object + * is new and which was already pushed. + * + * @author Sebastian Sdorra + */ +public class CollectingPackParserListener implements PackParserListener +{ + + /** + * the logger for CollectingPackParserListener + */ + private static final Logger logger = + LoggerFactory.getLogger(CollectingPackParserListener.class); + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns the listener from the receive pack. + * + * + * @param pack receive pack + * + * @return listener + */ + public static CollectingPackParserListener get(BaseReceivePack pack) + { + PackParserListener listener = pack.getPackParserListener(); + + if (listener == null) + { + throw new IllegalArgumentException( + "receive pack does not contain a listener"); + } + + Preconditions.checkArgument( + listener instanceof CollectingPackParserListener, + "listener is not a CollectingPackParserListener"); + + return (CollectingPackParserListener) listener; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Applies the listener to the receive pack. + * + * + * @param pack receive pack + */ + public static void set(BaseReceivePack pack) + { + logger.trace("apply collecting listener to receive pack"); + pack.setPackParserListener(new CollectingPackParserListener()); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Collects all new object ids. + * + * + * @param parser pack parser + */ + @Override + public void after(PackParser parser) + { + logger.trace("retrieve new object ids from pack parser"); + + ObjectIdSubclassMap newObjectIdMap = parser.getNewObjectIds(); + + if (newObjectIdMap != null) + { + newObjectIds = ImmutableSet.copyOf(newObjectIdMap); + } + else + { + logger.warn("pack parser returned no newObjectIds"); + newObjectIds = ImmutableSet.of(); + } + + if (newObjectIds.isEmpty()) + { + logger.debug("new object ids are empty, we treat every commit as new"); + } + else + { + logger.debug("collected {} new object ids", newObjectIds.size()); + } + } + + /** + * Prepares the pack parser to retrieve the new object ids. + * + * + * @param parser pack parser + */ + @Override + public void before(PackParser parser) + { + logger.trace("prepare pack parser to collect new object ids"); + parser.setNeedNewObjectIds(true); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns {@code true} if the object is a new object. The method will also + * return {@code true}, if the pack parser does not return a list with new + * object ids. + * + * + * @param object rev object + * + * @return {@code true} if the object is new + */ + public boolean isNew(RevObject object) + { + ensureAfterWasCalled(); + + return newObjectIds.isEmpty() || newObjectIds.contains(object.getId()); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Throws an {@link IllegalStateException} if the after method was not called. + */ + private void ensureAfterWasCalled() + { + if (newObjectIds == null) + { + throw new IllegalStateException( "Pack parser seem not to be finished. " + + "The receive pack has not called the after method of the listener."); + } + } + + //~--- fields --------------------------------------------------------------- + + /** set of new object ids */ + private Set newObjectIds; +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java index 535f0a2714..25bbe04cfc 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java @@ -96,7 +96,9 @@ public class GitReceivePackFactory rpack.setPreReceiveHook(hook); rpack.setPostReceiveHook(hook); - + // apply collecting listener, to be able to check which commits are new + CollectingPackParserListener.set(rpack); + return rpack; }