From 4e8d6a7323cadcf725c73bbe75d99145dd8c30d8 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 10 May 2013 15:12:53 +0200 Subject: [PATCH] implement git outgoing command --- .../AbstractGitIncomingOutgoingCommand.java | 315 ++++++++++++++++++ .../repository/spi/GitIncomingCommand.java | 199 ++--------- .../repository/spi/GitOutgoingCommand.java | 130 ++++++++ 3 files changed, 467 insertions(+), 177 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitOutgoingCommand.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java new file mode 100644 index 0000000000..9262166237 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java @@ -0,0 +1,315 @@ +/** + * 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.spi; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.collect.Lists; +import com.google.common.io.Closeables; + +import org.eclipse.jgit.api.FetchCommand; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.RefSpec; + +import sonia.scm.repository.Changeset; +import sonia.scm.repository.ChangesetPagingResult; +import sonia.scm.repository.GitChangesetConverter; +import sonia.scm.repository.GitRepositoryHandler; +import sonia.scm.repository.GitUtil; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryException; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +/** + * + * @author Sebastian Sdorra + */ +public abstract class AbstractGitIncomingOutgoingCommand + extends AbstractGitCommand +{ + + /** Field description */ + private static final String REFSPEC = "+refs/heads/*:refs/remote/scm/%s/*"; + + /** Field description */ + private static final String REMOTE_REF = "refs/remote/scm/%s/%s"; + + /** Field description */ + private static final String REMOTE_REF_PREFIX = "refs/remote/scm/%s/"; + + /** Field description */ + private static final int TIMEOUT = 5; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param handler + * @param context + * @param repository + */ + AbstractGitIncomingOutgoingCommand(GitRepositoryHandler handler, + GitContext context, Repository repository) + { + super(context, repository); + this.handler = handler; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param logCommand + * @param localId + * @param remoteId + * + * @throws IOException + */ + protected abstract void prepareLogCommand( + org.eclipse.jgit.api.LogCommand logCommand, ObjectId localId, + ObjectId remoteId) + throws IOException; + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param request + * + * @return + * + * @throws IOException + * @throws RepositoryException + */ + protected ChangesetPagingResult getIncomingOrOutgoingChangesets( + PagedRemoteCommandRequest request) + throws IOException, RepositoryException + { + Repository remoteRepository = request.getRemoteRepository(); + + Git git = Git.wrap(open()); + FetchCommand fetch = git.fetch(); + + fetch.setRemote(handler.getDirectory(remoteRepository).getAbsolutePath()); + fetch.setRefSpecs(createRefSpec(remoteRepository)); + fetch.setTimeout((int) TimeUnit.MINUTES.toSeconds(TIMEOUT)); + + try + { + fetch.call(); + } + catch (GitAPIException ex) + { + throw new RepositoryException("could not fetch", ex); + } + + ObjectId localId = GitUtil.getRepositoryHead(git.getRepository()); + ObjectId remoteId = null; + + + Ref remoteBranch = getRemoteBranch(git.getRepository(), localId, + remoteRepository); + + if ( remoteBranch != null ){ + remoteId = remoteBranch.getObjectId(); + } + + // TODO paging + List changesets = Lists.newArrayList(); + + if (retrieveChangesets(localId, remoteId)) + { + + GitChangesetConverter converter = null; + RevWalk walk = null; + + try + { + walk = new RevWalk(git.getRepository()); + converter = new GitChangesetConverter(git.getRepository(), walk); + + org.eclipse.jgit.api.LogCommand log = git.log(); + + prepareLogCommand(log, localId, remoteId); + + Iterable commits = log.call(); + + for (RevCommit commit : commits) + { + changesets.add(converter.createChangeset(commit)); + } + + changesets = Lists.reverse(changesets); + } + catch (Exception ex) + { + throw new RepositoryException("could not execute incoming command", ex); + } + finally + { + Closeables.close(converter, true); + GitUtil.release(walk); + } + + } + + return new ChangesetPagingResult(changesets.size(), changesets); + } + + protected abstract boolean retrieveChangesets(ObjectId localId, ObjectId remoteId); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param repository + * + * @return + */ + private RefSpec createRefSpec(Repository repository) + { + return new RefSpec(String.format(REFSPEC, repository.getId())); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param repository + * @param local + * @param remoteRepository + * + * @return + * + * @throws IOException + * @throws RepositoryException + */ + private Ref getRemoteBranch(org.eclipse.jgit.lib.Repository repository, + ObjectId local, Repository remoteRepository) + throws IOException, RepositoryException + { + Ref ref = null; + + if (local != null) + { + Ref localBranch = GitUtil.getRefForCommit(repository, local); + + if (localBranch != null) + { + ref = repository.getRef(getScmRemoteRefName(remoteRepository, + localBranch)); + } + } + else + { + ref = repository.getRef(getScmRemoteRefName(remoteRepository, "master")); + + if (ref == null) + { + String prefix = String.format(REMOTE_REF_PREFIX, + remoteRepository.getId()); + + for (Entry e : repository.getAllRefs().entrySet()) + { + if (e.getKey().startsWith(prefix)) + { + if (ref != null) + { + throw new RepositoryException("could not find remote branch"); + } + + ref = e.getValue(); + + break; + } + } + } + } + + return ref; + } + + /** + * Method description + * + * + * @param repository + * @param localBranch + * + * @return + */ + private String getScmRemoteRefName(Repository repository, Ref localBranch) + { + return getScmRemoteRefName(repository, localBranch.getName()); + } + + /** + * Method description + * + * + * @param repository + * @param localBranch + * + * @return + */ + private String getScmRemoteRefName(Repository repository, String localBranch) + { + return String.format(REMOTE_REF, repository.getId(), localBranch); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private GitRepositoryHandler handler; +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitIncomingCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitIncomingCommand.java index 7d8f9076a5..be7180845a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitIncomingCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitIncomingCommand.java @@ -38,6 +38,7 @@ import com.google.common.io.Closeables; import org.eclipse.jgit.api.FetchCommand; import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.LogCommand; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; @@ -65,24 +66,10 @@ import java.util.concurrent.TimeUnit; * * @author Sebastian Sdorra */ -public class GitIncomingCommand extends AbstractGitCommand +public class GitIncomingCommand extends AbstractGitIncomingOutgoingCommand implements IncomingCommand { - /** Field description */ - private static final String REFSPEC = "+refs/heads/*:refs/remote/scm/%s/*"; - - /** Field description */ - private static final String REMOTE_REF = "refs/remote/scm/%s/%s"; - - /** Field description */ - private static final String REMOTE_REF_PREFIX = "refs/remote/scm/%s/"; - - /** Field description */ - private static final int TIMEOUT = 5; - - //~--- constructors --------------------------------------------------------- - /** * Constructs ... * @@ -94,10 +81,17 @@ public class GitIncomingCommand extends AbstractGitCommand GitIncomingCommand(GitRepositoryHandler handler, GitContext context, Repository repository) { - super(context, repository); - this.handler = handler; + super(handler, context, repository); } + @Override + protected boolean retrieveChangesets(ObjectId localId, ObjectId remoteId) + { + return remoteId != null; + } + + + //~--- get methods ---------------------------------------------------------- /** @@ -116,73 +110,7 @@ public class GitIncomingCommand extends AbstractGitCommand IncomingCommandRequest request) throws IOException, RepositoryException { - Repository remoteRepository = request.getRemoteRepository(); - - Git git = Git.wrap(open()); - FetchCommand fetch = git.fetch(); - - fetch.setRemote(handler.getDirectory(remoteRepository).getAbsolutePath()); - fetch.setRefSpecs(createRefSpec(remoteRepository)); - fetch.setTimeout((int) TimeUnit.MINUTES.toSeconds(TIMEOUT)); - - try - { - fetch.call(); - } - catch (GitAPIException ex) - { - throw new RepositoryException("could not fetch", ex); - } - - ObjectId local = GitUtil.getRepositoryHead(git.getRepository()); - - Ref remoteBranch = getRemoteBranch(git.getRepository(), local, - remoteRepository); - - // TODO paging - List changesets = Lists.newArrayList(); - - if (remoteBranch != null) - { - - GitChangesetConverter converter = null; - RevWalk walk = null; - - try - { - walk = new RevWalk(git.getRepository()); - converter = new GitChangesetConverter(git.getRepository(), walk); - - org.eclipse.jgit.api.LogCommand log = git.log(); - - if (local != null) - { - log.not(local); - } - - Iterable commits = - log.add(remoteBranch.getObjectId()).call(); - - for (RevCommit commit : commits) - { - changesets.add(converter.createChangeset(commit)); - } - - changesets = Lists.reverse(changesets); - } - catch (Exception ex) - { - throw new RepositoryException("could not execute incoming command", ex); - } - finally - { - Closeables.close(converter, true); - GitUtil.release(walk); - } - - } - - return new ChangesetPagingResult(changesets.size(), changesets); + return getIncomingOrOutgoingChangesets(request); } //~--- methods -------------------------------------------------------------- @@ -191,105 +119,22 @@ public class GitIncomingCommand extends AbstractGitCommand * Method description * * - * @param repository - * - * @return - */ - private RefSpec createRefSpec(Repository repository) - { - return new RefSpec(String.format(REFSPEC, repository.getId())); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param repository - * @param local - * @param remoteRepository - * - * @return + * @param logCommand + * @param localId + * @param remoteId * * @throws IOException - * @throws RepositoryException */ - private Ref getRemoteBranch(org.eclipse.jgit.lib.Repository repository, - ObjectId local, Repository remoteRepository) - throws IOException, RepositoryException + @Override + protected void prepareLogCommand(LogCommand logCommand, ObjectId localId, + ObjectId remoteId) + throws IOException { - Ref ref = null; - - if (local != null) + if (localId != null) { - Ref localBranch = GitUtil.getRefForCommit(repository, local); - - if (localBranch != null) - { - ref = repository.getRef(getScmRemoteRefName(remoteRepository, - localBranch)); - } - } - else - { - ref = repository.getRef(getScmRemoteRefName(remoteRepository, "master")); - - if (ref == null) - { - String prefix = String.format(REMOTE_REF_PREFIX, - remoteRepository.getId()); - - for (Entry e : repository.getAllRefs().entrySet()) - { - if (e.getKey().startsWith(prefix)) - { - if (ref != null) - { - throw new RepositoryException("could not find remote branch"); - } - - ref = e.getValue(); - - break; - } - } - } + logCommand.not(localId); } - return ref; + logCommand.add(remoteId); } - - /** - * Method description - * - * - * @param repository - * @param localBranch - * - * @return - */ - private String getScmRemoteRefName(Repository repository, Ref localBranch) - { - return getScmRemoteRefName(repository, localBranch.getName()); - } - - /** - * Method description - * - * - * @param repository - * @param localBranch - * - * @return - */ - private String getScmRemoteRefName(Repository repository, String localBranch) - { - return String.format(REMOTE_REF, repository.getId(), localBranch); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private GitRepositoryHandler handler; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitOutgoingCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitOutgoingCommand.java new file mode 100644 index 0000000000..f8c50e790c --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitOutgoingCommand.java @@ -0,0 +1,130 @@ +/** + * 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.spi; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.eclipse.jgit.api.LogCommand; +import org.eclipse.jgit.lib.ObjectId; + +import sonia.scm.repository.ChangesetPagingResult; +import sonia.scm.repository.GitRepositoryHandler; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryException; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +/** + * + * @author Sebastian Sdorra + */ +public class GitOutgoingCommand extends AbstractGitIncomingOutgoingCommand + implements OutgoingCommand +{ + + /** + * Constructs ... + * + * + * @param handler + * @param context + * @param repository + */ + GitOutgoingCommand(GitRepositoryHandler handler, GitContext context, + Repository repository) + { + super(handler, context, repository); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param request + * + * @return + * + * @throws IOException + * @throws RepositoryException + */ + @Override + public ChangesetPagingResult getOutgoingChangesets( + OutgoingCommandRequest request) + throws IOException, RepositoryException + { + return getIncomingOrOutgoingChangesets(request); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param logCommand + * @param localId + * @param remoteId + * + * @throws IOException + */ + @Override + protected void prepareLogCommand(LogCommand logCommand, ObjectId localId, + ObjectId remoteId) + throws IOException + { + logCommand.add(localId); + + if (remoteId != null) + { + logCommand.not(remoteId); + } + } + + /** + * Method description + * + * + * @param localId + * @param remoteId + * + * @return + */ + @Override + protected boolean retrieveChangesets(ObjectId localId, ObjectId remoteId) + { + return localId != null; + } +}