From 4b024c650c2070e0caf85feb822c023ac6af388f Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 4 Nov 2019 15:16:37 +0100 Subject: [PATCH 01/16] implement squash to mergeCommand --- .../repository/api/MergeCommandBuilder.java | 12 +++++ .../scm/repository/api/ScmMergeStrategy.java | 5 ++ .../repository/spi/MergeCommandRequest.java | 16 +++++- .../scm/repository/spi/GitMergeCommand.java | 18 +++++-- .../repository/spi/GitMergeCommandTest.java | 51 ++++++++++++++++++ .../scm/repository/spi/scm-git-spi-test.zip | Bin 28443 -> 36171 bytes 6 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/api/ScmMergeStrategy.java diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java index 0a2267e888..df9f6bdfbe 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java @@ -92,6 +92,18 @@ public class MergeCommandBuilder { return this; } + /** + * Use this to set the strategy of the merge commit manually. + * + * This is optional and for {@link #executeMerge()} only. + * + * @return This builder instance. + */ + public MergeCommandBuilder setMergeStrategy(ScmMergeStrategy strategy) { + request.setScmMergeStrategy(strategy); + return this; + } + /** * Use this to set a template for the commit message. If no message is set, a default message will be used. * diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ScmMergeStrategy.java b/scm-core/src/main/java/sonia/scm/repository/api/ScmMergeStrategy.java new file mode 100644 index 0000000000..7ff1b6bc51 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/api/ScmMergeStrategy.java @@ -0,0 +1,5 @@ +package sonia.scm.repository.api; + +public enum ScmMergeStrategy { + SQUASH +} diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java index 223cf8c49e..f7094a5e7f 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java @@ -5,6 +5,7 @@ import com.google.common.base.Objects; import com.google.common.base.Strings; import sonia.scm.Validateable; import sonia.scm.repository.Person; +import sonia.scm.repository.api.ScmMergeStrategy; import sonia.scm.repository.util.AuthorUtil.CommandWithAuthor; import java.io.Serializable; @@ -17,6 +18,7 @@ public class MergeCommandRequest implements Validateable, Resetable, Serializabl private String targetBranch; private Person author; private String messageTemplate; + private ScmMergeStrategy scmMergeStrategy; public String getBranchToMerge() { return branchToMerge; @@ -50,6 +52,14 @@ public class MergeCommandRequest implements Validateable, Resetable, Serializabl this.messageTemplate = messageTemplate; } + public ScmMergeStrategy getScmMergeStrategy() { + return scmMergeStrategy; + } + + public void setScmMergeStrategy(ScmMergeStrategy scmMergeStrategy) { + this.scmMergeStrategy = scmMergeStrategy; + } + public boolean isValid() { return !Strings.isNullOrEmpty(getBranchToMerge()) && !Strings.isNullOrEmpty(getTargetBranch()); @@ -74,12 +84,13 @@ public class MergeCommandRequest implements Validateable, Resetable, Serializabl return Objects.equal(branchToMerge, other.branchToMerge) && Objects.equal(targetBranch, other.targetBranch) - && Objects.equal(author, other.author); + && Objects.equal(author, other.author) + && Objects.equal(scmMergeStrategy, other.scmMergeStrategy); } @Override public int hashCode() { - return Objects.hashCode(branchToMerge, targetBranch, author); + return Objects.hashCode(branchToMerge, targetBranch, author, scmMergeStrategy); } @Override @@ -88,6 +99,7 @@ public class MergeCommandRequest implements Validateable, Resetable, Serializabl .add("branchToMerge", branchToMerge) .add("targetBranch", targetBranch) .add("author", author) + .add("mergeStrategy", scmMergeStrategy) .toString(); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index 5643c858b5..bafe6b8376 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -16,6 +16,7 @@ import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Person; import sonia.scm.repository.api.MergeCommandResult; import sonia.scm.repository.api.MergeDryRunCommandResult; +import sonia.scm.repository.api.ScmMergeStrategy; import java.io.IOException; import java.text.MessageFormat; @@ -61,6 +62,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand private final String toMerge; private final Person author; private final String messageTemplate; + private final ScmMergeStrategy scmMergeStrategy; private MergeWorker(Git clone, MergeCommandRequest request) { super(clone); @@ -68,6 +70,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand this.toMerge = request.getBranchToMerge(); this.author = request.getAuthor(); this.messageTemplate = request.getMessageTemplate(); + this.scmMergeStrategy = request.getScmMergeStrategy(); } @Override @@ -86,11 +89,18 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand MergeResult result; try { ObjectId sourceRevision = resolveRevision(toMerge); - result = getClone().merge() - .setFastForward(FastForwardMode.NO_FF) + org.eclipse.jgit.api.MergeCommand mergeCommand = getClone().merge(); + mergeCommand .setCommit(false) // we want to set the author manually - .include(toMerge, sourceRevision) - .call(); + .include(toMerge, sourceRevision); + + if (scmMergeStrategy == ScmMergeStrategy.SQUASH) { + mergeCommand.setSquash(true); + } else { + mergeCommand.setFastForward(FastForwardMode.NO_FF); + } + + result = mergeCommand.call(); } catch (GitAPIException e) { throw new InternalRepositoryException(context.getRepository(), "could not merge branch " + toMerge + " into " + target, e); } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java index 5586a2f710..873dd23498 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java @@ -15,10 +15,12 @@ import org.junit.Test; import sonia.scm.NotFoundException; import sonia.scm.repository.Person; import sonia.scm.repository.api.MergeCommandResult; +import sonia.scm.repository.api.ScmMergeStrategy; import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.user.User; import java.io.IOException; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -211,6 +213,55 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { assertThat(new String(contentOfFileB)).isEqualTo("b\ncontent from branch\n"); } + @Test + public void shouldSquashCommitsIfSquashIsEnabled() throws IOException, GitAPIException { + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + request.setBranchToMerge("squash"); + request.setTargetBranch("master"); + request.setMessageTemplate("this is a squash"); + request.setScmMergeStrategy(ScmMergeStrategy.SQUASH); + + MergeCommandResult mergeCommandResult = command.merge(request); + + Repository repository = createContext().open(); + assertThat(mergeCommandResult.isSuccess()).isTrue(); + + Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call(); + RevCommit mergeCommit = commits.iterator().next(); + PersonIdent mergeAuthor = mergeCommit.getAuthorIdent(); + String message = mergeCommit.getFullMessage(); + assertThat(mergeAuthor.getName()).isEqualTo("Dirk Gently"); + assertThat(message).isEqualTo("this is a squash"); + } + + @Test + public void shouldSquashThreeCommitsIntoOne() throws IOException, GitAPIException { + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + request.setBranchToMerge("squash"); + request.setTargetBranch("master"); + request.setMessageTemplate("squash three commits"); + request.setScmMergeStrategy(ScmMergeStrategy.SQUASH); + Repository gitRepository = createContext().open(); + MergeCommandResult mergeCommandResult = command.merge(request); + + assertThat(mergeCommandResult.isSuccess()).isTrue(); + + Iterable commits = new Git(gitRepository).log().add(gitRepository.resolve("master")).setMaxCount(1).call(); + RevCommit mergeCommit = commits.iterator().next(); + PersonIdent mergeAuthor = mergeCommit.getAuthorIdent(); + String message = mergeCommit.getFullMessage(); + assertThat(mergeAuthor.getName()).isEqualTo("Dirk Gently"); + assertThat(message).isEqualTo("squash three commits"); + + GitModificationsCommand modificationsCommand = new GitModificationsCommand(createContext(), repository); + List changes = modificationsCommand.getModifications("master").getAdded(); + assertThat(changes.size()).isEqualTo(3); + } + @Test(expected = NotFoundException.class) public void shouldHandleNotExistingSourceBranchInMerge() { GitMergeCommand command = createCommand(); diff --git a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip index 4310e637335c09568da9d3afa63ce57949cec5ab..8e43da1d828d94bc3f0bbe423d6488c88c52e5bf 100644 GIT binary patch delta 18069 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 From 9df12bc35ed06f0d02af5b9314264d2947383007 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 4 Nov 2019 17:34:44 +0100 Subject: [PATCH 02/16] check if mergeStrategy is supported for mergeCommand --- .../repository/api/MergeCommandBuilder.java | 13 ++++++++++-- ...mMergeStrategy.java => MergeStrategy.java} | 2 +- .../scm/repository/spi/MergeCommand.java | 3 +++ .../repository/spi/MergeCommandRequest.java | 18 ++++++++--------- .../scm/repository/spi/GitMergeCommand.java | 20 +++++++++++++------ .../repository/spi/GitMergeCommandTest.java | 6 +++--- 6 files changed, 41 insertions(+), 21 deletions(-) rename scm-core/src/main/java/sonia/scm/repository/api/{ScmMergeStrategy.java => MergeStrategy.java} (59%) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java index df9f6bdfbe..6910dc02fa 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java @@ -55,6 +55,15 @@ public class MergeCommandBuilder { this.mergeCommand = mergeCommand; } + /** + * Use this to check if merge-strategy is supported by mergeCommand + * + * @return boolean. + */ + public boolean isSupported(MergeStrategy strategy) { + return mergeCommand.isSupported(strategy); + } + /** * Use this to set the branch that should be merged into the target branch. * @@ -99,8 +108,8 @@ public class MergeCommandBuilder { * * @return This builder instance. */ - public MergeCommandBuilder setMergeStrategy(ScmMergeStrategy strategy) { - request.setScmMergeStrategy(strategy); + public MergeCommandBuilder setMergeStrategy(MergeStrategy strategy) { + request.setMergeStrategy(strategy); return this; } diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ScmMergeStrategy.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java similarity index 59% rename from scm-core/src/main/java/sonia/scm/repository/api/ScmMergeStrategy.java rename to scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java index 7ff1b6bc51..9968dd61e2 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/ScmMergeStrategy.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java @@ -1,5 +1,5 @@ package sonia.scm.repository.api; -public enum ScmMergeStrategy { +public enum MergeStrategy { SQUASH } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java index 0a3680f6b3..cc20db4c5d 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java @@ -2,9 +2,12 @@ package sonia.scm.repository.spi; import sonia.scm.repository.api.MergeCommandResult; import sonia.scm.repository.api.MergeDryRunCommandResult; +import sonia.scm.repository.api.MergeStrategy; public interface MergeCommand { MergeCommandResult merge(MergeCommandRequest request); MergeDryRunCommandResult dryRun(MergeCommandRequest request); + + boolean isSupported(MergeStrategy strategy); } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java index f7094a5e7f..0751920d01 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java @@ -5,7 +5,7 @@ import com.google.common.base.Objects; import com.google.common.base.Strings; import sonia.scm.Validateable; import sonia.scm.repository.Person; -import sonia.scm.repository.api.ScmMergeStrategy; +import sonia.scm.repository.api.MergeStrategy; import sonia.scm.repository.util.AuthorUtil.CommandWithAuthor; import java.io.Serializable; @@ -18,7 +18,7 @@ public class MergeCommandRequest implements Validateable, Resetable, Serializabl private String targetBranch; private Person author; private String messageTemplate; - private ScmMergeStrategy scmMergeStrategy; + private MergeStrategy mergeStrategy; public String getBranchToMerge() { return branchToMerge; @@ -52,12 +52,12 @@ public class MergeCommandRequest implements Validateable, Resetable, Serializabl this.messageTemplate = messageTemplate; } - public ScmMergeStrategy getScmMergeStrategy() { - return scmMergeStrategy; + public MergeStrategy getMergeStrategy() { + return mergeStrategy; } - public void setScmMergeStrategy(ScmMergeStrategy scmMergeStrategy) { - this.scmMergeStrategy = scmMergeStrategy; + public void setMergeStrategy(MergeStrategy mergeStrategy) { + this.mergeStrategy = mergeStrategy; } public boolean isValid() { @@ -85,12 +85,12 @@ public class MergeCommandRequest implements Validateable, Resetable, Serializabl return Objects.equal(branchToMerge, other.branchToMerge) && Objects.equal(targetBranch, other.targetBranch) && Objects.equal(author, other.author) - && Objects.equal(scmMergeStrategy, other.scmMergeStrategy); + && Objects.equal(mergeStrategy, other.mergeStrategy); } @Override public int hashCode() { - return Objects.hashCode(branchToMerge, targetBranch, author, scmMergeStrategy); + return Objects.hashCode(branchToMerge, targetBranch, author, mergeStrategy); } @Override @@ -99,7 +99,7 @@ public class MergeCommandRequest implements Validateable, Resetable, Serializabl .add("branchToMerge", branchToMerge) .add("targetBranch", targetBranch) .add("author", author) - .add("mergeStrategy", scmMergeStrategy) + .add("mergeStrategy", mergeStrategy) .toString(); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index bafe6b8376..35e2f1e144 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -1,13 +1,13 @@ package sonia.scm.repository.spi; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.MergeCommand.FastForwardMode; import org.eclipse.jgit.api.MergeResult; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.ResolveMerger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,10 +16,11 @@ import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Person; import sonia.scm.repository.api.MergeCommandResult; import sonia.scm.repository.api.MergeDryRunCommandResult; -import sonia.scm.repository.api.ScmMergeStrategy; +import sonia.scm.repository.api.MergeStrategy; import java.io.IOException; import java.text.MessageFormat; +import java.util.Set; public class GitMergeCommand extends AbstractGitCommand implements MergeCommand { @@ -32,6 +33,8 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand private final GitWorkdirFactory workdirFactory; + private static final Set STRATEGIES = ImmutableSet.of(MergeStrategy.SQUASH); + GitMergeCommand(GitContext context, sonia.scm.repository.Repository repository, GitWorkdirFactory workdirFactory) { super(context, repository); this.workdirFactory = workdirFactory; @@ -46,7 +49,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand public MergeDryRunCommandResult dryRun(MergeCommandRequest request) { try { Repository repository = context.open(); - ResolveMerger merger = (ResolveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true); + ResolveMerger merger = (ResolveMerger) org.eclipse.jgit.merge.MergeStrategy.RECURSIVE.newMerger(repository, true); return new MergeDryRunCommandResult( merger.merge( resolveRevisionOrThrowNotFound(repository, request.getBranchToMerge()), @@ -56,13 +59,18 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand } } + @Override + public boolean isSupported(MergeStrategy strategy) { + return STRATEGIES.contains(strategy); + } + private class MergeWorker extends GitCloneWorker { private final String target; private final String toMerge; private final Person author; private final String messageTemplate; - private final ScmMergeStrategy scmMergeStrategy; + private final MergeStrategy mergeStrategy; private MergeWorker(Git clone, MergeCommandRequest request) { super(clone); @@ -70,7 +78,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand this.toMerge = request.getBranchToMerge(); this.author = request.getAuthor(); this.messageTemplate = request.getMessageTemplate(); - this.scmMergeStrategy = request.getScmMergeStrategy(); + this.mergeStrategy = request.getMergeStrategy(); } @Override @@ -94,7 +102,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand .setCommit(false) // we want to set the author manually .include(toMerge, sourceRevision); - if (scmMergeStrategy == ScmMergeStrategy.SQUASH) { + if (mergeStrategy == MergeStrategy.SQUASH) { mergeCommand.setSquash(true); } else { mergeCommand.setFastForward(FastForwardMode.NO_FF); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java index 873dd23498..f27b1e3760 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java @@ -15,7 +15,7 @@ import org.junit.Test; import sonia.scm.NotFoundException; import sonia.scm.repository.Person; import sonia.scm.repository.api.MergeCommandResult; -import sonia.scm.repository.api.ScmMergeStrategy; +import sonia.scm.repository.api.MergeStrategy; import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.user.User; @@ -221,7 +221,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { request.setBranchToMerge("squash"); request.setTargetBranch("master"); request.setMessageTemplate("this is a squash"); - request.setScmMergeStrategy(ScmMergeStrategy.SQUASH); + request.setMergeStrategy(MergeStrategy.SQUASH); MergeCommandResult mergeCommandResult = command.merge(request); @@ -244,7 +244,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { request.setBranchToMerge("squash"); request.setTargetBranch("master"); request.setMessageTemplate("squash three commits"); - request.setScmMergeStrategy(ScmMergeStrategy.SQUASH); + request.setMergeStrategy(MergeStrategy.SQUASH); Repository gitRepository = createContext().open(); MergeCommandResult mergeCommandResult = command.merge(request); From db46441adf9707715c08f96c32b1856823c8efbf Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 5 Nov 2019 09:51:58 +0100 Subject: [PATCH 03/16] move mergeResultDto + mapper to scm-core --- scm-core/pom.xml | 6 ++++++ .../java/sonia/scm/api/v2/resources/MergeResultDto.java | 0 .../sonia/scm/api/v2/resources/MergeResultToDtoMapper.java | 0 3 files changed, 6 insertions(+) rename {scm-webapp => scm-core}/src/main/java/sonia/scm/api/v2/resources/MergeResultDto.java (100%) rename {scm-webapp => scm-core}/src/main/java/sonia/scm/api/v2/resources/MergeResultToDtoMapper.java (100%) diff --git a/scm-core/pom.xml b/scm-core/pom.xml index f19d50064d..c7bbc664c9 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -131,6 +131,12 @@ mapstruct-jdk8 + + org.mapstruct + mapstruct-processor + provided + + com.webcohesion.enunciate diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeResultDto.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/MergeResultDto.java similarity index 100% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeResultDto.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/MergeResultDto.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeResultToDtoMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/MergeResultToDtoMapper.java similarity index 100% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeResultToDtoMapper.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/MergeResultToDtoMapper.java From bc5948f8230f8365dd7ff07a00cea6fd2751ecb9 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 6 Nov 2019 07:47:23 +0100 Subject: [PATCH 04/16] add new MergeStrategies --- .../sonia/scm/repository/api/MergeCommandBuilder.java | 6 ++++++ .../java/sonia/scm/repository/api/MergeStrategy.java | 4 +++- .../java/sonia/scm/repository/spi/MergeCommand.java | 4 ++++ .../sonia/scm/repository/spi/GitMergeCommand.java | 11 ++++++++++- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java index 6910dc02fa..267dfa9862 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java @@ -6,6 +6,8 @@ import sonia.scm.repository.spi.MergeCommand; import sonia.scm.repository.spi.MergeCommandRequest; import sonia.scm.repository.util.AuthorUtil; +import java.util.Set; + /** * Use this {@link MergeCommandBuilder} to merge two branches of a repository ({@link #executeMerge()}) or to check if * the branches could be merged without conflicts ({@link #dryRun()}). To do so, you have to specify the name of @@ -64,6 +66,10 @@ public class MergeCommandBuilder { return mergeCommand.isSupported(strategy); } + public Set getSupportedMergeStrategies() { + return mergeCommand.getSupportedMergeStrategies(); + } + /** * Use this to set the branch that should be merged into the target branch. * diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java index 9968dd61e2..8fa6b1c49c 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java @@ -1,5 +1,7 @@ package sonia.scm.repository.api; public enum MergeStrategy { - SQUASH + SQUASH, + MERGE_COMMIT, + FAST_FORWARD_IF_POSSIBLE } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java index cc20db4c5d..a62e373dca 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java @@ -4,10 +4,14 @@ import sonia.scm.repository.api.MergeCommandResult; import sonia.scm.repository.api.MergeDryRunCommandResult; import sonia.scm.repository.api.MergeStrategy; +import java.util.Set; + public interface MergeCommand { MergeCommandResult merge(MergeCommandRequest request); MergeDryRunCommandResult dryRun(MergeCommandRequest request); boolean isSupported(MergeStrategy strategy); + + Set getSupportedMergeStrategies(); } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index 35e2f1e144..378ec3afbd 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -33,7 +33,11 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand private final GitWorkdirFactory workdirFactory; - private static final Set STRATEGIES = ImmutableSet.of(MergeStrategy.SQUASH); + private static final Set STRATEGIES = ImmutableSet.of( + MergeStrategy.SQUASH, + MergeStrategy.MERGE_COMMIT, + MergeStrategy.FAST_FORWARD_IF_POSSIBLE + ); GitMergeCommand(GitContext context, sonia.scm.repository.Repository repository, GitWorkdirFactory workdirFactory) { super(context, repository); @@ -64,6 +68,11 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand return STRATEGIES.contains(strategy); } + @Override + public Set getSupportedMergeStrategies() { + return STRATEGIES; + } + private class MergeWorker extends GitCloneWorker { private final String target; From b4f1e8874af7399599041be50de41ff2d442c7c7 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 6 Nov 2019 09:54:17 +0100 Subject: [PATCH 05/16] change mergeStrategy order --- .../src/main/java/sonia/scm/repository/api/MergeStrategy.java | 4 ++-- .../main/java/sonia/scm/repository/spi/GitMergeCommand.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java index 8fa6b1c49c..4ccf1f706b 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java @@ -1,7 +1,7 @@ package sonia.scm.repository.api; public enum MergeStrategy { - SQUASH, MERGE_COMMIT, - FAST_FORWARD_IF_POSSIBLE + FAST_FORWARD_IF_POSSIBLE, + SQUASH } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index 378ec3afbd..ff882b4f20 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -34,9 +34,9 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand private final GitWorkdirFactory workdirFactory; private static final Set STRATEGIES = ImmutableSet.of( - MergeStrategy.SQUASH, MergeStrategy.MERGE_COMMIT, - MergeStrategy.FAST_FORWARD_IF_POSSIBLE + MergeStrategy.FAST_FORWARD_IF_POSSIBLE, + MergeStrategy.SQUASH ); GitMergeCommand(GitContext context, sonia.scm.repository.Repository repository, GitWorkdirFactory workdirFactory) { From cf90654e24d734fe784f806009ccde9a5da32dbb Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 6 Nov 2019 15:26:20 +0100 Subject: [PATCH 06/16] implement fast forward if possible --- .../repository/api/MergeCommandResult.java | 9 +++++++ .../scm/repository/spi/GitMergeCommand.java | 15 ++++++++--- .../repository/spi/GitMergeCommandTest.java | 25 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java index 53f712cddc..5088fcad1c 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java @@ -13,6 +13,7 @@ import static java.util.Collections.unmodifiableCollection; */ public class MergeCommandResult { private final Collection filesWithConflict; + private boolean aborted = false; private MergeCommandResult(Collection filesWithConflict) { this.filesWithConflict = filesWithConflict; @@ -41,4 +42,12 @@ public class MergeCommandResult { public Collection getFilesWithConflict() { return unmodifiableCollection(filesWithConflict); } + + public boolean isAborted() { + return aborted; + } + + public void setAborted(boolean aborted) { + this.aborted = aborted; + } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index ff882b4f20..6d5c33f874 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -3,7 +3,6 @@ package sonia.scm.repository.spi; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.MergeCommand.FastForwardMode; import org.eclipse.jgit.api.MergeResult; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.ObjectId; @@ -20,6 +19,7 @@ import sonia.scm.repository.api.MergeStrategy; import java.io.IOException; import java.text.MessageFormat; +import java.util.Collections; import java.util.Set; public class GitMergeCommand extends AbstractGitCommand implements MergeCommand { @@ -93,10 +93,17 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand @Override MergeCommandResult run() throws IOException { MergeResult result = doMergeInClone(); + if (result.getMergeStatus().isSuccessful()) { - doCommit(); + if (mergeStrategy != MergeStrategy.FAST_FORWARD_IF_POSSIBLE) { + doCommit(); + } push(); return MergeCommandResult.success(); + } else if (mergeStrategy == MergeStrategy.FAST_FORWARD_IF_POSSIBLE) { + MergeCommandResult failure = MergeCommandResult.failure(Collections.emptyList()); + failure.setAborted(true); + return failure; } else { return analyseFailure(result); } @@ -113,8 +120,10 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand if (mergeStrategy == MergeStrategy.SQUASH) { mergeCommand.setSquash(true); + } else if (mergeStrategy == MergeStrategy.FAST_FORWARD_IF_POSSIBLE) { + mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.FF_ONLY); } else { - mergeCommand.setFastForward(FastForwardMode.NO_FF); + mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.NO_FF); } result = mergeCommand.call(); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java index f27b1e3760..4f04205202 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java @@ -262,6 +262,31 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { assertThat(changes.size()).isEqualTo(3); } + + @Test + public void shouldMergeWithFastForward() throws IOException, GitAPIException { + Repository repository = createContext().open(); + + ObjectId featureBranchHead = new Git(repository).log().add(repository.resolve("squash")).setMaxCount(1).call().iterator().next().getId(); + + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setBranchToMerge("squash"); + request.setTargetBranch("master"); + request.setMergeStrategy(MergeStrategy.FAST_FORWARD_IF_POSSIBLE); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + MergeCommandResult mergeCommandResult = command.merge(request); + + assertThat(mergeCommandResult.isSuccess()).isTrue(); + + Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call(); + RevCommit mergeCommit = commits.iterator().next(); + PersonIdent mergeAuthor = mergeCommit.getAuthorIdent(); + assertThat(mergeAuthor.getName()).isEqualTo("Philip J Fry"); + assertThat(mergeCommit.getId()).isEqualTo(featureBranchHead); + } + @Test(expected = NotFoundException.class) public void shouldHandleNotExistingSourceBranchInMerge() { GitMergeCommand command = createCommand(); From de09097203de3c92406bf364517283c263f02502 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 7 Nov 2019 08:14:15 +0100 Subject: [PATCH 07/16] cleanup --- .../sonia/scm/repository/api/MergeCommandBuilder.java | 7 ++++++- .../java/sonia/scm/repository/spi/GitMergeCommand.java | 10 +++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java index 267dfa9862..db6c677e1f 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java @@ -58,7 +58,7 @@ public class MergeCommandBuilder { } /** - * Use this to check if merge-strategy is supported by mergeCommand + * Use this to check if merge-strategy is supported by mergeCommand. * * @return boolean. */ @@ -66,6 +66,11 @@ public class MergeCommandBuilder { return mergeCommand.isSupported(strategy); } + /** + * Use this to get a Set of all supported merge strategies by merge command. + * + * @return boolean. + */ public Set getSupportedMergeStrategies() { return mergeCommand.getSupportedMergeStrategies(); } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index 6d5c33f874..1ef4eacce3 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -95,12 +95,12 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand MergeResult result = doMergeInClone(); if (result.getMergeStatus().isSuccessful()) { - if (mergeStrategy != MergeStrategy.FAST_FORWARD_IF_POSSIBLE) { + if (!isFastForward()) { doCommit(); } push(); return MergeCommandResult.success(); - } else if (mergeStrategy == MergeStrategy.FAST_FORWARD_IF_POSSIBLE) { + } else if (isFastForward()) { MergeCommandResult failure = MergeCommandResult.failure(Collections.emptyList()); failure.setAborted(true); return failure; @@ -120,7 +120,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand if (mergeStrategy == MergeStrategy.SQUASH) { mergeCommand.setSquash(true); - } else if (mergeStrategy == MergeStrategy.FAST_FORWARD_IF_POSSIBLE) { + } else if (isFastForward()) { mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.FF_ONLY); } else { mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.NO_FF); @@ -150,5 +150,9 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand logger.info("could not merged branch {} into {} due to conflict in paths {}", toMerge, target, result.getConflicts().keySet()); return MergeCommandResult.failure(result.getConflicts().keySet()); } + + private boolean isFastForward() { + return mergeStrategy == MergeStrategy.FAST_FORWARD_IF_POSSIBLE; + } } } From ec6b4493c80cec368182958e279cd3297fd2fa18 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 7 Nov 2019 11:50:28 +0100 Subject: [PATCH 08/16] use strategy pattern for mergeStrategies --- .../repository/api/MergeCommandResult.java | 9 -- .../repository/spi/AbstractGitCommand.java | 21 +++- .../spi/GitFastForwardIfPossible.java | 42 +++++++ .../scm/repository/spi/GitMergeCommand.java | 110 ++---------------- .../scm/repository/spi/GitMergeCommit.java | 31 +++++ .../scm/repository/spi/GitMergeStrategy.java | 82 +++++++++++++ .../repository/spi/GitMergeWithSquash.java | 30 +++++ .../scm/repository/spi/GitModifyCommand.java | 2 +- 8 files changed, 213 insertions(+), 114 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommit.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeWithSquash.java diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java index 5088fcad1c..53f712cddc 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java @@ -13,7 +13,6 @@ import static java.util.Collections.unmodifiableCollection; */ public class MergeCommandResult { private final Collection filesWithConflict; - private boolean aborted = false; private MergeCommandResult(Collection filesWithConflict) { this.filesWithConflict = filesWithConflict; @@ -42,12 +41,4 @@ public class MergeCommandResult { public Collection getFilesWithConflict() { return unmodifiableCollection(filesWithConflict); } - - public boolean isAborted() { - return aborted; - } - - public void setAborted(boolean aborted) { - this.aborted = aborted; - } } 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 73159da5c1..adf7878221 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 @@ -152,19 +152,28 @@ class AbstractGitCommand } ObjectId resolveRevisionOrThrowNotFound(Repository repository, String revision) throws IOException { + sonia.scm.repository.Repository scmRepository = context.getRepository(); + return resolveRevisionOrThrowNotFound(repository, revision, scmRepository); + } + + static ObjectId resolveRevisionOrThrowNotFound(Repository repository, String revision, sonia.scm.repository.Repository scmRepository) throws IOException { ObjectId resolved = repository.resolve(revision); if (resolved == null) { - throw notFound(entity("Revision", revision).in(context.getRepository())); + throw notFound(entity("Revision", revision).in(scmRepository)); } else { return resolved; } } - abstract class GitCloneWorker { + abstract static class GitCloneWorker { private final Git clone; + private final GitContext context; + private final sonia.scm.repository.Repository repository; - GitCloneWorker(Git clone) { + GitCloneWorker(Git clone, GitContext context, sonia.scm.repository.Repository repository) { this.clone = clone; + this.context = context; + this.repository = repository; } abstract R run() throws IOException; @@ -173,6 +182,10 @@ class AbstractGitCommand return clone; } + GitContext getContext() { + return context; + } + void checkOutBranch(String branchName) throws IOException { try { clone.checkout().setName(branchName).call(); @@ -199,7 +212,7 @@ class AbstractGitCommand ObjectId resolveRevision(String revision) throws IOException { ObjectId resolved = clone.getRepository().resolve(revision); if (resolved == null) { - return resolveRevisionOrThrowNotFound(clone.getRepository(), "origin/" + revision); + return resolveRevisionOrThrowNotFound(clone.getRepository(), "origin/" + revision, context.getRepository()); } else { return resolved; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java new file mode 100644 index 0000000000..c7820009a1 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java @@ -0,0 +1,42 @@ +package sonia.scm.repository.spi; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.MergeCommand; +import org.eclipse.jgit.api.MergeResult; +import sonia.scm.repository.Repository; +import sonia.scm.repository.api.MergeCommandResult; + +import java.io.IOException; + +class GitFastForwardIfPossible extends GitMergeStrategy { + + GitFastForwardIfPossible(Git clone, MergeCommandRequest request, GitContext context, Repository repository) { + super(clone, request, context, repository); + } + + @Override + MergeCommandResult run() throws IOException { + MergeResult fastForwardResult = mergeWithFastForwardMode(MergeCommand.FastForwardMode.FF_ONLY); + if (fastForwardResult.getMergeStatus().isSuccessful()) { + push(); + return MergeCommandResult.success(); + } else { + return mergeWithCommit(); + } + } + + private MergeCommandResult mergeWithCommit() throws IOException { + MergeResult mergeCommitResult = mergeWithFastForwardMode(MergeCommand.FastForwardMode.NO_FF); + if (mergeCommitResult.getMergeStatus().isSuccessful()) { + return MergeCommandResult.success(); + } else { + return analyseFailure(mergeCommitResult); + } + } + + private MergeResult mergeWithFastForwardMode(MergeCommand.FastForwardMode fastForwardMode) throws IOException { + MergeCommand mergeCommand = getClone().merge(); + mergeCommand.setFastForward(fastForwardMode); + return doMergeInClone(mergeCommand); + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index 1ef4eacce3..94c29bdce9 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -1,36 +1,19 @@ package sonia.scm.repository.spi; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.MergeResult; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.merge.ResolveMerger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.InternalRepositoryException; -import sonia.scm.repository.Person; import sonia.scm.repository.api.MergeCommandResult; import sonia.scm.repository.api.MergeDryRunCommandResult; import sonia.scm.repository.api.MergeStrategy; import java.io.IOException; -import java.text.MessageFormat; -import java.util.Collections; import java.util.Set; public class GitMergeCommand extends AbstractGitCommand implements MergeCommand { - private static final Logger logger = LoggerFactory.getLogger(GitMergeCommand.class); - - private static final String MERGE_COMMIT_MESSAGE_TEMPLATE = String.join("\n", - "Merge of branch {0} into {1}", - "", - "Automatic merge by SCM-Manager."); - private final GitWorkdirFactory workdirFactory; private static final Set STRATEGIES = ImmutableSet.of( @@ -46,7 +29,16 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand @Override public MergeCommandResult merge(MergeCommandRequest request) { - return inClone(clone -> new MergeWorker(clone, request), workdirFactory, request.getTargetBranch()); + return mergeWithStrategy(request); + } + + private MergeCommandResult mergeWithStrategy(MergeCommandRequest request) { + if (request.getMergeStrategy() == MergeStrategy.SQUASH) { + return inClone(clone -> new GitMergeWithSquash(clone, request, context, repository), workdirFactory, request.getTargetBranch()); + } else if (request.getMergeStrategy() == MergeStrategy.FAST_FORWARD_IF_POSSIBLE) { + return inClone(clone -> new GitFastForwardIfPossible(clone, request, context, repository), workdirFactory, request.getTargetBranch()); + } + return inClone(clone -> new GitMergeCommit(clone, request, context, repository), workdirFactory, request.getTargetBranch()); } @Override @@ -73,86 +65,4 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand return STRATEGIES; } - private class MergeWorker extends GitCloneWorker { - - private final String target; - private final String toMerge; - private final Person author; - private final String messageTemplate; - private final MergeStrategy mergeStrategy; - - private MergeWorker(Git clone, MergeCommandRequest request) { - super(clone); - this.target = request.getTargetBranch(); - this.toMerge = request.getBranchToMerge(); - this.author = request.getAuthor(); - this.messageTemplate = request.getMessageTemplate(); - this.mergeStrategy = request.getMergeStrategy(); - } - - @Override - MergeCommandResult run() throws IOException { - MergeResult result = doMergeInClone(); - - if (result.getMergeStatus().isSuccessful()) { - if (!isFastForward()) { - doCommit(); - } - push(); - return MergeCommandResult.success(); - } else if (isFastForward()) { - MergeCommandResult failure = MergeCommandResult.failure(Collections.emptyList()); - failure.setAborted(true); - return failure; - } else { - return analyseFailure(result); - } - } - - private MergeResult doMergeInClone() throws IOException { - MergeResult result; - try { - ObjectId sourceRevision = resolveRevision(toMerge); - org.eclipse.jgit.api.MergeCommand mergeCommand = getClone().merge(); - mergeCommand - .setCommit(false) // we want to set the author manually - .include(toMerge, sourceRevision); - - if (mergeStrategy == MergeStrategy.SQUASH) { - mergeCommand.setSquash(true); - } else if (isFastForward()) { - mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.FF_ONLY); - } else { - mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.NO_FF); - } - - result = mergeCommand.call(); - } catch (GitAPIException e) { - throw new InternalRepositoryException(context.getRepository(), "could not merge branch " + toMerge + " into " + target, e); - } - return result; - } - - private void doCommit() { - logger.debug("merged branch {} into {}", toMerge, target); - doCommit(MessageFormat.format(determineMessageTemplate(), toMerge, target), author); - } - - private String determineMessageTemplate() { - if (Strings.isNullOrEmpty(messageTemplate)) { - return MERGE_COMMIT_MESSAGE_TEMPLATE; - } else { - return messageTemplate; - } - } - - private MergeCommandResult analyseFailure(MergeResult result) { - logger.info("could not merged branch {} into {} due to conflict in paths {}", toMerge, target, result.getConflicts().keySet()); - return MergeCommandResult.failure(result.getConflicts().keySet()); - } - - private boolean isFastForward() { - return mergeStrategy == MergeStrategy.FAST_FORWARD_IF_POSSIBLE; - } - } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommit.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommit.java new file mode 100644 index 0000000000..627efb9584 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommit.java @@ -0,0 +1,31 @@ +package sonia.scm.repository.spi; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.MergeCommand; +import org.eclipse.jgit.api.MergeResult; +import sonia.scm.repository.Repository; +import sonia.scm.repository.api.MergeCommandResult; + +import java.io.IOException; + +class GitMergeCommit extends GitMergeStrategy { + + GitMergeCommit(Git clone, MergeCommandRequest request, GitContext context, Repository repository) { + super(clone, request, context, repository); + } + + @Override + MergeCommandResult run() throws IOException { + org.eclipse.jgit.api.MergeCommand mergeCommand = getClone().merge(); + mergeCommand.setFastForward(MergeCommand.FastForwardMode.NO_FF); + MergeResult result = doMergeInClone(mergeCommand); + + if (result.getMergeStatus().isSuccessful()) { + doCommit(); + push(); + return MergeCommandResult.success(); + } else { + return analyseFailure(result); + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java new file mode 100644 index 0000000000..43a3145405 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java @@ -0,0 +1,82 @@ +package sonia.scm.repository.spi; + +import com.google.common.base.Strings; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.MergeCommand; +import org.eclipse.jgit.api.MergeResult; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.ObjectId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.InternalRepositoryException; +import sonia.scm.repository.Person; +import sonia.scm.repository.api.MergeCommandResult; +import sonia.scm.repository.api.MergeStrategy; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collections; + +abstract class GitMergeStrategy extends AbstractGitCommand.GitCloneWorker { + + private static final Logger logger = LoggerFactory.getLogger(GitMergeStrategy.class); + + private static final String MERGE_COMMIT_MESSAGE_TEMPLATE = String.join("\n", + "Merge of branch {0} into {1}", + "", + "Automatic merge by SCM-Manager."); + + private final String target; + private final String toMerge; + private final Person author; + private final String messageTemplate; + private final MergeStrategy mergeStrategy; + + GitMergeStrategy(Git clone, MergeCommandRequest request, GitContext context, sonia.scm.repository.Repository repository) { + super(clone, context, repository); + this.target = request.getTargetBranch(); + this.toMerge = request.getBranchToMerge(); + this.author = request.getAuthor(); + this.messageTemplate = request.getMessageTemplate(); + this.mergeStrategy = request.getMergeStrategy(); + } + + MergeResult doMergeInClone(MergeCommand mergeCommand) throws IOException { + MergeResult result; + try { + ObjectId sourceRevision = resolveRevision(toMerge); + mergeCommand + .setCommit(false) // we want to set the author manually + .include(toMerge, sourceRevision); + + result = mergeCommand.call(); + } catch (GitAPIException e) { + throw new InternalRepositoryException(getContext().getRepository(), "could not merge branch " + toMerge + " into " + target, e); + } + return result; + } + + void doCommit() { + logger.debug("merged branch {} into {}", toMerge, target); + doCommit(MessageFormat.format(determineMessageTemplate(), toMerge, target), author); + } + + private String determineMessageTemplate() { + if (Strings.isNullOrEmpty(messageTemplate)) { + return MERGE_COMMIT_MESSAGE_TEMPLATE; + } else { + return messageTemplate; + } + } + + MergeCommandResult analyseFailure(MergeResult result) { + logger.info("could not merged branch {} into {} due to conflict in paths {}", toMerge, target, result.getConflicts().keySet()); + return MergeCommandResult.failure(result.getConflicts().keySet()); + } + + MergeCommandResult aborted() { + MergeCommandResult failure = MergeCommandResult.failure(Collections.emptyList()); + failure.setAborted(true); + return failure; + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeWithSquash.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeWithSquash.java new file mode 100644 index 0000000000..dfbd31fe32 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeWithSquash.java @@ -0,0 +1,30 @@ +package sonia.scm.repository.spi; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.MergeResult; +import sonia.scm.repository.Repository; +import sonia.scm.repository.api.MergeCommandResult; + +import java.io.IOException; + +class GitMergeWithSquash extends GitMergeStrategy { + + GitMergeWithSquash(Git clone, MergeCommandRequest request, GitContext context, Repository repository) { + super(clone, request, context, repository); + } + + @Override + MergeCommandResult run() throws IOException { + org.eclipse.jgit.api.MergeCommand mergeCommand = getClone().merge(); + mergeCommand.setSquash(true); + MergeResult result = doMergeInClone(mergeCommand); + + if (result.getMergeStatus().isSuccessful()) { + doCommit(); + push(); + return MergeCommandResult.success(); + } else { + return analyseFailure(result); + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index a68be0a4da..19234c32e3 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -46,7 +46,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman private final ModifyCommandRequest request; ModifyWorker(Git clone, ModifyCommandRequest request) { - super(clone); + super(clone, context, repository); this.workDir = clone.getRepository().getWorkTree(); this.request = request; } From e736ae6c50a83a9cdbf9ac29025587016b88fafe Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 7 Nov 2019 13:42:45 +0100 Subject: [PATCH 09/16] cleanup --- .../scm/repository/spi/GitFastForwardIfPossible.java | 2 ++ .../sonia/scm/repository/spi/GitMergeStrategy.java | 10 ---------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java index c7820009a1..417280046f 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java @@ -28,6 +28,8 @@ class GitFastForwardIfPossible extends GitMergeStrategy { private MergeCommandResult mergeWithCommit() throws IOException { MergeResult mergeCommitResult = mergeWithFastForwardMode(MergeCommand.FastForwardMode.NO_FF); if (mergeCommitResult.getMergeStatus().isSuccessful()) { + doCommit(); + push(); return MergeCommandResult.success(); } else { return analyseFailure(mergeCommitResult); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java index 43a3145405..d4d9de3de7 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java @@ -11,11 +11,9 @@ import org.slf4j.LoggerFactory; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Person; import sonia.scm.repository.api.MergeCommandResult; -import sonia.scm.repository.api.MergeStrategy; import java.io.IOException; import java.text.MessageFormat; -import java.util.Collections; abstract class GitMergeStrategy extends AbstractGitCommand.GitCloneWorker { @@ -30,7 +28,6 @@ abstract class GitMergeStrategy extends AbstractGitCommand.GitCloneWorker Date: Fri, 8 Nov 2019 08:36:00 +0100 Subject: [PATCH 10/16] refactor after review --- .../repository/api/MergeCommandBuilder.java | 3 +++ .../spi/GitFastForwardIfPossible.java | 22 ++++++------------ .../scm/repository/spi/GitMergeCommand.java | 21 ++++++++++++----- .../scm/repository/spi/GitMergeCommit.java | 2 +- .../repository/spi/GitMergeWithSquash.java | 3 ++- .../repository/spi/GitMergeCommandTest.java | 23 +++++++++++++++++++ 6 files changed, 51 insertions(+), 23 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java index db6c677e1f..6c4324c259 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java @@ -120,6 +120,9 @@ public class MergeCommandBuilder { * @return This builder instance. */ public MergeCommandBuilder setMergeStrategy(MergeStrategy strategy) { + if (!mergeCommand.isSupported(strategy)) { + throw new IllegalArgumentException("merge strategy not supported: " + strategy); + } request.setMergeStrategy(strategy); return this; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java index 417280046f..64a20a33cb 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitFastForwardIfPossible.java @@ -10,35 +10,27 @@ import java.io.IOException; class GitFastForwardIfPossible extends GitMergeStrategy { + private GitMergeStrategy fallbackMerge; + GitFastForwardIfPossible(Git clone, MergeCommandRequest request, GitContext context, Repository repository) { super(clone, request, context, repository); + fallbackMerge = new GitMergeCommit(clone, request, context, repository); } @Override MergeCommandResult run() throws IOException { - MergeResult fastForwardResult = mergeWithFastForwardMode(MergeCommand.FastForwardMode.FF_ONLY); + MergeResult fastForwardResult = mergeWithFastForwardOnlyMode(); if (fastForwardResult.getMergeStatus().isSuccessful()) { push(); return MergeCommandResult.success(); } else { - return mergeWithCommit(); + return fallbackMerge.run(); } } - private MergeCommandResult mergeWithCommit() throws IOException { - MergeResult mergeCommitResult = mergeWithFastForwardMode(MergeCommand.FastForwardMode.NO_FF); - if (mergeCommitResult.getMergeStatus().isSuccessful()) { - doCommit(); - push(); - return MergeCommandResult.success(); - } else { - return analyseFailure(mergeCommitResult); - } - } - - private MergeResult mergeWithFastForwardMode(MergeCommand.FastForwardMode fastForwardMode) throws IOException { + private MergeResult mergeWithFastForwardOnlyMode() throws IOException { MergeCommand mergeCommand = getClone().merge(); - mergeCommand.setFastForward(fastForwardMode); + mergeCommand.setFastForward(MergeCommand.FastForwardMode.FF_ONLY); return doMergeInClone(mergeCommand); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index 94c29bdce9..27b4143b41 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -12,6 +12,8 @@ import sonia.scm.repository.api.MergeStrategy; import java.io.IOException; import java.util.Set; +import static org.eclipse.jgit.merge.MergeStrategy.RECURSIVE; + public class GitMergeCommand extends AbstractGitCommand implements MergeCommand { private final GitWorkdirFactory workdirFactory; @@ -33,19 +35,26 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand } private MergeCommandResult mergeWithStrategy(MergeCommandRequest request) { - if (request.getMergeStrategy() == MergeStrategy.SQUASH) { - return inClone(clone -> new GitMergeWithSquash(clone, request, context, repository), workdirFactory, request.getTargetBranch()); - } else if (request.getMergeStrategy() == MergeStrategy.FAST_FORWARD_IF_POSSIBLE) { - return inClone(clone -> new GitFastForwardIfPossible(clone, request, context, repository), workdirFactory, request.getTargetBranch()); + switch(request.getMergeStrategy()) { + case SQUASH: + return inClone(clone -> new GitMergeWithSquash(clone, request, context, repository), workdirFactory, request.getTargetBranch()); + + case FAST_FORWARD_IF_POSSIBLE: + return inClone(clone -> new GitFastForwardIfPossible(clone, request, context, repository), workdirFactory, request.getTargetBranch()); + + case MERGE_COMMIT: + return inClone(clone -> new GitMergeCommit(clone, request, context, repository), workdirFactory, request.getTargetBranch()); + + default: + throw new IllegalArgumentException("unknown merge strategy: " + request.getMergeStrategy()); } - return inClone(clone -> new GitMergeCommit(clone, request, context, repository), workdirFactory, request.getTargetBranch()); } @Override public MergeDryRunCommandResult dryRun(MergeCommandRequest request) { try { Repository repository = context.open(); - ResolveMerger merger = (ResolveMerger) org.eclipse.jgit.merge.MergeStrategy.RECURSIVE.newMerger(repository, true); + ResolveMerger merger = (ResolveMerger) RECURSIVE.newMerger(repository, true); return new MergeDryRunCommandResult( merger.merge( resolveRevisionOrThrowNotFound(repository, request.getBranchToMerge()), diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommit.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommit.java index 627efb9584..6aa68a0ea8 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommit.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommit.java @@ -16,7 +16,7 @@ class GitMergeCommit extends GitMergeStrategy { @Override MergeCommandResult run() throws IOException { - org.eclipse.jgit.api.MergeCommand mergeCommand = getClone().merge(); + MergeCommand mergeCommand = getClone().merge(); mergeCommand.setFastForward(MergeCommand.FastForwardMode.NO_FF); MergeResult result = doMergeInClone(mergeCommand); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeWithSquash.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeWithSquash.java index dfbd31fe32..b688956404 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeWithSquash.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeWithSquash.java @@ -4,6 +4,7 @@ import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.MergeResult; import sonia.scm.repository.Repository; import sonia.scm.repository.api.MergeCommandResult; +import org.eclipse.jgit.api.MergeCommand; import java.io.IOException; @@ -15,7 +16,7 @@ class GitMergeWithSquash extends GitMergeStrategy { @Override MergeCommandResult run() throws IOException { - org.eclipse.jgit.api.MergeCommand mergeCommand = getClone().merge(); + MergeCommand mergeCommand = getClone().merge(); mergeCommand.setSquash(true); MergeResult result = doMergeInClone(mergeCommand); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java index 4f04205202..d3c78ae9da 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java @@ -287,6 +287,29 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { assertThat(mergeCommit.getId()).isEqualTo(featureBranchHead); } + @Test + public void shouldDoMergeCommitIfFastForwardIsNotPossible() throws IOException, GitAPIException { + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setTargetBranch("master"); + request.setBranchToMerge("mergeable"); + request.setMergeStrategy(MergeStrategy.FAST_FORWARD_IF_POSSIBLE); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + MergeCommandResult mergeCommandResult = command.merge(request); + + assertThat(mergeCommandResult.isSuccess()).isTrue(); + + Repository repository = createContext().open(); + Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call(); + RevCommit mergeCommit = commits.iterator().next(); + PersonIdent mergeAuthor = mergeCommit.getAuthorIdent(); + String message = mergeCommit.getFullMessage(); + assertThat(mergeAuthor.getName()).isEqualTo("Dirk Gently"); + assertThat(mergeAuthor.getEmailAddress()).isEqualTo("dirk@holistic.det"); + assertThat(message).contains("master", "mergeable"); + } + @Test(expected = NotFoundException.class) public void shouldHandleNotExistingSourceBranchInMerge() { GitMergeCommand command = createCommand(); From b1216a898b56cd06e9d40b2ea69581ea9042cf1b Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Fri, 8 Nov 2019 11:46:13 +0100 Subject: [PATCH 11/16] remove mergeResource and move dryRun to review-plugin --- scm-core/pom.xml | 6 + .../scm/api/v2/resources/MergeCommandDto.java | 0 .../repository/spi/GitMergeCommandTest.java | 9 + .../scm/api/v2/resources/MergeResource.java | 95 -------- .../RepositoryToRepositoryDtoMapper.java | 6 - .../scm/api/v2/resources/ResourceLinks.java | 20 -- .../api/v2/resources/MergeResourceTest.java | 205 ------------------ 7 files changed, 15 insertions(+), 326 deletions(-) rename {scm-webapp => scm-core}/src/main/java/sonia/scm/api/v2/resources/MergeCommandDto.java (100%) delete mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeResource.java delete mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java diff --git a/scm-core/pom.xml b/scm-core/pom.xml index c7bbc664c9..8437b256b2 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -214,6 +214,12 @@ shiro-unit test + + org.hibernate + hibernate-validator + 5.3.6.Final + compile + diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeCommandDto.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/MergeCommandDto.java similarity index 100% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeCommandDto.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/MergeCommandDto.java diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java index d3c78ae9da..d7e8482f55 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java @@ -64,6 +64,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { MergeCommandRequest request = new MergeCommandRequest(); request.setTargetBranch("master"); request.setBranchToMerge("mergeable"); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); MergeCommandResult mergeCommandResult = command.merge(request); @@ -90,6 +91,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { MergeCommandRequest request = new MergeCommandRequest(); request.setTargetBranch("master"); request.setBranchToMerge("empty_merge"); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); MergeCommandResult mergeCommandResult = command.merge(request); @@ -111,6 +113,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { request.setTargetBranch("master"); request.setBranchToMerge("mergeable"); request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); MergeCommandResult mergeCommandResult = command.merge(request); @@ -134,6 +137,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { MergeCommandRequest request = new MergeCommandRequest(); request.setTargetBranch("master"); request.setBranchToMerge("mergeable"); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); request.setMessageTemplate("simple"); @@ -154,6 +158,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { MergeCommandRequest request = new MergeCommandRequest(); request.setBranchToMerge("test-branch"); request.setTargetBranch("master"); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); MergeCommandResult mergeCommandResult = command.merge(request); @@ -175,6 +180,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { MergeCommandRequest request = new MergeCommandRequest(); request.setTargetBranch("master"); request.setBranchToMerge("mergeable"); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); MergeCommandResult mergeCommandResult = command.merge(request); @@ -194,6 +200,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); request.setTargetBranch("mergeable"); request.setBranchToMerge("master"); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); MergeCommandResult mergeCommandResult = command.merge(request); @@ -316,6 +323,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { MergeCommandRequest request = new MergeCommandRequest(); request.setTargetBranch("mergeable"); request.setBranchToMerge("not_existing"); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); command.merge(request); } @@ -324,6 +332,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { public void shouldHandleNotExistingTargetBranchInMerge() { GitMergeCommand command = createCommand(); MergeCommandRequest request = new MergeCommandRequest(); + request.setMergeStrategy(MergeStrategy.MERGE_COMMIT); request.setTargetBranch("not_existing"); request.setBranchToMerge("master"); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeResource.java deleted file mode 100644 index df59ba2abc..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MergeResource.java +++ /dev/null @@ -1,95 +0,0 @@ -package sonia.scm.api.v2.resources; - -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import lombok.extern.slf4j.Slf4j; -import org.apache.http.HttpStatus; -import sonia.scm.ConcurrentModificationException; -import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.RepositoryPermissions; -import sonia.scm.repository.api.MergeCommandBuilder; -import sonia.scm.repository.api.MergeCommandResult; -import sonia.scm.repository.api.MergeDryRunCommandResult; -import sonia.scm.repository.api.RepositoryService; -import sonia.scm.repository.api.RepositoryServiceFactory; -import sonia.scm.web.VndMediaType; - -import javax.inject.Inject; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Response; - -@Slf4j -public class MergeResource { - - private final RepositoryServiceFactory serviceFactory; - private final MergeResultToDtoMapper mapper; - - @Inject - public MergeResource(RepositoryServiceFactory serviceFactory, MergeResultToDtoMapper mapper) { - this.serviceFactory = serviceFactory; - this.mapper = mapper; - } - - @POST - @Path("") - @Produces(VndMediaType.MERGE_RESULT) - @Consumes(VndMediaType.MERGE_COMMAND) - @StatusCodes({ - @ResponseCode(code = 204, condition = "merge has been executed successfully"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the privilege to write the repository"), - @ResponseCode(code = 409, condition = "The branches could not be merged automatically due to conflicts (conflicting files will be returned)"), - @ResponseCode(code = 500, condition = "internal server error") - }) - public Response merge(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid MergeCommandDto mergeCommand) { - NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); - log.info("Merge in Repository {}/{} from {} to {}", namespace, name, mergeCommand.getSourceRevision(), mergeCommand.getTargetRevision()); - try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { - RepositoryPermissions.push(repositoryService.getRepository()).check(); - MergeCommandResult mergeCommandResult = createMergeCommand(mergeCommand, repositoryService).executeMerge(); - if (mergeCommandResult.isSuccess()) { - return Response.noContent().build(); - } else { - return Response.status(HttpStatus.SC_CONFLICT).entity(mapper.map(mergeCommandResult)).build(); - } - } - } - - @POST - @Path("dry-run/") - @StatusCodes({ - @ResponseCode(code = 204, condition = "merge can be done automatically"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 409, condition = "The branches can not be merged automatically due to conflicts"), - @ResponseCode(code = 500, condition = "internal server error") - }) - public Response dryRun(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid MergeCommandDto mergeCommand) { - - NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); - log.info("Merge in Repository {}/{} from {} to {}", namespace, name, mergeCommand.getSourceRevision(), mergeCommand.getTargetRevision()); - try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { - if (RepositoryPermissions.push(repositoryService.getRepository()).isPermitted()) { - MergeDryRunCommandResult mergeCommandResult = createMergeCommand(mergeCommand, repositoryService).dryRun(); - if (mergeCommandResult.isMergeable()) { - return Response.noContent().build(); - } else { - throw new ConcurrentModificationException("revision", mergeCommand.getTargetRevision()); - } - } else { - return Response.noContent().build(); - } - } - } - - private MergeCommandBuilder createMergeCommand(MergeCommandDto mergeCommand, RepositoryService repositoryService) { - return repositoryService - .getMergeCommand() - .setBranchToMerge(mergeCommand.getSourceRevision()) - .setTargetBranch(mergeCommand.getTargetRevision()); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java index 67a6f26de5..c09f90c83d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java @@ -63,12 +63,6 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper Date: Fri, 8 Nov 2019 13:10:31 +0100 Subject: [PATCH 12/16] fix dependencies after removing mergeResource --- .../sonia/scm/api/v2/resources/RepositoryResource.java | 10 +++------- .../sonia/scm/api/v2/resources/RepositoryTestBase.java | 4 +--- .../sonia/scm/api/v2/resources/ResourceLinksMock.java | 1 - 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java index 1cfc8b332b..2294dc600e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java @@ -42,7 +42,6 @@ public class RepositoryResource { private final Provider diffRootResource; private final Provider modificationsRootResource; private final Provider fileHistoryRootResource; - private final Provider mergeResource; private final Provider incomingRootResource; @Inject @@ -57,8 +56,8 @@ public class RepositoryResource { Provider diffRootResource, Provider modificationsRootResource, Provider fileHistoryRootResource, - Provider incomingRootResource, - Provider mergeResource) { + Provider incomingRootResource + ) { this.dtoToRepositoryMapper = dtoToRepositoryMapper; this.manager = manager; this.repositoryToDtoMapper = repositoryToDtoMapper; @@ -72,8 +71,8 @@ public class RepositoryResource { this.diffRootResource = diffRootResource; this.modificationsRootResource = modificationsRootResource; this.fileHistoryRootResource = fileHistoryRootResource; - this.mergeResource = mergeResource; this.incomingRootResource = incomingRootResource; + } /** @@ -208,9 +207,6 @@ public class RepositoryResource { return incomingRootResource.get(); } - @Path("merge/") - public MergeResource merge() {return mergeResource.get(); } - private Supplier loadBy(String namespace, String name) { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); return () -> Optional.ofNullable(manager.get(namespaceAndName)).orElseThrow(() -> notFound(entity(namespaceAndName))); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTestBase.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTestBase.java index a8901e2d79..8f183f2aa6 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTestBase.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTestBase.java @@ -22,7 +22,6 @@ public abstract class RepositoryTestBase { protected Provider fileHistoryRootResource; protected Provider repositoryCollectionResource; protected Provider incomingRootResource; - protected Provider mergeResource; RepositoryRootResource getRepositoryRootResource() { @@ -39,8 +38,7 @@ public abstract class RepositoryTestBase { diffRootResource, modificationsRootResource, fileHistoryRootResource, - incomingRootResource, - mergeResource)), repositoryCollectionResource); + incomingRootResource)), repositoryCollectionResource); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java index 478b3efc92..672771aadd 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java @@ -45,7 +45,6 @@ public class ResourceLinksMock { when(resourceLinks.uiPlugin()).thenReturn(new ResourceLinks.UIPluginLinks(uriInfo)); when(resourceLinks.authentication()).thenReturn(new ResourceLinks.AuthenticationLinks(uriInfo)); when(resourceLinks.index()).thenReturn(new ResourceLinks.IndexLinks(uriInfo)); - when(resourceLinks.merge()).thenReturn(new ResourceLinks.MergeLinks(uriInfo)); when(resourceLinks.permissions()).thenReturn(new ResourceLinks.PermissionsLinks(uriInfo)); when(resourceLinks.repositoryVerbs()).thenReturn(new ResourceLinks.RepositoryVerbLinks(uriInfo)); when(resourceLinks.repositoryRole()).thenReturn(new ResourceLinks.RepositoryRoleLinks(uriInfo)); From 8e0026962d65c8814beb9394390ceeca98232558 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Fri, 8 Nov 2019 14:10:27 +0100 Subject: [PATCH 13/16] Fix typo --- .../main/java/sonia/scm/repository/spi/GitMergeStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java index d4d9de3de7..1d53b99c99 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeStrategy.java @@ -66,7 +66,7 @@ abstract class GitMergeStrategy extends AbstractGitCommand.GitCloneWorker Date: Fri, 8 Nov 2019 15:43:51 +0100 Subject: [PATCH 14/16] Secure test --- .../test/java/sonia/scm/repository/spi/GitMergeCommandTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java index d7e8482f55..fcd721c3a2 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java @@ -289,6 +289,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call(); RevCommit mergeCommit = commits.iterator().next(); + assertThat(mergeCommit.getParentCount()).isEqualTo(1); PersonIdent mergeAuthor = mergeCommit.getAuthorIdent(); assertThat(mergeAuthor.getName()).isEqualTo("Philip J Fry"); assertThat(mergeCommit.getId()).isEqualTo(featureBranchHead); @@ -311,6 +312,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call(); RevCommit mergeCommit = commits.iterator().next(); PersonIdent mergeAuthor = mergeCommit.getAuthorIdent(); + assertThat(mergeCommit.getParentCount()).isEqualTo(2); String message = mergeCommit.getFullMessage(); assertThat(mergeAuthor.getName()).isEqualTo("Dirk Gently"); assertThat(mergeAuthor.getEmailAddress()).isEqualTo("dirk@holistic.det"); From ae32b6f7971cd4594ab2d271ab398665e98c4092 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Fri, 8 Nov 2019 15:44:37 +0100 Subject: [PATCH 15/16] Use dedicated exception for unsupported merge strategies --- .../MergeStrategyNotSupportedException.java | 27 +++++++++++++++++++ .../scm/repository/spi/GitMergeCommand.java | 3 ++- .../main/resources/locales/de/plugins.json | 4 +++ .../main/resources/locales/en/plugins.json | 4 +++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/api/MergeStrategyNotSupportedException.java diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategyNotSupportedException.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategyNotSupportedException.java new file mode 100644 index 0000000000..ae9fbb08b4 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategyNotSupportedException.java @@ -0,0 +1,27 @@ +package sonia.scm.repository.api; + +import sonia.scm.BadRequestException; +import sonia.scm.repository.Repository; + +import static sonia.scm.ContextEntry.ContextBuilder.entity; + +@SuppressWarnings("squid:MaximumInheritanceDepth") // exceptions have a deep inheritance depth themselves; therefore we accept this here +public class MergeStrategyNotSupportedException extends BadRequestException { + + private static final long serialVersionUID = 256498734456613496L; + + private static final String CODE = "6eRhF9gU41"; + + public MergeStrategyNotSupportedException(Repository repository, MergeStrategy strategy) { + super(entity(repository).build(), createMessage(strategy)); + } + + @Override + public String getCode() { + return CODE; + } + + private static String createMessage(MergeStrategy strategy) { + return "merge strategy " + strategy + " is not supported by this repository"; + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index 27b4143b41..226dd0f285 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -8,6 +8,7 @@ import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.api.MergeCommandResult; import sonia.scm.repository.api.MergeDryRunCommandResult; import sonia.scm.repository.api.MergeStrategy; +import sonia.scm.repository.api.MergeStrategyNotSupportedException; import java.io.IOException; import java.util.Set; @@ -46,7 +47,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand return inClone(clone -> new GitMergeCommit(clone, request, context, repository), workdirFactory, request.getTargetBranch()); default: - throw new IllegalArgumentException("unknown merge strategy: " + request.getMergeStrategy()); + throw new MergeStrategyNotSupportedException(repository, request.getMergeStrategy()); } } diff --git a/scm-webapp/src/main/resources/locales/de/plugins.json b/scm-webapp/src/main/resources/locales/de/plugins.json index 2e36e60312..fe97d296c4 100644 --- a/scm-webapp/src/main/resources/locales/de/plugins.json +++ b/scm-webapp/src/main/resources/locales/de/plugins.json @@ -183,6 +183,10 @@ "65RdZ5atX1": { "displayName": "Fehler beim Löschen von Plugin-Dateien", "description": "Einige Dateien für die Plugin-Deinstallation konnten nicht gelöscht werden. Dieses kann zu Inkonsistenzen führen, so dass der SCM-Manager nicht mehr korrekt starten kann. Bitte prüfen Sie die Logs und bereinigen Sie das Plugin-Verzeichnis des SCM-Managers manuell. Um die Installation eines Plugins abzubrechen, löschen Sie die zugehörige smp Datei aus dem Plugin-Verzeichnis. Um ein Entfernen eines Plugins zu verhindern, entfernen Sie die Datei namens 'uninstall' aus dem entsprechenden Verzeichnis des Plugins." + }, + "6eRhF9gU41": { + "displayName": "Nicht unterstützte Merge-Strategie", + "description": "Die gewählte Merge-Strategie wird von dem Repository nicht unterstützt." } }, "namespaceStrategies": { diff --git a/scm-webapp/src/main/resources/locales/en/plugins.json b/scm-webapp/src/main/resources/locales/en/plugins.json index 11643c4a70..27171374c9 100644 --- a/scm-webapp/src/main/resources/locales/en/plugins.json +++ b/scm-webapp/src/main/resources/locales/en/plugins.json @@ -183,6 +183,10 @@ "65RdZ5atX1": { "displayName": "Error removing plugin files", "description": "Some files to cancel the plugin (un)installation could not be deleted. This can lead to inconsistencies so that the SCM-Manager cannot restart properly. Please check the logs and clean up the plugin folder manually. To cancel the installation of a plugin, remove the corresponding smp file. To cancel the uninstallation, remove the file named 'uninstall' inside the directory for this plugin." + }, + "6eRhF9gU41": { + "displayName": "Merge strategy not supported", + "description": "The selected merge strategy is not supported by the repository." } }, "namespaceStrategies": { From 520c0d6ecf35a9971b8603741f4190e8b00a0ff7 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Fri, 8 Nov 2019 15:41:45 +0000 Subject: [PATCH 16/16] Close branch feature/merge_with_squash