merge with branch 1.x

This commit is contained in:
Sebastian Sdorra
2017-01-12 19:50:39 +01:00
250 changed files with 16399 additions and 1573 deletions

View File

@@ -160,6 +160,23 @@ public class GitChangesetConverter implements Closeable
return createChangeset(commit, branches);
}
/**
* Method description
*
*
* @param commit
* @param branch
*
* @return
*
* @throws IOException
*/
public Changeset createChangeset(RevCommit commit, String branch)
throws IOException
{
return createChangeset(commit, Lists.newArrayList(branch));
}
/**
* Method description
*

View File

@@ -35,6 +35,9 @@ package sonia.scm.repository;
//~--- JDK imports ------------------------------------------------------------
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
@@ -42,4 +45,15 @@ import javax.xml.bind.annotation.XmlRootElement;
* @author Sebastian Sdorra
*/
@XmlRootElement(name = "config")
public class GitConfig extends SimpleRepositoryConfig {}
@XmlAccessorType(XmlAccessType.FIELD)
public class GitConfig extends SimpleRepositoryConfig {
@XmlElement(name = "gc-expression")
private String gcExpression;
public String getGcExpression()
{
return gcExpression;
}
}

View File

@@ -0,0 +1,49 @@
/**
* 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.repository;
/**
* Constants for Git.
*
* @author Sebastian Sdorra
* @since 1.50
*/
public final class GitConstants {
/**
* Default branch repository property.
*/
public static final String PROPERTY_DEFAULT_BRANCH = "git.default-branch";
private GitConstants() {
}
}

View File

@@ -0,0 +1,164 @@
/***
* Copyright (c) 2015, 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.
*
* https://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.repository;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import org.eclipse.jgit.api.GarbageCollectCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Executes git gc on every git repository. Statistics of the gc process are logged to the info level. The task is
* disabled by default and must be enabled through the global git configuration.
*
* @author Sebastian Sdorra
* @since 1.47
*/
public class GitGcTask implements Runnable {
private static final String SP = System.getProperty("line.seperator", "\n");
private static final Logger logger = LoggerFactory.getLogger(GitGcTask.class);
private final RepositoryManager repositoryManager;
private final RepositoryDirectoryHandler repositoryHandler;
@Inject
public GitGcTask(RepositoryManager repositoryManager)
{
this.repositoryManager = repositoryManager;
this.repositoryHandler = (RepositoryDirectoryHandler) repositoryManager.getHandler(GitRepositoryHandler.TYPE_NAME);
}
@Override
public void run()
{
for (Repository repository : repositoryManager.getAll())
{
handle(repository);
}
}
private void handle(Repository repository){
if (GitRepositoryHandler.TYPE_NAME.equals(repository.getType()))
{
if (repository.isValid() && repository.isHealthy())
{
logger.info("start git gc for repository {}", repository.getName());
Stopwatch sw = Stopwatch.createStarted();
gc(repository);
logger.debug("gc of repository {} has finished after {}", repository.getName(), sw.stop());
}
else
{
logger.debug("skip non valid/healthy repository {}", repository.getName());
}
}
else
{
logger.trace("skip non git repository {}", repository.getName());
}
}
private void appendProperties(StringBuilder buffer, Properties properties){
for (Map.Entry<Object,Object> entry : properties.entrySet()){
buffer.append(SP).append(" - ").append(entry.getKey()).append(" = ").append(entry.getValue());
}
}
private String message(Repository repository, Properties statistics, String span){
StringBuilder buffer = new StringBuilder("gc statistics for ");
buffer.append(repository.getName()).append(" ").append(span).append(" execution:");
appendProperties(buffer, statistics);
return buffer.toString();
}
private void statistics(Repository repository, GarbageCollectCommand gcc) throws GitAPIException {
Properties properties = gcc.getStatistics();
logger.info(message(repository, properties, "before"));
}
private void execute(Repository repository, GarbageCollectCommand gcc) throws GitAPIException {
Properties properties = gcc.call();
logger.info(message(repository, properties, "after"));
}
private void gc(Repository repository){
File file = repositoryHandler.getDirectory(repository);
Git git = null;
try {
git = open(file);
GarbageCollectCommand gcc = git.gc();
// print statistics before execution, because it looks like
// jgit returns the statistics after gc has finished
statistics(repository, gcc);
execute(repository, gcc);
}
catch (IOException ex)
{
logger.warn("failed to open git repository", ex);
}
catch (GitAPIException ex)
{
logger.warn("failed running git gc command", ex);
}
finally
{
if (git != null){
git.close();
}
}
}
/**
* Opens the git repository. This method is only visible for testing purposes.
*
* @param file repository directory
*
* @return git for repository
*
* @throws IOException
*/
@VisibleForTesting
protected Git open(File file) throws IOException {
return Git.open(file);
}
}

View File

@@ -36,6 +36,7 @@ package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.ObjectId;
@@ -49,12 +50,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.util.IOUtil;
import sonia.scm.web.CollectingPackParserListener;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
*
@@ -72,7 +75,7 @@ public class GitHookChangesetCollector
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
* Constructs a new instance
*
*
* @param rpack
@@ -83,19 +86,19 @@ public class GitHookChangesetCollector
{
this.rpack = rpack;
this.receiveCommands = receiveCommands;
this.listener = CollectingPackParserListener.get(rpack);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
* Collect all new changesets from the received hook.
*
*
* @return
* @return new changesets
*/
public List<Changeset> collectChangesets()
{
List<Changeset> changesets = Lists.newArrayList();
Map<String, Changeset> changesets = Maps.newLinkedHashMap();
org.eclipse.jgit.lib.Repository repository = rpack.getRepository();
@@ -110,7 +113,19 @@ public class GitHookChangesetCollector
for (ReceiveCommand rc : receiveCommands)
{
if (rc.getType() != ReceiveCommand.Type.DELETE)
String ref = rc.getRefName();
logger.trace("handle receive command, type={}, ref={}, result={}", rc.getType(), ref, rc.getResult());
if (rc.getType() == ReceiveCommand.Type.DELETE)
{
logger.debug("skip delete of ref {}", ref);
}
else if (! GitUtil.isBranch(ref))
{
logger.debug("skip ref {}, because it is not a branch", ref);
}
else
{
try
{
@@ -124,13 +139,10 @@ public class GitHookChangesetCollector
builder.append(rc.getType()).append(", ref=");
builder.append(rc.getRefName()).append(", result=");
builder.append(rc.getResult());
logger.error(builder.toString(), ex);
}
}
else
{
logger.debug("skip delete of branch {}", rc.getRefName());
}
}
}
@@ -144,35 +156,13 @@ public class GitHookChangesetCollector
GitUtil.release(walk);
}
return changesets;
return Lists.newArrayList(changesets.values());
}
/**
* Method description
*
*
* @param changesets
* @param converter
* @param walk
* @param rc
*
* @throws IOException
* @throws IncorrectObjectTypeException
*/
private void collectChangesets(List<Changeset> changesets,
private void collectChangesets(Map<String, Changeset> changesets,
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());
@@ -187,7 +177,7 @@ public class GitHookChangesetCollector
ObjectId oldId = rc.getOldId();
if ((oldId != null) &&!oldId.equals(ObjectId.zeroId()))
if ((oldId != null) && !oldId.equals(ObjectId.zeroId()))
{
logger.trace("mark {} as uninteresting for rev walk", oldId.getName());
@@ -196,19 +186,39 @@ public class GitHookChangesetCollector
RevCommit commit = walk.next();
List<String> branches = Lists.newArrayList(branch);
while (commit != null)
{
String id = commit.getId().name();
Changeset changeset = changesets.get(id);
// parse commit body to avoid npe
walk.parseBody(commit);
if (changeset != null)
{
logger.trace(
"commit {} already received durring this push, add branch {} to the commit",
commit, branch);
changeset.getBranches().add(branch);
}
else
{
Changeset changeset = converter.createChangeset(commit, branches);
// only append new commits
if (listener.isNew(commit))
{
logger.trace("retrive commit {} for hook", changeset.getId());
// parse commit body to avoid npe
walk.parseBody(commit);
changesets.add(changeset);
changeset = converter.createChangeset(commit, branch);
logger.trace("retrieve commit {} for hook", changeset.getId());
changesets.put(id, changeset);
}
else
{
logger.trace("commit {} was already received", commit.getId());
}
}
commit = walk.next();
}
@@ -216,9 +226,10 @@ public class GitHookChangesetCollector
//~--- fields ---------------------------------------------------------------
/** Field description */
/** listener to track new objects */
private final CollectingPackParserListener listener;
private final List<ReceiveCommand> receiveCommands;
/** Field description */
private final ReceivePack rpack;
}

View File

@@ -35,6 +35,7 @@ package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -50,6 +51,11 @@ import sonia.scm.repository.spi.GitRepositoryServiceProvider;
import java.io.File;
import java.io.IOException;
import sonia.scm.store.ConfigurationStoreFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContextProvider;
import sonia.scm.schedule.Scheduler;
import sonia.scm.schedule.Task;
/**
*
@@ -66,19 +72,27 @@ public class GitRepositoryHandler
/** Field description */
public static final String RESOURCE_VERSION =
"/sonia/scm/version/scm-git-plugin";
"sonia/scm/version/scm-git-plugin";
/** Field description */
public static final String TYPE_DISPLAYNAME = "Git";
/** Field description */
public static final String TYPE_NAME = "git";
private static final Logger logger = LoggerFactory.getLogger(GitRepositoryHandler.class);
/** Field description */
public static final Type TYPE = new RepositoryType(TYPE_NAME,
TYPE_DISPLAYNAME,
GitRepositoryServiceProvider.COMMANDS);
private static final Object LOCK = new Object();
private final Scheduler scheduler;
private Task task;
//~--- constructors ---------------------------------------------------------
/**
@@ -87,15 +101,48 @@ public class GitRepositoryHandler
*
* @param storeFactory
* @param fileSystem
* @param scheduler
*/
@Inject
public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, FileSystem fileSystem)
public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, FileSystem fileSystem, Scheduler scheduler)
{
super(storeFactory, fileSystem);
this.scheduler = scheduler;
}
//~--- get methods ----------------------------------------------------------
@Override
public void init(SCMContextProvider context)
{
super.init(context);
scheduleGc();
}
@Override
public void setConfig(GitConfig config)
{
super.setConfig(config);
scheduleGc();
}
private void scheduleGc()
{
synchronized (LOCK){
if ( task != null ){
logger.debug("cancel existing git gc task");
task.cancel();
task = null;
}
String exp = getConfig().getGcExpression();
if (!Strings.isNullOrEmpty(exp))
{
logger.info("schedule git gc task with expression {}", exp);
task = scheduler.schedule(exp, GitGcTask.class);
}
}
}
/**
* Method description
*

View File

@@ -0,0 +1,97 @@
/**
* 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.repository;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.EagerSingleton;
import sonia.scm.HandlerEventType;
import sonia.scm.event.ScmEventBus;
import sonia.scm.plugin.Extension;
/**
* Repository listener which handles git related repository events.
*
* @author Sebastian Sdorra
* @since 1.50
*/
@Extension
@EagerSingleton
public class GitRepositoryModifyListener {
/**
* the logger for GitRepositoryModifyListener
*/
private static final Logger logger = LoggerFactory.getLogger(GitRepositoryModifyListener.class);
/**
* Receives {@link RepositoryModificationEvent} and fires a {@link ClearRepositoryCacheEvent} if
* the default branch of a git repository was modified.
*
* @param event repository modification event
*/
@Subscribe
public void handleEvent(RepositoryModificationEvent event){
Repository repository = event.getItem();
if ( isModifyEvent(event) &&
isGitRepository(event.getItem()) &&
hasDefaultBranchChanged(event.getItemBeforeModification(), repository))
{
logger.info("git default branch of repository {} has changed, sending clear cache event", repository.getId());
sendClearRepositoryCacheEvent(repository);
}
}
@VisibleForTesting
protected void sendClearRepositoryCacheEvent(Repository repository) {
ScmEventBus.getInstance().post(new ClearRepositoryCacheEvent(repository));
}
private boolean isModifyEvent(RepositoryEvent event) {
return event.getEventType() == HandlerEventType.MODIFY;
}
private boolean isGitRepository(Repository repository) {
return GitRepositoryHandler.TYPE_NAME.equals(repository.getType());
}
private boolean hasDefaultBranchChanged(Repository old, Repository current) {
return !Objects.equal(
old.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH),
current.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH)
);
}
}

View File

@@ -36,6 +36,7 @@ package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
@@ -262,7 +263,7 @@ public final class GitUtil
{
if (formatter != null)
{
formatter.release();
formatter.close();
}
}
@@ -276,7 +277,7 @@ public final class GitUtil
{
if (walk != null)
{
walk.release();
walk.close();
}
}
@@ -290,7 +291,7 @@ public final class GitUtil
{
if (walk != null)
{
walk.release();
walk.close();;
}
}
@@ -335,6 +336,20 @@ public final class GitUtil
return branch;
}
/**
* Returns {@code true} if the provided reference name is a branch name.
*
* @param refName reference name
*
* @return {@code true} if the name is a branch name
*
* @since 1.50
*/
public static boolean isBranch(String refName)
{
return Strings.nullToEmpty(refName).startsWith(PREFIX_HEADS);
}
/**
* Method description
@@ -631,6 +646,26 @@ public final class GitUtil
return String.format(REMOTE_REF, repository.getId(), branch);
}
/**
* Returns the name of the tag or {@code null} if the the ref is not a tag.
*
* @param refName ref name
*
* @return name of tag or {@link null}
*
* @since 1.50
*/
public static String getTagName(String refName)
{
String tagName = null;
if (refName.startsWith(PREFIX_TAG))
{
tagName = refName.substring(PREFIX_TAG.length());
}
return tagName;
}
/**
* Method description
*

View File

@@ -0,0 +1,120 @@
/**
* 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.repository.api;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceiveCommand.Type;
import sonia.scm.repository.GitUtil;
//~--- JDK imports ------------------------------------------------------------
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Collects created, modified and deleted git branches during a hook.
*
* @author Sebastian Sdorra
*/
public class GitHookBranchProvider implements HookBranchProvider
{
private static final Logger logger = LoggerFactory.getLogger(GitHookBranchProvider.class);
/**
* Constructs a new instance.
*
*
* @param commands received git commands
*/
public GitHookBranchProvider(List<ReceiveCommand> commands)
{
Builder<String> createdOrModifiedBuilder = ImmutableList.builder();
Builder<String> deletedOrClosedBuilder = ImmutableList.builder();
for (ReceiveCommand command : commands)
{
Type type = command.getType();
String ref = command.getRefName();
String branch = GitUtil.getBranch(ref);
if (Strings.isNullOrEmpty(branch))
{
logger.debug("ref {} is not a branch", ref);
}
else if (isCreateOrUpdate(type))
{
createdOrModifiedBuilder.add(branch);
}
else if (command.getType() == Type.DELETE)
{
deletedOrClosedBuilder.add(branch);
}
}
createdOrModified = createdOrModifiedBuilder.build();
deletedOrClosed = deletedOrClosedBuilder.build();
}
private boolean isCreateOrUpdate(Type type){
return type == Type.CREATE || type == Type.UPDATE || type == Type.UPDATE_NONFASTFORWARD;
}
//~--- get methods ----------------------------------------------------------
@Override
public List<String> getCreatedOrModified()
{
return createdOrModified;
}
@Override
public List<String> getDeletedOrClosed()
{
return deletedOrClosed;
}
//~--- fields ---------------------------------------------------------------
private final List<String> createdOrModified;
private final List<String> deletedOrClosed;
}

View File

@@ -0,0 +1,92 @@
/***
* Copyright (c) 2015, 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.
*
* https://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.repository.api;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.GitUtil;
import sonia.scm.repository.Tag;
/**
* Git provider implementation of {@link HookTagProvider}.
*
* @since 1.50
* @author Sebastian Sdorra
*/
public class GitHookTagProvider implements HookTagProvider {
private static final Logger logger = LoggerFactory.getLogger(GitHookTagProvider.class);
private final List<Tag> createdTags;
private final List<Tag> deletedTags;
/**
* Constructs new instance.
*
* @param commands received commands
*/
public GitHookTagProvider(List<ReceiveCommand> commands) {
ImmutableList.Builder<Tag> createdTagBuilder = ImmutableList.builder();
ImmutableList.Builder<Tag> deletedTagBuilder = ImmutableList.builder();
for ( ReceiveCommand rc : commands ){
String refName = rc.getRefName();
String tag = GitUtil.getTagName(refName);
if (Strings.isNullOrEmpty(tag)){
logger.debug("received ref name {} is not a tag", refName);
} else if (rc.getType() == ReceiveCommand.Type.CREATE) {
createdTagBuilder.add(new Tag(tag, GitUtil.getId(rc.getNewId())));
} else if (rc.getType() == ReceiveCommand.Type.DELETE){
deletedTagBuilder.add(new Tag(tag, GitUtil.getId(rc.getOldId())));
}
}
createdTags = createdTagBuilder.build();
deletedTags = deletedTagBuilder.build();
}
@Override
public List<Tag> getCreatedTags() {
return createdTags;
}
@Override
public List<Tag> getDeletedTags() {
return deletedTags;
}
}

View File

@@ -34,11 +34,18 @@ package sonia.scm.repository.spi;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import org.eclipse.jgit.lib.Repository;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.GitConstants;
import sonia.scm.repository.GitUtil;
/**
*
@@ -46,6 +53,11 @@ import java.io.IOException;
*/
public class AbstractGitCommand
{
/**
* the logger for AbstractGitCommand
*/
private static final Logger logger = LoggerFactory.getLogger(AbstractGitCommand.class);
/**
* Constructs ...
@@ -75,6 +87,38 @@ public class AbstractGitCommand
{
return context.open();
}
protected ObjectId getCommitOrDefault(Repository gitRepository, String requestedCommit) throws IOException {
ObjectId commit;
if ( Strings.isNullOrEmpty(requestedCommit) ) {
commit = getDefaultBranch(gitRepository);
} else {
commit = gitRepository.resolve(requestedCommit);
}
return commit;
}
protected ObjectId getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException {
ObjectId head;
if ( Strings.isNullOrEmpty(requestedBranch) ) {
head = getDefaultBranch(gitRepository);
} else {
head = GitUtil.getBranchId(gitRepository, requestedBranch);
}
return head;
}
protected ObjectId getDefaultBranch(Repository gitRepository) throws IOException {
ObjectId head;
String defaultBranchName = repository.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH);
if (!Strings.isNullOrEmpty(defaultBranchName)) {
head = GitUtil.getBranchId(gitRepository, defaultBranchName);
} else {
logger.trace("no default branch configured, use repository head as default");
head = GitUtil.getRepositoryHead(gitRepository);
}
return head;
}
//~--- fields ---------------------------------------------------------------

View File

@@ -138,7 +138,7 @@ public abstract class AbstractGitIncomingOutgoingCommand
GitUtil.fetch(git, handler.getDirectory(remoteRepository), remoteRepository);
ObjectId localId = GitUtil.getRepositoryHead(git.getRepository());
ObjectId localId = getDefaultBranch(git.getRepository());
ObjectId remoteId = null;
Ref remoteBranch = getRemoteBranch(git.getRepository(), localId,

View File

@@ -124,7 +124,7 @@ public class GitBlameCommand extends AbstractGitCommand implements BlameCommand
blame.setFilePath(request.getPath());
ObjectId revId = GitUtil.getRevisionId(gr, request.getRevision());
ObjectId revId = getCommitOrDefault(gr, request.getRevision());
blame.setStartCommit(revId);

View File

@@ -105,7 +105,7 @@ public class GitBranchesCommand extends AbstractGitCommand
if (branchName != null)
{
branch = new Branch(branchName);
branch = new Branch(branchName, GitUtil.getId(ref.getObjectId()));
}
return branch;

View File

@@ -71,7 +71,6 @@ import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import sonia.scm.util.IOUtil;
/**
@@ -96,8 +95,6 @@ public class GitBrowseCommand extends AbstractGitCommand
/**
* Constructs ...
*
*
*
* @param context
* @param repository
*/
@@ -124,18 +121,15 @@ public class GitBrowseCommand extends AbstractGitCommand
public BrowserResult getBrowserResult(BrowseCommandRequest request)
throws IOException, RepositoryException
{
if (logger.isDebugEnabled())
{
logger.debug("try to create browse result for {}", request);
}
logger.debug("try to create browse result for {}", request);
BrowserResult result = null;
BrowserResult result;
org.eclipse.jgit.lib.Repository repo = open();
ObjectId revId = null;
ObjectId revId;
if (Util.isEmpty(request.getRevision()))
{
revId = GitUtil.getRepositoryHead(repo);
revId = getDefaultBranch(repo);
}
else
{
@@ -169,70 +163,6 @@ public class GitBrowseCommand extends AbstractGitCommand
/**
* Method description
*
*
* @param files
* @param repo
* @param revId
* @param path
*
* @throws IOException
* @throws RepositoryException
*/
private void appendSubModules(List<FileObject> files,
org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path)
throws IOException, RepositoryException
{
path = Util.nonNull(path);
Map<String, SubRepository> subRepositories = subrepositoryCache.get(revId);
if (subRepositories == null)
{
subRepositories = getSubRepositories(repo, revId);
subrepositoryCache.put(revId, subRepositories);
}
if (subRepositories != null)
{
for (Entry<String, SubRepository> e : subRepositories.entrySet())
{
String p = e.getKey();
if (p.startsWith(path))
{
p = p.substring(path.length());
if (p.startsWith("/"))
{
p = p.substring(1);
}
if (p.endsWith("/"))
{
p = p.substring(0, p.length() - 1);
}
if (!p.contains("/"))
{
FileObject fo = new FileObject();
fo.setDirectory(true);
fo.setPath(path);
fo.setName(p);
fo.setSubRepository(e.getValue());
files.add(fo);
}
}
}
}
}
/**
* Method description
*
*
*
*
* @param repo
* @param request
* @param revId
@@ -244,9 +174,9 @@ public class GitBrowseCommand extends AbstractGitCommand
*/
private FileObject createFileObject(org.eclipse.jgit.lib.Repository repo,
BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk)
throws IOException
throws IOException, RepositoryException
{
FileObject file = null;
FileObject file;
try
{
@@ -257,26 +187,43 @@ public class GitBrowseCommand extends AbstractGitCommand
file.setName(treeWalk.getNameString());
file.setPath(path);
ObjectLoader loader = repo.open(treeWalk.getObjectId(0));
SubRepository sub = null;
file.setDirectory(loader.getType() == Constants.OBJ_TREE);
file.setLength(loader.getSize());
// don't show message and date for directories to improve performance
if (!file.isDirectory() &&!request.isDisableLastCommit())
if (!request.isDisableSubRepositoryDetection())
{
logger.trace("fetch last commit for {} at {}", path, revId.getName());
sub = getSubRepository(repo, revId, path);
}
RevCommit commit = getLatestCommit(repo, revId, path);
if (sub != null)
{
logger.trace("{} seems to be a sub repository", path);
file.setDirectory(true);
file.setSubRepository(sub);
}
else
{
ObjectLoader loader = repo.open(treeWalk.getObjectId(0));
if (commit != null)
file.setDirectory(loader.getType() == Constants.OBJ_TREE);
file.setLength(loader.getSize());
// don't show message and date for directories to improve performance
if (!file.isDirectory() &&!request.isDisableLastCommit())
{
file.setLastModified(GitUtil.getCommitTime(commit));
file.setDescription(commit.getShortMessage());
}
else if (logger.isWarnEnabled())
{
logger.warn("could not find latest commit for {} on {}", path, revId);
logger.trace("fetch last commit for {} at {}", path, revId.getName());
RevCommit commit = getLatestCommit(repo, revId, path);
if (commit != null)
{
file.setLastModified(GitUtil.getCommitTime(commit));
file.setDescription(commit.getShortMessage());
}
else if (logger.isWarnEnabled())
{
logger.warn("could not find latest commit for {} on {}", path,
revId);
}
}
}
}
@@ -386,11 +333,6 @@ public class GitBrowseCommand extends AbstractGitCommand
String path = request.getPath();
if (!request.isDisableSubRepositoryDetection())
{
appendSubModules(files, repo, revId, path);
}
if (Util.isEmpty(path))
{
while (treeWalk.next())
@@ -496,6 +438,28 @@ public class GitBrowseCommand extends AbstractGitCommand
return subRepositories;
}
private SubRepository getSubRepository(org.eclipse.jgit.lib.Repository repo,
ObjectId revId, String path)
throws IOException, RepositoryException
{
Map<String, SubRepository> subRepositories = subrepositoryCache.get(revId);
if (subRepositories == null)
{
subRepositories = getSubRepositories(repo, revId);
subrepositoryCache.put(revId, subRepositories);
}
SubRepository sub = null;
if (subRepositories != null)
{
sub = subRepositories.get(path);
}
return sub;
}
//~--- fields ---------------------------------------------------------------
/** Field description */

View File

@@ -34,6 +34,7 @@ package sonia.scm.repository.spi;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -78,7 +79,6 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand
*
* @param context
* @param repository
* @param repositoryDirectory
*/
public GitCatCommand(GitContext context,
sonia.scm.repository.Repository repository)
@@ -102,17 +102,11 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand
public void getCatResult(CatCommandRequest request, OutputStream output)
throws IOException, RepositoryException
{
if (logger.isDebugEnabled())
{
logger.debug("try to read content for {}", request);
}
org.eclipse.jgit.lib.Repository repo = null;
repo = open();
ObjectId revId = GitUtil.getRevisionId(repo, request.getRevision());
logger.debug("try to read content for {}", request);
org.eclipse.jgit.lib.Repository repo = open();
ObjectId revId = getCommitOrDefault(repo, request.getRevision());
getContent(repo, revId, request.getPath(), output);
}

View File

@@ -79,7 +79,6 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand
*
* @param context
* @param repository
* @param repositoryDirectory
*/
public GitDiffCommand(GitContext context, Repository repository)
{

View File

@@ -36,7 +36,9 @@ package sonia.scm.repository.spi;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceivePack;
import sonia.scm.repository.api.GitHookBranchProvider;
import sonia.scm.repository.api.GitHookMessageProvider;
import sonia.scm.repository.api.HookBranchProvider;
import sonia.scm.repository.api.HookFeature;
import sonia.scm.repository.api.HookMessageProvider;
@@ -45,6 +47,8 @@ import sonia.scm.repository.api.HookMessageProvider;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import sonia.scm.repository.api.GitHookTagProvider;
import sonia.scm.repository.api.HookTagProvider;
/**
*
@@ -55,33 +59,28 @@ public class GitHookContextProvider extends HookContextProvider
/** Field description */
private static final Set<HookFeature> SUPPORTED_FEATURES =
EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER);
EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER,
HookFeature.BRANCH_PROVIDER, HookFeature.TAG_PROVIDER);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
* Constructs a new instance
*
*
* @param receivePack
* @param receiveCommands
* @param receivePack git receive pack
* @param receiveCommands received commands
*/
public GitHookContextProvider(ReceivePack receivePack,
List<ReceiveCommand> receiveCommands)
{
this.receivePack = receivePack;
this.receiveCommands = receiveCommands;
this.changesetProvider = new GitHookChangesetProvider(receivePack,
receiveCommands);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public HookMessageProvider createMessageProvider()
{
@@ -90,24 +89,23 @@ public class GitHookContextProvider extends HookContextProvider
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public HookBranchProvider getBranchProvider()
{
return new GitHookBranchProvider(receiveCommands);
}
@Override
public HookTagProvider getTagProvider() {
return new GitHookTagProvider(receiveCommands);
}
@Override
public HookChangesetProvider getChangesetProvider()
{
return changesetProvider;
}
/**
* Method description
*
*
* @return
*/
@Override
public Set<HookFeature> getSupportedFeatures()
{
@@ -117,8 +115,11 @@ public class GitHookContextProvider extends HookContextProvider
//~--- fields ---------------------------------------------------------------
/** Field description */
private GitHookChangesetProvider changesetProvider;
private final GitHookChangesetProvider changesetProvider;
/** Field description */
private ReceivePack receivePack;
private final List<ReceiveCommand> receiveCommands;
/** Field description */
private final ReceivePack receivePack;
}

View File

@@ -221,17 +221,8 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
AndTreeFilter.create(
PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF));
}
ObjectId head = null;
if (!Strings.isNullOrEmpty(request.getBranch()))
{
head = GitUtil.getBranchId(gr, request.getBranch());
}
else
{
head = GitUtil.getRepositoryHead(gr);
}
ObjectId head = getBranchOrDefault(gr, request.getBranch());
if (head != null)
{

View File

@@ -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<ObjectId> 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<ObjectId> newObjectIds;
}

View File

@@ -35,27 +35,18 @@ package sonia.scm.web;
import com.google.inject.Inject;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
import sonia.scm.ClientMessages;
import sonia.scm.Priority;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.filter.Filters;
import sonia.scm.filter.WebElement;
import sonia.scm.repository.GitUtil;
import sonia.scm.web.filter.AuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* Handles git specific basic authentication.
*
* @author Sebastian Sdorra
*/
@Priority(Filters.PRIORITY_AUTHENTICATION)
@@ -64,11 +55,10 @@ public class GitBasicAuthenticationFilter extends AuthenticationFilter
{
/**
* Constructs ...
* Constructs a new instance.
*
*
* @param configuration
* @param webTokenGenerators
* @param configuration scm-manager main configuration
* @param webTokenGenerators web token generators
*/
@Inject
public GitBasicAuthenticationFilter(ScmConfiguration configuration,
@@ -76,32 +66,4 @@ public class GitBasicAuthenticationFilter extends AuthenticationFilter
{
super(configuration, webTokenGenerators);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
*
* @throws IOException
*/
@Override
protected void sendFailedAuthenticationError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
if (GitUtil.isGitClient(request))
{
GitSmartHttpTools.sendError(request, response,
HttpServletResponse.SC_FORBIDDEN,
ClientMessages.get(request).failedAuthentication());
}
else
{
super.sendFailedAuthenticationError(request, response);
}
}
}

View File

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

View File

@@ -0,0 +1,97 @@
/**
* 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.web;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import sonia.scm.plugin.Extension;
/**
*
* @author Sebastian Sdorra <sebastian.sdorra@gmail.com>
* @since 1.45
*/
@Extension
public class GitUserAgentProvider implements UserAgentProvider
{
/** Field description */
@VisibleForTesting
static final UserAgent GIT = UserAgent.builder("Git").browser(
false).basicAuthenticationCharset(
Charsets.UTF_8).build();
/** Field description */
@VisibleForTesting
static final UserAgent MSYSGIT = UserAgent.builder("msysGit").browser(
false).basicAuthenticationCharset(
Charsets.UTF_8).build();
/** Field description */
private static final String PREFIX = "git/";
/** Field description */
private static final String SUFFIX = "msysgit";
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param userAgentString
*
* @return
*/
@Override
public UserAgent parseUserAgent(String userAgentString)
{
UserAgent ua = null;
if (userAgentString.startsWith(PREFIX))
{
if (userAgentString.contains(SUFFIX))
{
ua = MSYSGIT;
}
else
{
ua = GIT;
}
}
return ua;
}
}

View File

@@ -54,6 +54,7 @@ import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sonia.scm.repository.RepositoryException;
/**
*
@@ -170,7 +171,11 @@ public class ScmGitServlet extends GitServlet
{
repositoryViewer.handleRequest(request, response, scmRepository);
}
catch (Exception ex)
catch (RepositoryException ex)
{
throw new ServletException("could not create repository view", ex);
}
catch (IOException ex)
{
throw new ServletException("could not create repository view", ex);
}
@@ -184,11 +189,11 @@ public class ScmGitServlet extends GitServlet
//~--- fields ---------------------------------------------------------------
/** Field description */
private RepositoryProvider repositoryProvider;
private final RepositoryProvider repositoryProvider;
/** Field description */
private RepositoryRequestListenerUtil repositoryRequestListenerUtil;
private final RepositoryRequestListenerUtil repositoryRequestListenerUtil;
/** Field description */
private GitRepositoryViewer repositoryViewer;
private final GitRepositoryViewer repositoryViewer;
}