diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd1413127c..e763de6daf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## Unreleased
+### Added
+- Introduced merge detection for receive hooks ([#1278](https://github.com/scm-manager/scm-manager/pull/1278))
+
## [2.3.1] - 2020-08-04
### Added
- New api to resolve SCM-Manager root url ([#1276](https://github.com/scm-manager/scm-manager/pull/1276))
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java b/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java
index ff49542222..588fc342d2 100644
--- a/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java
+++ b/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository.api;
//~--- non-JDK imports --------------------------------------------------------
@@ -33,18 +33,18 @@ import sonia.scm.repository.Changeset;
import sonia.scm.repository.PreProcessorUtil;
import sonia.scm.repository.Repository;
import sonia.scm.repository.spi.HookContextProvider;
+import sonia.scm.repository.spi.HookMergeDetectionProvider;
/**
* The context for all repository hooks. With the {@link HookContext} class it
* is able to send messages back to the client, retrieve {@link Changeset}s
- * which are added during this push/commit and gives informations about changed
+ * which are added during this push/commit and gives informations about changed
* branches and tags.
*
* @author Sebastian Sdorra
* @since 1.33
*/
-public final class HookContext
-{
+public final class HookContext {
/**
* the logger for HookContext
@@ -52,8 +52,6 @@ public final class HookContext
private static final Logger logger =
LoggerFactory.getLogger(HookContext.class);
- //~--- constructors ---------------------------------------------------------
-
/**
* Constructs ...
*
@@ -62,9 +60,7 @@ public final class HookContext
* @param repository
* @param preProcessorUtil
*/
- HookContext(HookContextProvider provider, Repository repository,
- PreProcessorUtil preProcessorUtil)
- {
+ HookContext(HookContextProvider provider, Repository repository, PreProcessorUtil preProcessorUtil) {
this.provider = provider;
this.repository = repository;
this.preProcessorUtil = preProcessorUtil;
@@ -77,41 +73,33 @@ public final class HookContext
* about changed branches during the current hook.
*
* @return {@link HookBranchProvider}
- *
- * @throws HookFeatureIsNotSupportedException if the feature is not supported
+ *
+ * @throws HookFeatureIsNotSupportedException if the feature is not supported
* by the underlying provider
- *
+ *
* @since 1.45
*/
- public HookBranchProvider getBranchProvider()
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("create branch provider for repository {}",
- repository.getName());
- }
+ public HookBranchProvider getBranchProvider() {
+ logger.debug("create branch provider for repository {}",
+ repository.getName());
return provider.getBranchProvider();
}
-
+
/**
* Returns a {@link HookTagProvider} which is able to return informations
* about changed tags during the current hook.
*
* @return {@link HookTagProvider}
- *
- * @throws HookFeatureIsNotSupportedException if the feature is not supported
+ *
+ * @throws HookFeatureIsNotSupportedException if the feature is not supported
* by the underlying provider
- *
+ *
* @since 1.50
*/
- public HookTagProvider getTagProvider()
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("create tag provider for repository {}",
- repository.getName());
- }
+ public HookTagProvider getTagProvider() {
+ logger.debug("create tag provider for repository {}",
+ repository.getName());
return provider.getTagProvider();
}
@@ -122,25 +110,19 @@ public final class HookContext
*
*
* @return {@link HookChangesetBuilder}
- *
- * @throws HookFeatureIsNotSupportedException if the feature is not supported
+ *
+ * @throws HookFeatureIsNotSupportedException if the feature is not supported
* by the underlying provider
*/
- public HookChangesetBuilder getChangesetProvider()
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("create changeset provider for repository {}",
- repository.getName());
- }
+ public HookChangesetBuilder getChangesetProvider() {
+ logger.debug("create changeset provider for repository {}",
+ repository.getName());
- //J-
return new HookChangesetBuilder(
- repository,
+ repository,
preProcessorUtil,
provider.getChangesetProvider()
);
- //J+
}
/**
@@ -152,21 +134,33 @@ public final class HookContext
*
* @return {@link HookMessageProvider} which is able to send message back to
* the scm client
- *
- * @throws HookFeatureIsNotSupportedException if the feature is not supported
+ *
+ * @throws HookFeatureIsNotSupportedException if the feature is not supported
* by the underlying provider
*/
- public HookMessageProvider getMessageProvider()
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("create message provider for repository {}",
- repository.getName());
- }
+ public HookMessageProvider getMessageProvider() {
+ logger.debug("create message provider for repository {}",
+ repository.getName());
return provider.getMessageProvider();
}
+ /**
+ * Returns a {@link HookMergeDetectionProvider} which is able to check whether two
+ * branches have been merged with the incoming changesets.
+ *
+ * @return {@link HookMergeDetectionProvider} which is able to detect merges.
+ *
+ * @throws HookFeatureIsNotSupportedException if the feature is not supported
+ * by the underlying provider
+ */
+ public HookMergeDetectionProvider getMergeDetectionProvider() {
+ logger.debug("create merge detection provider for repository {}",
+ repository.getName());
+
+ return provider.getMergeDetectionProvider();
+ }
+
/**
* Returns true if the underlying provider support the requested feature.
*
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java b/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java
index 0f0aaf0832..da664b5772 100644
--- a/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java
+++ b/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository.api;
/**
@@ -49,11 +49,18 @@ public enum HookFeature
* @since 1.45
*/
BRANCH_PROVIDER,
-
+
/**
* Hook tag provider
- *
+ *
* @since 1.50
*/
- TAG_PROVIDER;
+ TAG_PROVIDER,
+
+ /**
+ * Provider to detect merges
+ *
+ * @since 2.4.0
+ */
+ MERGE_DETECTION_PROVIDER
}
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java b/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java
index ddb8cb9a9b..f9af110ea5 100644
--- a/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository.spi;
//~--- non-JDK imports --------------------------------------------------------
@@ -50,7 +50,7 @@ public abstract class HookContextProvider
/**
* Return the provider specific {@link HookMessageProvider} or throws a {@link HookFeatureIsNotSupportedException}.
* The method will throw a {@link HookException} if the client is already disconnected.
- *
+ *
* @return provider specific {@link HookMessageProvider}
*/
public final HookMessageProvider getMessageProvider()
@@ -86,31 +86,31 @@ public abstract class HookContextProvider
/**
* Return the provider specific {@link HookBranchProvider} or throws a {@link HookFeatureIsNotSupportedException}.
- *
+ *
* @return provider specific {@link HookBranchProvider}
- *
+ *
* @since 1.45
*/
public HookBranchProvider getBranchProvider()
{
throw new HookFeatureIsNotSupportedException(HookFeature.BRANCH_PROVIDER);
}
-
+
/**
* Return the provider specific {@link HookTagProvider} or throws a {@link HookFeatureIsNotSupportedException}.
- *
+ *
* @return provider specific {@link HookTagProvider}
- *
+ *
* @since 1.50
*/
- public HookTagProvider getTagProvider()
+ public HookTagProvider getTagProvider()
{
throw new HookFeatureIsNotSupportedException(HookFeature.TAG_PROVIDER);
}
/**
* Return the provider specific {@link HookChangesetProvider} or throws a {@link HookFeatureIsNotSupportedException}.
- *
+ *
* @return provider specific {@link HookChangesetProvider}
*/
public HookChangesetProvider getChangesetProvider()
@@ -118,11 +118,21 @@ public abstract class HookContextProvider
throw new HookFeatureIsNotSupportedException(HookFeature.CHANGESET_PROVIDER);
}
+ /**
+ * Return the provider specific {@link HookMergeDetectionProvider} or throws a {@link HookFeatureIsNotSupportedException}.
+ *
+ * @return provider specific {@link HookMergeDetectionProvider}
+ */
+ public HookMergeDetectionProvider getMergeDetectionProvider()
+ {
+ throw new HookFeatureIsNotSupportedException(HookFeature.MERGE_DETECTION_PROVIDER);
+ }
+
//~--- methods --------------------------------------------------------------
/**
* Creates a new provider specific {@link HookMessageProvider} or throws a {@link HookFeatureIsNotSupportedException}.
- *
+ *
* @return provider specific {@link HookChangesetProvider}
*/
protected HookMessageProvider createMessageProvider()
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/HookMergeDetectionProvider.java b/scm-core/src/main/java/sonia/scm/repository/spi/HookMergeDetectionProvider.java
new file mode 100644
index 0000000000..3deacd22c3
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/HookMergeDetectionProvider.java
@@ -0,0 +1,42 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package sonia.scm.repository.spi;
+
+/**
+ * @since 2.4.0
+ */
+public interface HookMergeDetectionProvider {
+
+ /**
+ * Checks whether branch has been merged into target. So this will also return
+ * true, when branch has been deleted with this change.
+ *
+ * @param target The name of the branch to check, whether the other branch has been merged into.
+ * @param branch The name of the branch to check, whether it has been merged into the other branch.
+ * @return true when branch has been merged into target, false
+ * otherwise.
+ */
+ boolean branchesMerged(String target, String branch);
+}
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 dee72ce345..b55aec06bd 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
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
@@ -337,6 +337,16 @@ public final class GitUtil
return Strings.nullToEmpty(refName).startsWith(PREFIX_HEADS);
}
+ public static Ref getBranchIdOrCurrentHead(org.eclipse.jgit.lib.Repository gitRepository, String requestedBranch) throws IOException {
+ if ( Strings.isNullOrEmpty(requestedBranch) ) {
+ logger.trace("no default branch configured, use repository head as default");
+ Optional[ repositoryHeadRef = GitUtil.getRepositoryHeadRef(gitRepository);
+ return repositoryHeadRef.orElse(null);
+ } else {
+ return GitUtil.getBranchId(gitRepository, requestedBranch);
+ }
+ }
+
/**
* Method description
*
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitPostReceiveHookMergeDetectionProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitPostReceiveHookMergeDetectionProvider.java
new file mode 100644
index 0000000000..e8e41ac3a2
--- /dev/null
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitPostReceiveHookMergeDetectionProvider.java
@@ -0,0 +1,52 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package sonia.scm.repository.api;
+
+import org.eclipse.jgit.lib.Repository;
+import sonia.scm.repository.ChangesetPagingResult;
+import sonia.scm.repository.spi.GitLogComputer;
+import sonia.scm.repository.spi.HookMergeDetectionProvider;
+import sonia.scm.repository.spi.LogCommandRequest;
+
+public class GitPostReceiveHookMergeDetectionProvider implements HookMergeDetectionProvider {
+ private final Repository repository;
+ private final String repositoryId;
+
+ public GitPostReceiveHookMergeDetectionProvider(Repository repository, String repositoryId) {
+ this.repository = repository;
+ this.repositoryId = repositoryId;
+ }
+
+ @Override
+ public boolean branchesMerged(String target, String branch) {
+ LogCommandRequest request = new LogCommandRequest();
+ request.setBranch(branch);
+ request.setAncestorChangeset(target);
+ request.setPagingLimit(1);
+
+ ChangesetPagingResult changesets = new GitLogComputer(repositoryId, repository).compute(request);
+ return changesets.getTotal() == 0;
+ }
+}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitPreReceiveHookMergeDetectionProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitPreReceiveHookMergeDetectionProvider.java
new file mode 100644
index 0000000000..3f760bdaaa
--- /dev/null
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitPreReceiveHookMergeDetectionProvider.java
@@ -0,0 +1,63 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package sonia.scm.repository.api;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import sonia.scm.repository.ChangesetPagingResult;
+import sonia.scm.repository.GitUtil;
+import sonia.scm.repository.spi.GitLogComputer;
+import sonia.scm.repository.spi.HookMergeDetectionProvider;
+import sonia.scm.repository.spi.LogCommandRequest;
+
+import java.util.List;
+
+public class GitPreReceiveHookMergeDetectionProvider implements HookMergeDetectionProvider {
+ private final List receiveCommands;
+ private final Repository repository;
+ private final String repositoryId;
+
+ public GitPreReceiveHookMergeDetectionProvider(List receiveCommands, Repository repository, String repositoryId) {
+ this.receiveCommands = receiveCommands;
+ this.repository = repository;
+ this.repositoryId = repositoryId;
+ }
+
+ @Override
+ public boolean branchesMerged(String target, String branch) {
+
+ String sourceToUse = receiveCommands.stream().filter(receiveCommand -> GitUtil.getBranch(receiveCommand.getRef()).equals(branch)).findFirst().map(ReceiveCommand::getNewId).map(AnyObjectId::getName).orElse(branch);
+ String targetToUse = receiveCommands.stream().filter(receiveCommand -> GitUtil.getBranch(receiveCommand.getRef()).equals(target)).findFirst().map(ReceiveCommand::getNewId).map(AnyObjectId::getName).orElse(target);
+
+ LogCommandRequest request = new LogCommandRequest();
+ request.setBranch(sourceToUse);
+ request.setAncestorChangeset(targetToUse);
+ request.setPagingLimit(1);
+
+ ChangesetPagingResult changesets = new GitLogComputer(repositoryId, repository).compute(request);
+ return changesets.getTotal() == 0;
+ }
+}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java
index d52d0a1574..28f0f72b8b 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java
@@ -59,6 +59,7 @@ import static java.util.Optional.empty;
import static java.util.Optional.of;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
+import static sonia.scm.repository.GitUtil.getBranchIdOrCurrentHead;
//~--- JDK imports ------------------------------------------------------------
@@ -123,15 +124,9 @@ class AbstractGitCommand
Ref getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException {
if ( Strings.isNullOrEmpty(requestedBranch) ) {
String defaultBranchName = context.getConfig().getDefaultBranch();
- if (!Strings.isNullOrEmpty(defaultBranchName)) {
- return GitUtil.getBranchId(gitRepository, defaultBranchName);
- } else {
- logger.trace("no default branch configured, use repository head as default");
- Optional][ repositoryHeadRef = GitUtil.getRepositoryHeadRef(gitRepository);
- return repositoryHeadRef.orElse(null);
- }
+ return getBranchIdOrCurrentHead(gitRepository, defaultBranchName);
} else {
- return GitUtil.getBranchId(gitRepository, requestedBranch);
+ return getBranchIdOrCurrentHead(gitRepository, requestedBranch);
}
}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java
index 8e642a18ce..aaa5f09bda 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java
@@ -21,27 +21,28 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository.spi;
//~--- non-JDK imports --------------------------------------------------------
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceivePack;
-
+import sonia.scm.repository.RepositoryHookType;
import sonia.scm.repository.api.GitHookBranchProvider;
import sonia.scm.repository.api.GitHookMessageProvider;
+import sonia.scm.repository.api.GitHookTagProvider;
+import sonia.scm.repository.api.GitPostReceiveHookMergeDetectionProvider;
+import sonia.scm.repository.api.GitPreReceiveHookMergeDetectionProvider;
import sonia.scm.repository.api.HookBranchProvider;
import sonia.scm.repository.api.HookFeature;
import sonia.scm.repository.api.HookMessageProvider;
-
-//~--- JDK imports ------------------------------------------------------------
+import sonia.scm.repository.api.HookTagProvider;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
-import sonia.scm.repository.api.GitHookTagProvider;
-import sonia.scm.repository.api.HookTagProvider;
/**
*
@@ -50,24 +51,37 @@ import sonia.scm.repository.api.HookTagProvider;
public class GitHookContextProvider extends HookContextProvider
{
- /** Field description */
- private static final Set SUPPORTED_FEATURES =
- EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER,
- HookFeature.BRANCH_PROVIDER, HookFeature.TAG_PROVIDER);
+ /**
+ * Field description
+ */
+ private static final Set SUPPORTED_FEATURES = EnumSet.of(
+ HookFeature.MESSAGE_PROVIDER,
+ HookFeature.CHANGESET_PROVIDER,
+ HookFeature.BRANCH_PROVIDER,
+ HookFeature.TAG_PROVIDER,
+ HookFeature.MERGE_DETECTION_PROVIDER
+ );
//~--- constructors ---------------------------------------------------------
/**
* Constructs a new instance
- *
- * @param receivePack git receive pack
+ * @param receivePack git receive pack
* @param receiveCommands received commands
+ * @param type
*/
- public GitHookContextProvider(ReceivePack receivePack,
- List receiveCommands)
- {
+ public GitHookContextProvider(
+ ReceivePack receivePack,
+ List receiveCommands,
+ RepositoryHookType type,
+ Repository repository,
+ String repositoryId
+ ) {
this.receivePack = receivePack;
this.receiveCommands = receiveCommands;
+ this.type = type;
+ this.repository = repository;
+ this.repositoryId = repositoryId;
this.changesetProvider = new GitHookChangesetProvider(receivePack,
receiveCommands);
}
@@ -99,20 +113,25 @@ public class GitHookContextProvider extends HookContextProvider
return changesetProvider;
}
+ @Override
+ public HookMergeDetectionProvider getMergeDetectionProvider() {
+ if (type == RepositoryHookType.POST_RECEIVE) {
+ return new GitPostReceiveHookMergeDetectionProvider(repository, repositoryId);
+ } else {
+ return new GitPreReceiveHookMergeDetectionProvider(receiveCommands, repository, repositoryId);
+ }
+ }
+
@Override
public Set getSupportedFeatures()
{
return SUPPORTED_FEATURES;
}
- //~--- fields ---------------------------------------------------------------
-
- /** Field description */
private final GitHookChangesetProvider changesetProvider;
-
- /** Field description */
private final List receiveCommands;
-
- /** Field description */
private final ReceivePack receivePack;
+ private final RepositoryHookType type;
+ private final Repository repository;
+ private final String repositoryId;
}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java
index da00ba451b..340df65c16 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java
@@ -27,19 +27,12 @@ package sonia.scm.repository.spi;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
-import org.eclipse.jgit.treewalk.filter.PathFilter;
-import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sonia.scm.NotFoundException;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.GitChangesetConverter;
@@ -48,9 +41,6 @@ import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.util.IOUtil;
import java.io.IOException;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
@@ -183,123 +173,14 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
@Override
@SuppressWarnings("unchecked")
public ChangesetPagingResult getChangesets(LogCommandRequest request) {
- if (logger.isDebugEnabled()) {
- logger.debug("fetch changesets for request: {}", request);
- }
-
- ChangesetPagingResult changesets = null;
- GitChangesetConverter converter = null;
- RevWalk revWalk = null;
-
- try (org.eclipse.jgit.lib.Repository repository = open()) {
- if (!repository.getAllRefs().isEmpty()) {
- int counter = 0;
- int start = request.getPagingStart();
-
- if (start < 0) {
- if (logger.isErrorEnabled()) {
- logger.error("start parameter is negative, reset to 0");
- }
-
- start = 0;
- }
-
- List changesetList = Lists.newArrayList();
- int limit = request.getPagingLimit();
- ObjectId startId = null;
-
- if (!Strings.isNullOrEmpty(request.getStartChangeset())) {
- startId = repository.resolve(request.getStartChangeset());
- }
-
- ObjectId endId = null;
-
- if (!Strings.isNullOrEmpty(request.getEndChangeset())) {
- endId = repository.resolve(request.getEndChangeset());
- }
-
- Ref branch = getBranchOrDefault(repository,request.getBranch());
-
- ObjectId ancestorId = null;
-
- if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) {
- ancestorId = repository.resolve(request.getAncestorChangeset());
- if (ancestorId == null) {
- throw notFound(entity(REVISION, request.getAncestorChangeset()).in(this.repository));
- }
- }
-
- revWalk = new RevWalk(repository);
-
- converter = new GitChangesetConverter(repository, revWalk);
-
- if (!Strings.isNullOrEmpty(request.getPath())) {
- revWalk.setTreeFilter(
- AndTreeFilter.create(
- PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF));
- }
-
- if (branch != null) {
- if (startId != null) {
- revWalk.markStart(revWalk.lookupCommit(startId));
- } else {
- revWalk.markStart(revWalk.lookupCommit(branch.getObjectId()));
- }
-
- if (ancestorId != null) {
- revWalk.markUninteresting(revWalk.lookupCommit(ancestorId));
- }
-
- Iterator iterator = revWalk.iterator();
-
- while (iterator.hasNext()) {
- RevCommit commit = iterator.next();
-
- if ((counter >= start)
- && ((limit < 0) || (counter < start + limit))) {
- changesetList.add(converter.createChangeset(commit));
- }
-
- counter++;
-
- if (commit.getId().equals(endId)) {
- break;
- }
- }
- } else if (ancestorId != null) {
- throw notFound(entity(REVISION, request.getBranch()).in(this.repository));
- }
-
- if (branch != null) {
- changesets = new ChangesetPagingResult(counter, changesetList, GitUtil.getBranch(branch.getName()));
- } else {
- changesets = new ChangesetPagingResult(counter, changesetList);
- }
- } else if (logger.isWarnEnabled()) {
- logger.warn("the repository {} seems to be empty",
- this.repository.getName());
-
- changesets = new ChangesetPagingResult(0, Collections.EMPTY_LIST);
+ try (org.eclipse.jgit.lib.Repository gitRepository = open()) {
+ if (Strings.isNullOrEmpty(request.getBranch())) {
+ request.setBranch(context.getConfig().getDefaultBranch());
}
+ return new GitLogComputer(this.repository.getId(), gitRepository).compute(request);
+ } catch (IOException e) {
+ throw new InternalRepositoryException(repository, "could not create change log", e);
}
- catch (MissingObjectException e)
- {
- throw notFound(entity(REVISION, e.getObjectId().getName()).in(repository));
- }
- catch (NotFoundException e)
- {
- throw e;
- }
- catch (Exception ex)
- {
- throw new InternalRepositoryException(repository, "could not create change log", ex);
- }
- finally
- {
- IOUtil.close(converter);
- GitUtil.release(revWalk);
- }
-
- return changesets;
}
+
}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogComputer.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogComputer.java
new file mode 100644
index 0000000000..b8c31045d8
--- /dev/null
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogComputer.java
@@ -0,0 +1,187 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package sonia.scm.repository.spi;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.eclipse.jgit.errors.InvalidObjectIdException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sonia.scm.ContextEntry;
+import sonia.scm.NotFoundException;
+import sonia.scm.repository.Changeset;
+import sonia.scm.repository.ChangesetPagingResult;
+import sonia.scm.repository.GitChangesetConverter;
+import sonia.scm.repository.GitUtil;
+import sonia.scm.repository.InternalRepositoryException;
+import sonia.scm.util.IOUtil;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import static sonia.scm.ContextEntry.ContextBuilder.entity;
+import static sonia.scm.NotFoundException.notFound;
+
+public class GitLogComputer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(GitLogComputer.class);
+
+ private final String repositoryId;
+ private final Repository gitRepository;
+
+ public GitLogComputer(String repositoryId, Repository repository) {
+ this.repositoryId = repositoryId;
+ this.gitRepository = repository;
+ }
+
+ public ChangesetPagingResult compute(LogCommandRequest request) {
+ LOG.debug("fetch changesets for request: {}", request);
+
+ GitChangesetConverter converter = null;
+ RevWalk revWalk = null;
+
+ try {
+ if (!gitRepository.getAllRefs().isEmpty()) {
+ int counter = 0;
+ int start = request.getPagingStart();
+
+ if (start < 0) {
+ LOG.error("start parameter is negative, reset to 0");
+
+ start = 0;
+ }
+
+ List changesetList = Lists.newArrayList();
+ int limit = request.getPagingLimit();
+ ObjectId startId = null;
+
+ if (!Strings.isNullOrEmpty(request.getStartChangeset())) {
+ startId = gitRepository.resolve(request.getStartChangeset());
+ }
+
+ ObjectId endId = null;
+
+ if (!Strings.isNullOrEmpty(request.getEndChangeset())) {
+ endId = gitRepository.resolve(request.getEndChangeset());
+ }
+
+ Ref branch = GitUtil.getBranchIdOrCurrentHead(gitRepository, request.getBranch());
+ ObjectId branchId;
+ if (branch == null) {
+ if (request.getBranch() != null) {
+ try {
+ branchId = ObjectId.fromString(request.getBranch());
+ } catch (InvalidObjectIdException e) {
+ throw notFound(entity(GitLogCommand.REVISION, request.getBranch()).in(sonia.scm.repository.Repository.class, repositoryId));
+ }
+ } else {
+ branchId = null;
+ }
+ } else {
+ branchId = branch.getObjectId();
+ }
+
+ ObjectId ancestorId = null;
+
+ if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) {
+ ancestorId = gitRepository.resolve(request.getAncestorChangeset());
+ if (ancestorId == null) {
+ throw notFound(entity(GitLogCommand.REVISION, request.getAncestorChangeset()).in(sonia.scm.repository.Repository.class, repositoryId));
+ }
+ }
+
+ revWalk = new RevWalk(gitRepository);
+
+ converter = new GitChangesetConverter(gitRepository, revWalk);
+
+ if (!Strings.isNullOrEmpty(request.getPath())) {
+ revWalk.setTreeFilter(
+ AndTreeFilter.create(
+ PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF));
+ }
+
+ if (branchId != null) {
+ if (startId != null) {
+ revWalk.markStart(revWalk.lookupCommit(startId));
+ } else {
+ revWalk.markStart(revWalk.lookupCommit(branchId));
+ }
+
+ if (ancestorId != null) {
+ revWalk.markUninteresting(revWalk.lookupCommit(ancestorId));
+ }
+
+ Iterator iterator = revWalk.iterator();
+
+ while (iterator.hasNext()) {
+ RevCommit commit = iterator.next();
+
+ if ((counter >= start)
+ && ((limit < 0) || (counter < start + limit))) {
+ changesetList.add(converter.createChangeset(commit));
+ }
+
+ counter++;
+
+ if (commit.getId().equals(endId)) {
+ break;
+ }
+ }
+ } else if (ancestorId != null) {
+ throw notFound(entity(GitLogCommand.REVISION, request.getBranch()).in(sonia.scm.repository.Repository.class, repositoryId));
+ }
+
+ if (branch != null) {
+ return new ChangesetPagingResult(counter, changesetList, GitUtil.getBranch(branch.getName()));
+ } else {
+ return new ChangesetPagingResult(counter, changesetList);
+ }
+ } else {
+ LOG.debug("the repository with id {} seems to be empty", this.repositoryId);
+
+ return new ChangesetPagingResult(0, Collections.EMPTY_LIST);
+ }
+ } catch (MissingObjectException e) {
+ throw notFound(entity(GitLogCommand.REVISION, e.getObjectId().getName()).in(sonia.scm.repository.Repository.class, repositoryId));
+ } catch (NotFoundException e) {
+ throw e;
+ } catch (Exception ex) {
+ throw new InternalRepositoryException(entity(sonia.scm.repository.Repository.class, repositoryId).build(), "could not create change log", ex);
+ } finally {
+ IOUtil.close(converter);
+ GitUtil.release(revWalk);
+ }
+ }
+}
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 c7b323224c..a2d218b99c 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
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
@@ -122,8 +122,7 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook
logger.trace("resolved repository to {}", repositoryId);
- GitHookContextProvider context = new GitHookContextProvider(rpack,
- receiveCommands);
+ GitHookContextProvider context = new GitHookContextProvider(rpack, receiveCommands, type, repository, repositoryId);
hookEventFacade.handle(repositoryId).fireHookEvent(type, context);
]