From 93cec3d28208c9d568c6b1e77512a85aa96f62f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 28 Mar 2019 16:15:31 +0100 Subject: [PATCH] Add parameter for parent of new branch --- .../repository/api/BranchCommandBuilder.java | 13 ++++++---- .../scm/repository/api/BranchRequest.java | 22 +++++++++++++++++ .../repository/api/HookChangesetBuilder.java | 5 ++-- .../scm/repository/spi/BranchCommand.java | 3 ++- .../main/java/sonia/scm/web/VndMediaType.java | 1 + .../scm/repository/spi/GitBranchCommand.java | 14 +++++++---- .../repository/spi/GitBranchCommandTest.java | 6 ++++- .../scm/repository/spi/HgBranchCommand.java | 17 +++++++++---- .../repository/spi/HgBranchCommandTest.java | 9 +++++-- .../api/v2/resources/BranchRequestDto.java | 19 +++++++++++++++ .../api/v2/resources/BranchRootResource.java | 24 +++++++++++++------ 11 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/api/BranchRequest.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRequestDto.java diff --git a/scm-core/src/main/java/sonia/scm/repository/api/BranchCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/BranchCommandBuilder.java index 09860d1514..aa8485efca 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/BranchCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/BranchCommandBuilder.java @@ -44,17 +44,20 @@ import java.io.IOException; */ public final class BranchCommandBuilder { - private static final Logger LOG = LoggerFactory.getLogger(BranchCommandBuilder.class); - public BranchCommandBuilder(BranchCommand command) { this.command = command; } - public Branch branch(String name) throws IOException { - LOG.debug("branch {}", name); + public BranchCommandBuilder from(String parentBranch) { + request.setParentBranch(parentBranch); + return this; + } - return command.branch(name); + public Branch branch(String name) throws IOException { + request.setNewBranch(name); + return command.branch(request); } private BranchCommand command; + private BranchRequest request = new BranchRequest(); } diff --git a/scm-core/src/main/java/sonia/scm/repository/api/BranchRequest.java b/scm-core/src/main/java/sonia/scm/repository/api/BranchRequest.java new file mode 100644 index 0000000000..8473567156 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/api/BranchRequest.java @@ -0,0 +1,22 @@ +package sonia.scm.repository.api; + +public class BranchRequest { + private String parentBranch; + private String newBranch; + + public String getParentBranch() { + return parentBranch; + } + + public void setParentBranch(String parentBranch) { + this.parentBranch = parentBranch; + } + + public String getNewBranch() { + return newBranch; + } + + public void setNewBranch(String newBranch) { + this.newBranch = newBranch; + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookChangesetBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/HookChangesetBuilder.java index fe43fee038..7e016db430 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/HookChangesetBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/HookChangesetBuilder.java @@ -46,6 +46,7 @@ import sonia.scm.repository.PreProcessorUtil; import sonia.scm.repository.Repository; import sonia.scm.repository.spi.HookChangesetProvider; import sonia.scm.repository.spi.HookChangesetRequest; +import sonia.scm.repository.spi.HookChangesetResponse; //~--- JDK imports ------------------------------------------------------------ @@ -115,8 +116,8 @@ public final class HookChangesetBuilder */ public Iterable getChangesets() { - Iterable changesets = - provider.handleRequest(request).getChangesets(); + HookChangesetResponse hookChangesetResponse = provider.handleRequest(request); + Iterable changesets = hookChangesetResponse.getChangesets(); if (!disablePreProcessors) { diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/BranchCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/BranchCommand.java index cbce371f4a..181e373382 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/BranchCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/BranchCommand.java @@ -34,6 +34,7 @@ package sonia.scm.repository.spi; import sonia.scm.repository.Branch; +import sonia.scm.repository.api.BranchRequest; import java.io.IOException; @@ -41,5 +42,5 @@ import java.io.IOException; * @since 2.0 */ public interface BranchCommand { - Branch branch(String name) throws IOException; + Branch branch(BranchRequest name) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java index d0a64b17ec..4dcb2f1d96 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -27,6 +27,7 @@ public class VndMediaType { public static final String TAG = PREFIX + "tag" + SUFFIX; public static final String TAG_COLLECTION = PREFIX + "tagCollection" + SUFFIX; public static final String BRANCH = PREFIX + "branch" + SUFFIX; + public static final String BRANCH_REQUEST = PREFIX + "branchRequest" + SUFFIX; public static final String DIFF = PLAIN_TEXT_PREFIX + "diff" + PLAIN_TEXT_SUFFIX; public static final String USER_COLLECTION = PREFIX + "userCollection" + SUFFIX; public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchCommand.java index 42667f6e51..2040cd24be 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchCommand.java @@ -42,6 +42,7 @@ import sonia.scm.repository.GitUtil; import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Repository; +import sonia.scm.repository.api.BranchRequest; import sonia.scm.repository.util.WorkingCopy; import java.util.stream.StreamSupport; @@ -56,19 +57,22 @@ public class GitBranchCommand extends AbstractGitCommand implements BranchComman } @Override - public Branch branch(String name) { + public Branch branch(BranchRequest request) { try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(context)) { Git clone = new Git(workingCopy.get()); - Ref ref = clone.branchCreate().setName(name).call(); - Iterable call = clone.push().add(name).call(); + if (request.getParentBranch() != null) { + clone.checkout().setName(request.getParentBranch()); + } + Ref ref = clone.branchCreate().setName(request.getNewBranch()).call(); + Iterable call = clone.push().add(request.getNewBranch()).call(); StreamSupport.stream(call.spliterator(), false) .flatMap(pushResult -> pushResult.getRemoteUpdates().stream()) .filter(remoteRefUpdate -> remoteRefUpdate.getStatus() != RemoteRefUpdate.Status.OK) .findFirst() .ifPresent(this::handlePushError); - return Branch.normalBranch(name, GitUtil.getId(ref.getObjectId())); + return Branch.normalBranch(request.getNewBranch(), GitUtil.getId(ref.getObjectId())); } catch (GitAPIException ex) { - throw new InternalRepositoryException(repository, "could not create branch " + name, ex); + throw new InternalRepositoryException(repository, "could not create branch " + request.getNewBranch(), ex); } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBranchCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBranchCommandTest.java index 83f211fed8..364306c033 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBranchCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBranchCommandTest.java @@ -4,6 +4,7 @@ import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import sonia.scm.repository.Branch; +import sonia.scm.repository.api.BranchRequest; import java.io.IOException; import java.util.List; @@ -20,7 +21,10 @@ public class GitBranchCommandTest extends AbstractGitCommandTestBase { Assertions.assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isEmpty(); - new GitBranchCommand(context, repository, new SimpleGitWorkdirFactory()).branch("new_branch"); + BranchRequest branchRequest = new BranchRequest(); + branchRequest.setNewBranch("new_branch"); + + new GitBranchCommand(context, repository, new SimpleGitWorkdirFactory()).branch(branchRequest); Assertions.assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty(); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBranchCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBranchCommand.java index 0d69e5bc75..c4a10ce19f 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBranchCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBranchCommand.java @@ -32,12 +32,16 @@ package sonia.scm.repository.spi; import com.aragost.javahg.Changeset; import com.aragost.javahg.commands.CommitCommand; +import com.aragost.javahg.commands.UpdateCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.Branch; import sonia.scm.repository.Repository; +import sonia.scm.repository.api.BranchRequest; import sonia.scm.repository.util.WorkingCopy; +import java.io.IOException; + /** * Mercurial implementation of the {@link BranchCommand}. * Note that this creates an empty commit to "persist" the new branch. @@ -54,21 +58,24 @@ public class HgBranchCommand extends AbstractCommand implements BranchCommand { } @Override - public Branch branch(String name) { + public Branch branch(BranchRequest request) throws IOException { try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(getContext())) { com.aragost.javahg.Repository repository = workingCopy.get().get(); - com.aragost.javahg.commands.BranchCommand.on(repository).set(name); + if (request.getParentBranch() != null) { + UpdateCommand.on(repository).rev(request.getParentBranch()).execute(); + } + com.aragost.javahg.commands.BranchCommand.on(repository).set(request.getNewBranch()); Changeset emptyChangeset = CommitCommand .on(repository) .user("SCM-Manager") - .message("Create new branch " + name) + .message("Create new branch " + request.getNewBranch()) .execute(); LOG.debug("Created new branch '{}' in repository {} with changeset {}", - name, getRepository().getNamespaceAndName(), emptyChangeset.getNode()); + request.getNewBranch(), getRepository().getNamespaceAndName(), emptyChangeset.getNode()); - return Branch.normalBranch(name, emptyChangeset.getNode()); + return Branch.normalBranch(request.getNewBranch(), emptyChangeset.getNode()); } } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgBranchCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgBranchCommandTest.java index be803d4ef2..34f1f5994d 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgBranchCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgBranchCommandTest.java @@ -5,13 +5,15 @@ import org.assertj.core.api.Assertions; import org.junit.Test; import sonia.scm.repository.Branch; import sonia.scm.repository.HgTestUtil; +import sonia.scm.repository.api.BranchRequest; import sonia.scm.web.HgRepositoryEnvironmentBuilder; +import java.io.IOException; import java.util.List; public class HgBranchCommandTest extends AbstractHgCommandTestBase { @Test - public void shouldCreateBranch() { + public void shouldCreateBranch() throws IOException { Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isEmpty(); HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder = @@ -19,7 +21,10 @@ public class HgBranchCommandTest extends AbstractHgCommandTestBase { SimpleHgWorkdirFactory workdirFactory = new SimpleHgWorkdirFactory(Providers.of(hgRepositoryEnvironmentBuilder), pc -> {}); - new HgBranchCommand(cmdContext, repository, workdirFactory).branch("new_branch"); + BranchRequest branchRequest = new BranchRequest(); + branchRequest.setNewBranch("new_branch"); + + new HgBranchCommand(cmdContext, repository, workdirFactory).branch(branchRequest); Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty(); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRequestDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRequestDto.java new file mode 100644 index 0000000000..3db338ea85 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRequestDto.java @@ -0,0 +1,19 @@ +package sonia.scm.api.v2.resources; + +import lombok.Getter; +import lombok.Setter; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.validation.constraints.Pattern; + +import static sonia.scm.api.v2.resources.BranchDto.VALID_BRANCH_NAMES; + +@Getter +@Setter +public class BranchRequestDto { + + @NotEmpty @Length(min = 1, max=100) @Pattern(regexp = VALID_BRANCH_NAMES) + private String name; + private String parent; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java index b35b59beca..f0edbf79fb 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import com.google.common.base.Strings; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; @@ -13,6 +14,7 @@ import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryPermissions; +import sonia.scm.repository.api.BranchCommandBuilder; import sonia.scm.repository.api.CommandNotSupportedException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -132,14 +134,14 @@ public class BranchRootResource { /** * Creates a new branch. * - * @param namespace the namespace of the repository - * @param name the name of the repository - * @param branchName The branch to be created. + * @param namespace the namespace of the repository + * @param name the name of the repository + * @param branchRequest the request giving the name of the new branch and an optional parent branch * @return A response with the link to the new branch (if created successfully). */ @POST @Path("") - @Consumes(VndMediaType.BRANCH) + @Consumes(VndMediaType.BRANCH_REQUEST) @StatusCodes({ @ResponseCode(code = 201, condition = "create success"), @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), @@ -151,16 +153,24 @@ public class BranchRootResource { @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created branch")) public Response create(@PathParam("namespace") String namespace, @PathParam("name") String name, - @Valid BranchDto branchToCreate) throws IOException { + @Valid BranchRequestDto branchRequest) throws IOException { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); - String branchName = branchToCreate.getName(); + String branchName = branchRequest.getName(); + String parentName = branchRequest.getParent(); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { if (branchExists(branchName, repositoryService)) { throw alreadyExists(entity(Branch.class, branchName).in(Repository.class, namespace + "/" + name)); } Repository repository = repositoryService.getRepository(); RepositoryPermissions.push(repository).check(); - Branch newBranch = repositoryService.getBranchCommand().branch(branchName); + BranchCommandBuilder branchCommand = repositoryService.getBranchCommand(); + if (!Strings.isNullOrEmpty(parentName)) { + if (!branchExists(parentName, repositoryService)) { + throw notFound(entity(Branch.class, parentName).in(Repository.class, namespace + "/" + name)); + } + branchCommand.from(parentName); + } + Branch newBranch = branchCommand.branch(branchName); return Response.created(URI.create(resourceLinks.branch().self(namespaceAndName, newBranch.getName()))).build(); } }