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 4310e63733..8e43da1d82 100644 Binary files a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip and b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip differ