Add parameter for parent of new branch

This commit is contained in:
René Pfeuffer
2019-03-28 16:15:31 +01:00
parent 81e493ddf0
commit 93cec3d282
11 changed files with 105 additions and 28 deletions

View File

@@ -44,17 +44,20 @@ import java.io.IOException;
*/ */
public final class BranchCommandBuilder { public final class BranchCommandBuilder {
private static final Logger LOG = LoggerFactory.getLogger(BranchCommandBuilder.class);
public BranchCommandBuilder(BranchCommand command) { public BranchCommandBuilder(BranchCommand command) {
this.command = command; this.command = command;
} }
public Branch branch(String name) throws IOException { public BranchCommandBuilder from(String parentBranch) {
LOG.debug("branch {}", name); 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 BranchCommand command;
private BranchRequest request = new BranchRequest();
} }

View File

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

View File

@@ -46,6 +46,7 @@ import sonia.scm.repository.PreProcessorUtil;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.spi.HookChangesetProvider; import sonia.scm.repository.spi.HookChangesetProvider;
import sonia.scm.repository.spi.HookChangesetRequest; import sonia.scm.repository.spi.HookChangesetRequest;
import sonia.scm.repository.spi.HookChangesetResponse;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
@@ -115,8 +116,8 @@ public final class HookChangesetBuilder
*/ */
public Iterable<Changeset> getChangesets() public Iterable<Changeset> getChangesets()
{ {
Iterable<Changeset> changesets = HookChangesetResponse hookChangesetResponse = provider.handleRequest(request);
provider.handleRequest(request).getChangesets(); Iterable<Changeset> changesets = hookChangesetResponse.getChangesets();
if (!disablePreProcessors) if (!disablePreProcessors)
{ {

View File

@@ -34,6 +34,7 @@ package sonia.scm.repository.spi;
import sonia.scm.repository.Branch; import sonia.scm.repository.Branch;
import sonia.scm.repository.api.BranchRequest;
import java.io.IOException; import java.io.IOException;
@@ -41,5 +42,5 @@ import java.io.IOException;
* @since 2.0 * @since 2.0
*/ */
public interface BranchCommand { public interface BranchCommand {
Branch branch(String name) throws IOException; Branch branch(BranchRequest name) throws IOException;
} }

View File

@@ -27,6 +27,7 @@ public class VndMediaType {
public static final String TAG = PREFIX + "tag" + SUFFIX; public static final String TAG = PREFIX + "tag" + SUFFIX;
public static final String TAG_COLLECTION = PREFIX + "tagCollection" + SUFFIX; public static final String TAG_COLLECTION = PREFIX + "tagCollection" + SUFFIX;
public static final String BRANCH = PREFIX + "branch" + 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 DIFF = PLAIN_TEXT_PREFIX + "diff" + PLAIN_TEXT_SUFFIX;
public static final String USER_COLLECTION = PREFIX + "userCollection" + SUFFIX; public static final String USER_COLLECTION = PREFIX + "userCollection" + SUFFIX;
public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX; public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX;

View File

@@ -42,6 +42,7 @@ import sonia.scm.repository.GitUtil;
import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.GitWorkdirFactory;
import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.api.BranchRequest;
import sonia.scm.repository.util.WorkingCopy; import sonia.scm.repository.util.WorkingCopy;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
@@ -56,19 +57,22 @@ public class GitBranchCommand extends AbstractGitCommand implements BranchComman
} }
@Override @Override
public Branch branch(String name) { public Branch branch(BranchRequest request) {
try (WorkingCopy<org.eclipse.jgit.lib.Repository> workingCopy = workdirFactory.createWorkingCopy(context)) { try (WorkingCopy<org.eclipse.jgit.lib.Repository> workingCopy = workdirFactory.createWorkingCopy(context)) {
Git clone = new Git(workingCopy.get()); Git clone = new Git(workingCopy.get());
Ref ref = clone.branchCreate().setName(name).call(); if (request.getParentBranch() != null) {
Iterable<PushResult> call = clone.push().add(name).call(); clone.checkout().setName(request.getParentBranch());
}
Ref ref = clone.branchCreate().setName(request.getNewBranch()).call();
Iterable<PushResult> call = clone.push().add(request.getNewBranch()).call();
StreamSupport.stream(call.spliterator(), false) StreamSupport.stream(call.spliterator(), false)
.flatMap(pushResult -> pushResult.getRemoteUpdates().stream()) .flatMap(pushResult -> pushResult.getRemoteUpdates().stream())
.filter(remoteRefUpdate -> remoteRefUpdate.getStatus() != RemoteRefUpdate.Status.OK) .filter(remoteRefUpdate -> remoteRefUpdate.getStatus() != RemoteRefUpdate.Status.OK)
.findFirst() .findFirst()
.ifPresent(this::handlePushError); .ifPresent(this::handlePushError);
return Branch.normalBranch(name, GitUtil.getId(ref.getObjectId())); return Branch.normalBranch(request.getNewBranch(), GitUtil.getId(ref.getObjectId()));
} catch (GitAPIException ex) { } catch (GitAPIException ex) {
throw new InternalRepositoryException(repository, "could not create branch " + name, ex); throw new InternalRepositoryException(repository, "could not create branch " + request.getNewBranch(), ex);
} }
} }

View File

@@ -4,6 +4,7 @@ import org.assertj.core.api.Assertions;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import sonia.scm.repository.Branch; import sonia.scm.repository.Branch;
import sonia.scm.repository.api.BranchRequest;
import java.io.IOException; import java.io.IOException;
import java.util.List; 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(); 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(); Assertions.assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
} }

View File

@@ -32,12 +32,16 @@ package sonia.scm.repository.spi;
import com.aragost.javahg.Changeset; import com.aragost.javahg.Changeset;
import com.aragost.javahg.commands.CommitCommand; import com.aragost.javahg.commands.CommitCommand;
import com.aragost.javahg.commands.UpdateCommand;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.repository.Branch; import sonia.scm.repository.Branch;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.api.BranchRequest;
import sonia.scm.repository.util.WorkingCopy; import sonia.scm.repository.util.WorkingCopy;
import java.io.IOException;
/** /**
* Mercurial implementation of the {@link BranchCommand}. * Mercurial implementation of the {@link BranchCommand}.
* Note that this creates an empty commit to "persist" the new branch. * Note that this creates an empty commit to "persist" the new branch.
@@ -54,21 +58,24 @@ public class HgBranchCommand extends AbstractCommand implements BranchCommand {
} }
@Override @Override
public Branch branch(String name) { public Branch branch(BranchRequest request) throws IOException {
try (WorkingCopy<RepositoryCloseableWrapper> workingCopy = workdirFactory.createWorkingCopy(getContext())) { try (WorkingCopy<RepositoryCloseableWrapper> workingCopy = workdirFactory.createWorkingCopy(getContext())) {
com.aragost.javahg.Repository repository = workingCopy.get().get(); 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 Changeset emptyChangeset = CommitCommand
.on(repository) .on(repository)
.user("SCM-Manager") .user("SCM-Manager")
.message("Create new branch " + name) .message("Create new branch " + request.getNewBranch())
.execute(); .execute();
LOG.debug("Created new branch '{}' in repository {} with changeset {}", 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());
} }
} }
} }

View File

@@ -5,13 +5,15 @@ import org.assertj.core.api.Assertions;
import org.junit.Test; import org.junit.Test;
import sonia.scm.repository.Branch; import sonia.scm.repository.Branch;
import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.HgTestUtil;
import sonia.scm.repository.api.BranchRequest;
import sonia.scm.web.HgRepositoryEnvironmentBuilder; import sonia.scm.web.HgRepositoryEnvironmentBuilder;
import java.io.IOException;
import java.util.List; import java.util.List;
public class HgBranchCommandTest extends AbstractHgCommandTestBase { public class HgBranchCommandTest extends AbstractHgCommandTestBase {
@Test @Test
public void shouldCreateBranch() { public void shouldCreateBranch() throws IOException {
Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isEmpty(); Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isEmpty();
HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder = HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder =
@@ -19,7 +21,10 @@ public class HgBranchCommandTest extends AbstractHgCommandTestBase {
SimpleHgWorkdirFactory workdirFactory = new SimpleHgWorkdirFactory(Providers.of(hgRepositoryEnvironmentBuilder), pc -> {}); 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(); Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
} }

View File

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

View File

@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources; 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.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; 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.NamespaceAndName;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.RepositoryPermissions;
import sonia.scm.repository.api.BranchCommandBuilder;
import sonia.scm.repository.api.CommandNotSupportedException; import sonia.scm.repository.api.CommandNotSupportedException;
import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.repository.api.RepositoryServiceFactory;
@@ -134,12 +136,12 @@ public class BranchRootResource {
* *
* @param namespace the namespace of the repository * @param namespace the namespace of the repository
* @param name the name of the repository * @param name the name of the repository
* @param branchName The branch to be created. * @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). * @return A response with the link to the new branch (if created successfully).
*/ */
@POST @POST
@Path("") @Path("")
@Consumes(VndMediaType.BRANCH) @Consumes(VndMediaType.BRANCH_REQUEST)
@StatusCodes({ @StatusCodes({
@ResponseCode(code = 201, condition = "create success"), @ResponseCode(code = 201, condition = "create success"),
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), @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")) @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created branch"))
public Response create(@PathParam("namespace") String namespace, public Response create(@PathParam("namespace") String namespace,
@PathParam("name") String name, @PathParam("name") String name,
@Valid BranchDto branchToCreate) throws IOException { @Valid BranchRequestDto branchRequest) throws IOException {
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
String branchName = branchToCreate.getName(); String branchName = branchRequest.getName();
String parentName = branchRequest.getParent();
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
if (branchExists(branchName, repositoryService)) { if (branchExists(branchName, repositoryService)) {
throw alreadyExists(entity(Branch.class, branchName).in(Repository.class, namespace + "/" + name)); throw alreadyExists(entity(Branch.class, branchName).in(Repository.class, namespace + "/" + name));
} }
Repository repository = repositoryService.getRepository(); Repository repository = repositoryService.getRepository();
RepositoryPermissions.push(repository).check(); 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(); return Response.created(URI.create(resourceLinks.branch().self(namespaceAndName, newBranch.getName()))).build();
} }
} }