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;
}