diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java index 2535b276bb..7277ad55ab 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java @@ -35,6 +35,7 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import org.eclipse.jgit.diff.DiffEntry; @@ -132,7 +133,6 @@ public class GitChangesetConverter implements Closeable * Method description * * - * * @param commit * * @return @@ -140,6 +140,42 @@ public class GitChangesetConverter implements Closeable * @throws IOException */ public Changeset createChangeset(RevCommit commit) throws IOException + { + List branches = Lists.newArrayList(); + Set refs = repository.getAllRefsByPeeledObjectId().get(commit.getId()); + + if (Util.isNotEmpty(refs)) + { + + for (Ref ref : refs) + { + String branch = GitUtil.getBranch(ref); + + if (branch != null) + { + branches.add(branch); + } + } + + } + + return createChangeset(commit, branches); + } + + /** + * Method description + * + * + * + * @param commit + * @param branches + * + * @return + * + * @throws IOException + */ + public Changeset createChangeset(RevCommit commit, List branches) + throws IOException { String id = commit.getId().name(); List parentList = null; @@ -181,22 +217,7 @@ public class GitChangesetConverter implements Closeable changeset.getTags().addAll(tagCollection); } - Set refs = repository.getAllRefsByPeeledObjectId().get(commit.getId()); - - if (Util.isNotEmpty(refs)) - { - - for (Ref ref : refs) - { - String branch = GitUtil.getBranch(ref); - - if (branch != null) - { - changeset.getBranches().add(branch); - } - } - - } + changeset.setBranches(branches); return changeset; } @@ -267,7 +288,8 @@ public class GitChangesetConverter implements Closeable { if (logger.isTraceEnabled()) { - logger.trace("no parent tree at position 0 for commit {}", commit); + logger.trace("no parent tree at position 0 for commit {}", + commit.getName()); } treeWalk.addTree(new EmptyTreeIterator()); @@ -277,7 +299,7 @@ public class GitChangesetConverter implements Closeable { if (logger.isTraceEnabled()) { - logger.trace("no parent available for commit {}", commit); + logger.trace("no parent available for commit {}", commit.getName()); } treeWalk.addTree(new EmptyTreeIterator()); 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 new file mode 100644 index 0000000000..b3b1f97467 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2010, 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.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.collect.Lists; + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevSort; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.eclipse.jgit.transport.ReceivePack; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.util.IOUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; +import java.util.Map; + +/** + * + * @author Sebastian Sdorra + */ +public class GitHookChangesetCollector +{ + + /** + * the logger for GitHookChangesetCollector + */ + private static final Logger logger = + LoggerFactory.getLogger(GitHookChangesetCollector.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param rpack + * @param receiveCommands + */ + public GitHookChangesetCollector(ReceivePack rpack, + List receiveCommands) + { + this.rpack = rpack; + this.receiveCommands = receiveCommands; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public List collectChangesets() + { + List changesets = Lists.newArrayList(); + + org.eclipse.jgit.lib.Repository repository = rpack.getRepository(); + + RevWalk walk = null; + + GitChangesetConverter converter = null; + + try + { + walk = rpack.getRevWalk(); + converter = new GitChangesetConverter(repository, walk); + + for (ReceiveCommand rc : receiveCommands) + { + //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()); + + walk.reset(); + walk.sort(RevSort.TOPO); + walk.sort(RevSort.REVERSE, true); + + if (logger.isTraceEnabled()) + { + logger.trace("mark {} as start for rev walk", newId.getName()); + } + + walk.markStart(walk.parseCommit(newId)); + + ObjectId oldId = rc.getOldId(); + + if ((oldId != null) &&!oldId.equals(ObjectId.zeroId())) + { + if (logger.isTraceEnabled()) + { + logger.trace("mark {} as uninteresting for rev walk", + oldId.getName()); + } + + walk.markUninteresting(walk.parseCommit(oldId)); + } + + for (ObjectId id : getExistingObjects(rpack)) + { + if (logger.isTraceEnabled()) + { + logger.trace("mark {} as uninteresting for rev walk", id.getName()); + } + + walk.markUninteresting(walk.parseCommit(id)); + } + + RevCommit commit = walk.next(); + + List branches = Lists.newArrayList(branch); + + while (commit != null) + { + + // parse commit body to avoid npe + walk.parseBody(commit); + + Changeset changeset = converter.createChangeset(commit, branches); + + if (logger.isTraceEnabled()) + { + logger.trace("retrive commit {} for hook", changeset.getId()); + } + + changesets.add(changeset); + + commit = walk.next(); + } + + } + + } + catch (Exception ex) + { + logger.error("could not collect changesets", ex); + } + finally + { + IOUtil.close(converter); + } + + return changesets; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param rpack + * + * @return + */ + private List getExistingObjects(ReceivePack rpack) + { + List existingObjects = Lists.newArrayList(); + + if (existingObjects == null) + { + Map refs = rpack.getRepository().getAllRefs(); + + for (Ref r : refs.values()) + { + existingObjects.add(r.getObjectId()); + } + } + + return existingObjects; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private List receiveCommands; + + /** Field description */ + private ReceivePack rpack; +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHookEvent.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHookEvent.java index cc4248bbf4..ebcc4fefad 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHookEvent.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHookEvent.java @@ -35,25 +35,11 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevSort; -import org.eclipse.jgit.revwalk.RevWalk; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sonia.scm.util.IOUtil; -import sonia.scm.util.Util; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.eclipse.jgit.transport.ReceivePack; //~--- JDK imports ------------------------------------------------------------ -import java.io.File; -import java.io.IOException; - -import java.util.ArrayList; -import java.util.Collection; import java.util.List; /** @@ -63,29 +49,19 @@ import java.util.List; public class GitRepositoryHookEvent extends AbstractRepositoryHookEvent { - /** the logger for GitRepositoryHookEvent */ - private static final Logger logger = - LoggerFactory.getLogger(GitRepositoryHookEvent.class); - - //~--- constructors --------------------------------------------------------- - /** * Constructs ... * * - * @param directory - * @param ref - * @param newId - * @param oldId + * @param rpack + * @param receiveCommands * @param type */ - public GitRepositoryHookEvent(File directory, Ref ref, ObjectId newId, - ObjectId oldId, RepositoryHookType type) + public GitRepositoryHookEvent(ReceivePack rpack, + List receiveCommands, RepositoryHookType type) { - this.directory = directory; - this.defaultBranch = GitUtil.getBranch(ref); - this.newId = newId; - this.oldId = oldId; + this.rpack = rpack; + this.receiveCommands = receiveCommands; this.type = type; } @@ -98,11 +74,14 @@ public class GitRepositoryHookEvent extends AbstractRepositoryHookEvent * @return */ @Override - public Collection getChangesets() + public List getChangesets() { if (changesets == null) { - changesets = fetchChangesets(); + GitHookChangesetCollector collector = + new GitHookChangesetCollector(rpack, receiveCommands); + + changesets = collector.collectChangesets(); } return changesets; @@ -120,91 +99,16 @@ public class GitRepositoryHookEvent extends AbstractRepositoryHookEvent return type; } - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - private List fetchChangesets() - { - List result = new ArrayList(); - - if (newId != null) - { - GitChangesetConverter converter = null; - RevWalk walk = null; - org.eclipse.jgit.lib.Repository repository = null; - - try - { - repository = GitUtil.open(directory); - converter = new GitChangesetConverter(repository); - walk = new RevWalk(repository); - walk.reset(); - walk.sort(RevSort.NONE); - walk.markStart(walk.parseCommit(newId)); - - if (oldId != null) - { - walk.markUninteresting(walk.parseCommit(oldId)); - } - - RevCommit commit = walk.next(); - - while (commit != null) - { - Changeset changeset = converter.createChangeset(commit); - - if (changeset.getBranches().isEmpty() - && Util.isNotEmpty(defaultBranch)) - { - if (logger.isTraceEnabled()) - { - logger.trace("set branch to current default branch {}", - defaultBranch); - } - - changeset.getBranches().add(defaultBranch); - } - - result.add(changeset); - commit = walk.next(); - } - } - catch (IOException ex) - { - logger.error("could not fetch changesets", ex); - } - finally - { - IOUtil.close(converter); - GitUtil.release(walk); - GitUtil.close(repository); - } - } - - return result; - } - //~--- fields --------------------------------------------------------------- /** Field description */ private List changesets; /** Field description */ - private String defaultBranch; + private List receiveCommands; /** Field description */ - private File directory; - - /** Field description */ - private ObjectId newId; - - /** Field description */ - private ObjectId oldId; + private ReceivePack rpack; /** Field description */ private RepositoryHookType type; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java index 07c6554507..6cd837e6b3 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java @@ -223,14 +223,27 @@ public class GitUtil if (ref != null) { + branch = getBranch(ref.getName()); + } - String name = ref.getName(); + return branch; + } - if (name.startsWith(PREFIX_HEADS)) - { - branch = name.substring(PREFIX_HEADS.length()); - } + /** + * Method description + * + * + * @param name + * + * @return + */ + public static String getBranch(String name) + { + String branch = null; + if (Util.isNotEmpty(name) && name.startsWith(PREFIX_HEADS)) + { + branch = name.substring(PREFIX_HEADS.length()); } return branch; @@ -326,6 +339,26 @@ public class GitUtil return date; } + /** + * Method description + * + * + * @param objectId + * + * @return + */ + public static String getId(ObjectId objectId) + { + String id = Util.EMPTY_STRING; + + if (objectId != null) + { + id = objectId.name(); + } + + return id; + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceiveHook.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceiveHook.java index e41495afe2..2696a03846 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceiveHook.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceiveHook.java @@ -35,7 +35,10 @@ package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.collect.Lists; + import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.PostReceiveHook; import org.eclipse.jgit.transport.PreReceiveHook; import org.eclipse.jgit.transport.ReceiveCommand; @@ -49,9 +52,9 @@ import sonia.scm.io.CommandResult; import sonia.scm.io.SimpleCommand; import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.GitRepositoryHookEvent; +import sonia.scm.repository.GitUtil; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RepositoryUtil; import sonia.scm.util.IOUtil; import sonia.scm.util.Util; @@ -62,6 +65,8 @@ import java.io.File; import java.io.IOException; import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * @@ -148,8 +153,14 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook File repositoryDirectory, File hook, ObjectId oldId, ObjectId newId, String refName) { - final Command cmd = new SimpleCommand(hook.getAbsolutePath(), getId(oldId), - getId(newId), Util.nonNull(refName)); + if (logger.isDebugEnabled()) + { + logger.debug("execute file hook '{}' in directoy '{}'"); + } + + final Command cmd = new SimpleCommand(hook.getAbsolutePath(), + GitUtil.getId(oldId), GitUtil.getId(newId), + Util.nonNull(refName)); // issue-99 cmd.setWorkDirectory(repositoryDirectory); @@ -199,40 +210,83 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook } /** - * Method description, occurred + * Method description + * * * @param rpack * @param rc - * @param directory - * @param oldId * @param newId * @param type */ - private void fireHookEvent(ReceivePack rpack, ReceiveCommand rc, - File directory, ObjectId oldId, ObjectId newId, RepositoryHookType type) + private void handleFileHooks(ReceivePack rpack, ReceiveCommand rc, + RepositoryHookType type) + { + ObjectId newId = rc.getNewId(); + ObjectId oldId = null; + + if (isUpdateCommand(rc)) + { + oldId = rc.getOldId(); + + if (logger.isTraceEnabled()) + { + logger.trace("handle update receive command from commit '{}' to '{}'", + oldId.getName(), newId.getName()); + } + } + else if (logger.isTraceEnabled()) + { + logger.trace("handle receive command for commit '{}'", newId.getName()); + } + + File directory = rpack.getRepository().getDirectory(); + String scriptName = null; + + if (type == RepositoryHookType.POST_RECEIVE) + { + scriptName = FILE_HOOK_POST_RECEIVE; + } + else if (type == RepositoryHookType.PRE_RECEIVE) + { + scriptName = FILE_HOOK_PRE_RECEIVE; + } + + if (scriptName != null) + { + File hookScript = getHookScript(directory, scriptName); + + if (hookScript != null) + { + executeFileHook(rpack, rc, directory, hookScript, oldId, newId, + rc.getRefName()); + } + } + } + + /** + * Method description + * + * + * @param rpack + * @param receiveCommands + * @param type + */ + private void handleReceiveCommands(ReceivePack rpack, + List receiveCommands, RepositoryHookType type) { try { + Repository repository = rpack.getRepository(); String repositoryName = RepositoryUtil.getRepositoryName(handler, - directory); - GitRepositoryHookEvent e = new GitRepositoryHookEvent(directory, - rc.getRef(), newId, oldId, type); + repository.getDirectory()); repositoryManager.fireHookEvent(GitRepositoryHandler.TYPE_NAME, - repositoryName, e); - } - catch (RepositoryNotFoundException ex) - { - logger.error("repository could not be found", ex); + repositoryName, + new GitRepositoryHookEvent(rpack, receiveCommands, type)); } catch (Exception ex) { - if (logger.isWarnEnabled()) - { - logger.warn("execption occurred during hook execution", ex); - } - - sendError(rpack, rc, ex.getMessage()); + logger.error("could not handle receive commands", ex); } } @@ -247,47 +301,41 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook private void onReceive(ReceivePack rpack, Collection receiveCommands, RepositoryHookType type) { + if (logger.isTraceEnabled()) + { + logger.trace("received git hook, type={}", type); + } + + List commands = Lists.newArrayList(); + for (ReceiveCommand rc : receiveCommands) { - if (((RepositoryHookType.PRE_RECEIVE == type) - && (rc.getResult() - == ReceiveCommand.Result.NOT_ATTEMPTED)) || ((RepositoryHookType - .POST_RECEIVE == type) && (rc.getResult() - == ReceiveCommand.Result.OK))) + if (isReceiveable(rc, type)) { - ObjectId newId = rc.getNewId(); - ObjectId oldId = null; - - if (isUpdateCommand(rc)) - { - oldId = rc.getOldId(); - } - - File directory = rpack.getRepository().getDirectory(); - String scriptName = null; - - if (type == RepositoryHookType.POST_RECEIVE) - { - scriptName = FILE_HOOK_POST_RECEIVE; - } - else if (type == RepositoryHookType.PRE_RECEIVE) - { - scriptName = FILE_HOOK_PRE_RECEIVE; - } - - if (scriptName != null) - { - File hookScript = getHookScript(directory, scriptName); - - if (hookScript != null) - { - executeFileHook(rpack, rc, directory, hookScript, oldId, newId, - rc.getRefName()); - } - } - - fireHookEvent(rpack, rc, directory, oldId, newId, type); + commands.add(rc); + handleFileHooks(rpack, rc, type); } + else if (logger.isTraceEnabled()) + { + //J- + logger.trace("skip receive command, type={}, ref={}, result={}", + new Object[] { + rc.getType(), + rc.getRefName(), + rc.getResult() + } + ); + //J+ + } + } + + if (!commands.isEmpty()) + { + handleReceiveCommands(rpack, commands, type); + } + else if (logger.isDebugEnabled()) + { + logger.debug("no receive command found to process"); } } @@ -329,20 +377,19 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook * Method description * * - * @param objectId + * @param rc + * @param type * * @return */ - private String getId(ObjectId objectId) + private boolean isReceiveable(ReceiveCommand rc, RepositoryHookType type) { - String id = Util.EMPTY_STRING; - - if (objectId != null) - { - id = objectId.name(); - } - - return id; + //J- + return ((RepositoryHookType.PRE_RECEIVE == type) && + (rc.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED)) || + ((RepositoryHookType.POST_RECEIVE == type) && + (rc.getResult() == ReceiveCommand.Result.OK)); + //J+ } /**