Merge with 2.0.0-m3

This commit is contained in:
Rene Pfeuffer
2019-11-12 08:51:28 +01:00
15 changed files with 282 additions and 65 deletions

View File

@@ -1,12 +0,0 @@
package sonia.scm.api.v2.resources;
import lombok.Getter;
import lombok.Setter;
import java.util.Collection;
@Getter
@Setter
public class MergeResultDto {
private Collection<String> filesWithConflict;
}

View File

@@ -1,9 +0,0 @@
package sonia.scm.api.v2.resources;
import org.mapstruct.Mapper;
import sonia.scm.repository.api.MergeCommandResult;
@Mapper
public interface MergeResultToDtoMapper {
MergeResultDto map(MergeCommandResult result);
}

View File

@@ -68,6 +68,10 @@ public final class BranchCommandBuilder {
return command.branch(request); return command.branch(request);
} }
public void delete(String branchName) {
command.deleteOrClose(branchName);
}
private BranchCommand command; private BranchCommand command;
private BranchRequest request = new BranchRequest(); private BranchRequest request = new BranchRequest();
} }

View File

@@ -41,4 +41,6 @@ import sonia.scm.repository.api.BranchRequest;
*/ */
public interface BranchCommand { public interface BranchCommand {
Branch branch(BranchRequest name); Branch branch(BranchRequest name);
void deleteOrClose(String branchName);
} }

View File

@@ -0,0 +1,19 @@
package sonia.scm.repository.spi;
import sonia.scm.ContextEntry;
import sonia.scm.ExceptionWithContext;
import sonia.scm.repository.Repository;
public class CannotDeleteDefaultBranchException extends ExceptionWithContext {
public static final String CODE = "78RhWxTIw1";
public CannotDeleteDefaultBranchException(Repository repository, String branchName) {
super(ContextEntry.ContextBuilder.entity("Branch", branchName).in(repository).build(), "default branch cannot be deleted");
}
@Override
public String getCode() {
return CODE;
}
}

View File

@@ -33,51 +33,120 @@
package sonia.scm.repository.spi; package sonia.scm.repository.spi;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException;
import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.PushResult; import sonia.scm.event.ScmEventBus;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import sonia.scm.repository.Branch; import sonia.scm.repository.Branch;
import sonia.scm.repository.GitUtil; import sonia.scm.repository.GitUtil;
import sonia.scm.repository.GitWorkdirFactory;
import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.PostReceiveRepositoryHookEvent;
import sonia.scm.repository.PreReceiveRepositoryHookEvent;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryHookEvent;
import sonia.scm.repository.RepositoryHookType;
import sonia.scm.repository.api.BranchRequest; import sonia.scm.repository.api.BranchRequest;
import sonia.scm.repository.util.WorkingCopy; import sonia.scm.repository.api.HookBranchProvider;
import sonia.scm.repository.api.HookContext;
import sonia.scm.repository.api.HookContextFactory;
import sonia.scm.repository.api.HookFeature;
import java.util.stream.StreamSupport; import java.io.IOException;
import java.util.List;
import java.util.Set;
import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
public class GitBranchCommand extends AbstractGitCommand implements BranchCommand { public class GitBranchCommand extends AbstractGitCommand implements BranchCommand {
private final GitWorkdirFactory workdirFactory; private final HookContextFactory hookContextFactory;
private final ScmEventBus eventBus;
GitBranchCommand(GitContext context, Repository repository, GitWorkdirFactory workdirFactory) { GitBranchCommand(GitContext context, Repository repository, HookContextFactory hookContextFactory, ScmEventBus eventBus) {
super(context, repository); super(context, repository);
this.workdirFactory = workdirFactory; this.hookContextFactory = hookContextFactory;
this.eventBus = eventBus;
} }
@Override @Override
public Branch branch(BranchRequest request) { public Branch branch(BranchRequest request) {
try (WorkingCopy<org.eclipse.jgit.lib.Repository, org.eclipse.jgit.lib.Repository> workingCopy = workdirFactory.createWorkingCopy(context, request.getParentBranch())) { try (Git git = new Git(context.open())) {
Git clone = new Git(workingCopy.getWorkingRepository()); RepositoryHookEvent hookEvent = createBranchHookEvent(BranchHookContextProvider.createHookEvent(request.getNewBranch()));
Ref ref = clone.branchCreate().setName(request.getNewBranch()).call(); eventBus.post(new PreReceiveRepositoryHookEvent(hookEvent));
Iterable<PushResult> call = clone.push().add(request.getNewBranch()).call(); Ref ref = git.branchCreate().setStartPoint(request.getParentBranch()).setName(request.getNewBranch()).call();
StreamSupport.stream(call.spliterator(), false) eventBus.post(new PostReceiveRepositoryHookEvent(hookEvent));
.flatMap(pushResult -> pushResult.getRemoteUpdates().stream())
.filter(remoteRefUpdate -> remoteRefUpdate.getStatus() != RemoteRefUpdate.Status.OK)
.findFirst()
.ifPresent(r -> this.handlePushError(r, request, context.getRepository()));
return Branch.normalBranch(request.getNewBranch(), GitUtil.getId(ref.getObjectId())); return Branch.normalBranch(request.getNewBranch(), GitUtil.getId(ref.getObjectId()));
} catch (GitAPIException ex) { } catch (GitAPIException | IOException ex) {
throw new InternalRepositoryException(repository, "could not create branch " + request.getNewBranch(), ex); throw new InternalRepositoryException(repository, "could not create branch " + request.getNewBranch(), ex);
} }
} }
private void handlePushError(RemoteRefUpdate remoteRefUpdate, BranchRequest request, Repository repository) { @Override
if (remoteRefUpdate.getStatus() != RemoteRefUpdate.Status.OK) { public void deleteOrClose(String branchName) {
// TODO handle failed remote update try (Git gitRepo = new Git(context.open())) {
throw new IntegrateChangesFromWorkdirException(repository, RepositoryHookEvent hookEvent = createBranchHookEvent(BranchHookContextProvider.deleteHookEvent(branchName));
String.format("Could not push new branch '%s' into central repository", request.getNewBranch())); eventBus.post(new PreReceiveRepositoryHookEvent(hookEvent));
gitRepo
.branchDelete()
.setBranchNames(branchName)
.setForce(true)
.call();
eventBus.post(new PostReceiveRepositoryHookEvent(hookEvent));
} catch (CannotDeleteCurrentBranchException e) {
throw new CannotDeleteDefaultBranchException(context.getRepository(), branchName);
} catch (GitAPIException | IOException ex) {
throw new InternalRepositoryException(entity(context.getRepository()), String.format("Could not delete branch: %s", branchName));
}
}
private RepositoryHookEvent createBranchHookEvent(BranchHookContextProvider hookEvent) {
HookContext context = hookContextFactory.createContext(hookEvent, this.context.getRepository());
return new RepositoryHookEvent(context, this.context.getRepository(), RepositoryHookType.PRE_RECEIVE);
}
private static class BranchHookContextProvider extends HookContextProvider {
private final List<String> newBranches;
private final List<String> deletedBranches;
private BranchHookContextProvider(List<String> newBranches, List<String> deletedBranches) {
this.newBranches = newBranches;
this.deletedBranches = deletedBranches;
}
static BranchHookContextProvider createHookEvent(String newBranch) {
return new BranchHookContextProvider(singletonList(newBranch), emptyList());
}
static BranchHookContextProvider deleteHookEvent(String deletedBranch) {
return new BranchHookContextProvider(emptyList(), singletonList(deletedBranch));
}
@Override
public Set<HookFeature> getSupportedFeatures() {
return singleton(HookFeature.BRANCH_PROVIDER);
}
@Override
public HookBranchProvider getBranchProvider() {
return new HookBranchProvider() {
@Override
public List<String> getCreatedOrModified() {
return newBranches;
}
@Override
public List<String> getDeletedOrClosed() {
return deletedBranches;
}
};
}
@Override
public HookChangesetProvider getChangesetProvider() {
return r -> new HookChangesetResponse(emptyList());
} }
} }
} }

View File

@@ -35,10 +35,12 @@ package sonia.scm.repository.spi;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
import sonia.scm.event.ScmEventBus;
import sonia.scm.repository.Feature; import sonia.scm.repository.Feature;
import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.GitRepositoryHandler;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.api.Command; import sonia.scm.repository.api.Command;
import sonia.scm.repository.api.HookContextFactory;
import sonia.scm.web.lfs.LfsBlobStoreFactory; import sonia.scm.web.lfs.LfsBlobStoreFactory;
import java.io.IOException; import java.io.IOException;
@@ -64,6 +66,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
Command.DIFF_RESULT, Command.DIFF_RESULT,
Command.LOG, Command.LOG,
Command.TAGS, Command.TAGS,
Command.BRANCH,
Command.BRANCHES, Command.BRANCHES,
Command.INCOMING, Command.INCOMING,
Command.OUTGOING, Command.OUTGOING,
@@ -77,10 +80,12 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
//~--- constructors --------------------------------------------------------- //~--- constructors ---------------------------------------------------------
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory) { public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory, HookContextFactory hookContextFactory, ScmEventBus eventBus) {
this.handler = handler; this.handler = handler;
this.repository = repository; this.repository = repository;
this.lfsBlobStoreFactory = lfsBlobStoreFactory; this.lfsBlobStoreFactory = lfsBlobStoreFactory;
this.hookContextFactory = hookContextFactory;
this.eventBus = eventBus;
this.context = new GitContext(handler.getDirectory(repository.getId()), repository, storeProvider); this.context = new GitContext(handler.getDirectory(repository.getId()), repository, storeProvider);
} }
@@ -133,7 +138,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
@Override @Override
public BranchCommand getBranchCommand() public BranchCommand getBranchCommand()
{ {
return new GitBranchCommand(context, repository, handler.getWorkdirFactory()); return new GitBranchCommand(context, repository, hookContextFactory, eventBus);
} }
/** /**
@@ -292,4 +297,8 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
private final Repository repository; private final Repository repository;
private final LfsBlobStoreFactory lfsBlobStoreFactory; private final LfsBlobStoreFactory lfsBlobStoreFactory;
private final HookContextFactory hookContextFactory;
private final ScmEventBus eventBus;
} }

View File

@@ -36,9 +36,11 @@ package sonia.scm.repository.spi;
import com.google.inject.Inject; import com.google.inject.Inject;
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
import sonia.scm.event.ScmEventBus;
import sonia.scm.plugin.Extension; import sonia.scm.plugin.Extension;
import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.GitRepositoryHandler;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.api.HookContextFactory;
import sonia.scm.web.lfs.LfsBlobStoreFactory; import sonia.scm.web.lfs.LfsBlobStoreFactory;
/** /**
@@ -51,12 +53,16 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
private final GitRepositoryHandler handler; private final GitRepositoryHandler handler;
private final GitRepositoryConfigStoreProvider storeProvider; private final GitRepositoryConfigStoreProvider storeProvider;
private final LfsBlobStoreFactory lfsBlobStoreFactory; private final LfsBlobStoreFactory lfsBlobStoreFactory;
private final HookContextFactory hookContextFactory;
private final ScmEventBus eventBus;
@Inject @Inject
public GitRepositoryServiceResolver(GitRepositoryHandler handler, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory) { public GitRepositoryServiceResolver(GitRepositoryHandler handler, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory, HookContextFactory hookContextFactory, ScmEventBus eventBus) {
this.handler = handler; this.handler = handler;
this.storeProvider = storeProvider; this.storeProvider = storeProvider;
this.lfsBlobStoreFactory = lfsBlobStoreFactory; this.lfsBlobStoreFactory = lfsBlobStoreFactory;
this.hookContextFactory = hookContextFactory;
this.eventBus = eventBus;
} }
@Override @Override
@@ -64,7 +70,7 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
GitRepositoryServiceProvider provider = null; GitRepositoryServiceProvider provider = null;
if (GitRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) { if (GitRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
provider = new GitRepositoryServiceProvider(handler, repository, storeProvider, lfsBlobStoreFactory); provider = new GitRepositoryServiceProvider(handler, repository, storeProvider, lfsBlobStoreFactory, hookContextFactory, eventBus);
} }
return provider; return provider;

View File

@@ -1,20 +1,37 @@
package sonia.scm.repository.spi; package sonia.scm.repository.spi;
import org.assertj.core.api.Assertions;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.event.ScmEventBus;
import sonia.scm.repository.Branch; import sonia.scm.repository.Branch;
import sonia.scm.repository.PostReceiveRepositoryHookEvent;
import sonia.scm.repository.PreReceiveRepositoryHookEvent;
import sonia.scm.repository.api.BranchRequest; import sonia.scm.repository.api.BranchRequest;
import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.repository.api.HookContext;
import sonia.scm.repository.api.HookContextFactory;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class GitBranchCommandTest extends AbstractGitCommandTestBase { public class GitBranchCommandTest extends AbstractGitCommandTestBase {
@Rule @Mock
public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule(); private HookContextFactory hookContextFactory;
@Mock
private ScmEventBus eventBus;
@Test @Test
public void shouldCreateBranchWithDefinedSourceBranch() throws IOException { public void shouldCreateBranchWithDefinedSourceBranch() throws IOException {
@@ -26,10 +43,10 @@ public class GitBranchCommandTest extends AbstractGitCommandTestBase {
branchRequest.setParentBranch(source.getName()); branchRequest.setParentBranch(source.getName());
branchRequest.setNewBranch("new_branch"); branchRequest.setNewBranch("new_branch");
new GitBranchCommand(context, repository, new SimpleGitWorkdirFactory(new WorkdirProvider())).branch(branchRequest); createCommand().branch(branchRequest);
Branch newBranch = findBranch(context, "new_branch"); Branch newBranch = findBranch(context, "new_branch");
Assertions.assertThat(newBranch.getRevision()).isEqualTo(source.getRevision()); assertThat(newBranch.getRevision()).isEqualTo(source.getRevision());
} }
private Branch findBranch(GitContext context, String name) throws IOException { private Branch findBranch(GitContext context, String name) throws IOException {
@@ -41,17 +58,79 @@ public class GitBranchCommandTest extends AbstractGitCommandTestBase {
public void shouldCreateBranch() throws IOException { public void shouldCreateBranch() throws IOException {
GitContext context = createContext(); GitContext context = createContext();
Assertions.assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isEmpty(); assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isEmpty();
BranchRequest branchRequest = new BranchRequest(); BranchRequest branchRequest = new BranchRequest();
branchRequest.setNewBranch("new_branch"); branchRequest.setNewBranch("new_branch");
new GitBranchCommand(context, repository, new SimpleGitWorkdirFactory(new WorkdirProvider())).branch(branchRequest); createCommand().branch(branchRequest);
Assertions.assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty(); assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
}
@Test
public void shouldDeleteBranch() throws IOException {
GitContext context = createContext();
String branchToBeDeleted = "squash";
createCommand().deleteOrClose(branchToBeDeleted);
assertThat(readBranches(context)).filteredOn(b -> b.getName().equals(branchToBeDeleted)).isEmpty();
}
@Test
public void shouldThrowExceptionWhenDeletingDefaultBranch() {
String branchToBeDeleted = "master";
assertThrows(CannotDeleteDefaultBranchException.class, () -> createCommand().deleteOrClose(branchToBeDeleted));
}
private GitBranchCommand createCommand() {
return new GitBranchCommand(createContext(), repository, hookContextFactory, eventBus);
} }
private List<Branch> readBranches(GitContext context) throws IOException { private List<Branch> readBranches(GitContext context) throws IOException {
return new GitBranchesCommand(context, repository).getBranches(); return new GitBranchesCommand(context, repository).getBranches();
} }
@Test
public void shouldPostCreateEvents() {
ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
doNothing().when(eventBus).post(captor.capture());
when(hookContextFactory.createContext(any(), any())).thenAnswer(this::createMockedContext);
BranchRequest branchRequest = new BranchRequest();
branchRequest.setParentBranch("mergeable");
branchRequest.setNewBranch("new_branch");
createCommand().branch(branchRequest);
List<Object> events = captor.getAllValues();
assertThat(events.get(0)).isInstanceOf(PreReceiveRepositoryHookEvent.class);
assertThat(events.get(1)).isInstanceOf(PostReceiveRepositoryHookEvent.class);
PreReceiveRepositoryHookEvent event = (PreReceiveRepositoryHookEvent) events.get(0);
assertThat(event.getContext().getBranchProvider().getCreatedOrModified()).containsExactly("new_branch");
assertThat(event.getContext().getBranchProvider().getDeletedOrClosed()).isEmpty();
}
@Test
public void shouldPostDeleteEvents() {
ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
doNothing().when(eventBus).post(captor.capture());
when(hookContextFactory.createContext(any(), any())).thenAnswer(this::createMockedContext);
createCommand().deleteOrClose("squash");
List<Object> events = captor.getAllValues();
assertThat(events.get(0)).isInstanceOf(PreReceiveRepositoryHookEvent.class);
assertThat(events.get(1)).isInstanceOf(PostReceiveRepositoryHookEvent.class);
PreReceiveRepositoryHookEvent event = (PreReceiveRepositoryHookEvent) events.get(0);
assertThat(event.getContext().getBranchProvider().getDeletedOrClosed()).containsExactly("squash");
assertThat(event.getContext().getBranchProvider().getCreatedOrModified()).isEmpty();
}
private HookContext createMockedContext(InvocationOnMock invocation) {
HookContext mock = mock(HookContext.class);
when(mock.getBranchProvider()).thenReturn(((HookContextProvider) invocation.getArgument(0)).getBranchProvider());
return mock;
}
} }

View File

@@ -36,7 +36,9 @@ import com.aragost.javahg.commands.PullCommand;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.ContextEntry;
import sonia.scm.repository.Branch; import sonia.scm.repository.Branch;
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.api.BranchRequest;
import sonia.scm.repository.util.WorkingCopy; import sonia.scm.repository.util.WorkingCopy;
@@ -67,23 +69,46 @@ public class HgBranchCommand extends AbstractCommand implements BranchCommand {
LOG.debug("Created new branch '{}' in repository {} with changeset {}", LOG.debug("Created new branch '{}' in repository {} with changeset {}",
request.getNewBranch(), getRepository().getNamespaceAndName(), emptyChangeset.getNode()); request.getNewBranch(), getRepository().getNamespaceAndName(), emptyChangeset.getNode());
pullNewBranchIntoCentralRepository(request, workingCopy); pullChangesIntoCentralRepository(workingCopy, request.getNewBranch());
return Branch.normalBranch(request.getNewBranch(), emptyChangeset.getNode()); return Branch.normalBranch(request.getNewBranch(), emptyChangeset.getNode());
} }
} }
@Override
public void deleteOrClose(String branchName) {
try (WorkingCopy<com.aragost.javahg.Repository, com.aragost.javahg.Repository> workingCopy = workdirFactory.createWorkingCopy(getContext(), branchName)) {
User currentUser = SecurityUtils.getSubject().getPrincipals().oneByType(User.class);
LOG.debug("Closing branch '{}' in repository {}", branchName, getRepository().getNamespaceAndName());
com.aragost.javahg.commands.CommitCommand
.on(workingCopy.getWorkingRepository())
.user(getFormattedUser(currentUser))
.message(String.format("Close branch: %s", branchName))
.closeBranch()
.execute();
pullChangesIntoCentralRepository(workingCopy, branchName);
} catch (Exception ex) {
throw new InternalRepositoryException(ContextEntry.ContextBuilder.entity(getContext().getScmRepository()), String.format("Could not close branch: %s", branchName));
}
}
private String getFormattedUser(User currentUser) {
return String.format("%s <%s>", currentUser.getDisplayName(), currentUser.getMail());
}
private Changeset createNewBranchWithEmptyCommit(BranchRequest request, com.aragost.javahg.Repository repository) { private Changeset createNewBranchWithEmptyCommit(BranchRequest request, com.aragost.javahg.Repository repository) {
com.aragost.javahg.commands.BranchCommand.on(repository).set(request.getNewBranch()); com.aragost.javahg.commands.BranchCommand.on(repository).set(request.getNewBranch());
User currentUser = SecurityUtils.getSubject().getPrincipals().oneByType(User.class); User currentUser = SecurityUtils.getSubject().getPrincipals().oneByType(User.class);
return CommitCommand return CommitCommand
.on(repository) .on(repository)
.user(String.format("%s <%s>", currentUser.getDisplayName(), currentUser.getMail())) .user(getFormattedUser(currentUser))
.message("Create new branch " + request.getNewBranch()) .message("Create new branch " + request.getNewBranch())
.execute(); .execute();
} }
private void pullNewBranchIntoCentralRepository(BranchRequest request, WorkingCopy<com.aragost.javahg.Repository, com.aragost.javahg.Repository> workingCopy) { private void pullChangesIntoCentralRepository(WorkingCopy<com.aragost.javahg.Repository, com.aragost.javahg.Repository> workingCopy, String branch) {
try { try {
PullCommand pullCommand = PullCommand.on(workingCopy.getCentralRepository()); PullCommand pullCommand = PullCommand.on(workingCopy.getCentralRepository());
workdirFactory.configure(pullCommand); workdirFactory.configure(pullCommand);
@@ -91,7 +116,7 @@ public class HgBranchCommand extends AbstractCommand implements BranchCommand {
} catch (Exception e) { } catch (Exception e) {
// TODO handle failed update // TODO handle failed update
throw new IntegrateChangesFromWorkdirException(getRepository(), throw new IntegrateChangesFromWorkdirException(getRepository(),
String.format("Could not pull new branch '%s' into central repository", request.getNewBranch()), String.format("Could not pull changes '%s' into central repository", branch),
e); e);
} }
} }

View File

@@ -61,7 +61,8 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider
Command.CAT, Command.CAT,
Command.DIFF, Command.DIFF,
Command.LOG, Command.LOG,
Command.TAGS, Command.TAGS,
Command.BRANCH,
Command.BRANCHES, Command.BRANCHES,
Command.INCOMING, Command.INCOMING,
Command.OUTGOING, Command.OUTGOING,

View File

@@ -6,6 +6,7 @@ import org.junit.Before;
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.InternalRepositoryException;
import sonia.scm.repository.api.BranchRequest; import sonia.scm.repository.api.BranchRequest;
import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.web.HgRepositoryEnvironmentBuilder; import sonia.scm.web.HgRepositoryEnvironmentBuilder;
@@ -13,6 +14,7 @@ import sonia.scm.web.HgRepositoryEnvironmentBuilder;
import java.util.List; import java.util.List;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class HgBranchCommandTest extends AbstractHgCommandTestBase { public class HgBranchCommandTest extends AbstractHgCommandTestBase {
@@ -54,6 +56,22 @@ public class HgBranchCommandTest extends AbstractHgCommandTestBase {
assertThat(cmdContext.open().changeset(newBranch.getRevision()).getParent1().getBranch()).isEqualTo("test-branch"); assertThat(cmdContext.open().changeset(newBranch.getRevision()).getParent1().getBranch()).isEqualTo("test-branch");
} }
@Test
public void shouldCloseBranch() {
String branchToBeClosed = "test-branch";
new HgBranchCommand(cmdContext, repository, workdirFactory).deleteOrClose(branchToBeClosed);
assertThat(readBranches()).filteredOn(b -> b.getName().equals(branchToBeClosed)).isEmpty();
}
@Test
public void shouldThrowInternalRepositoryException() {
String branchToBeClosed = "default";
new HgBranchCommand(cmdContext, repository, workdirFactory).deleteOrClose(branchToBeClosed);
assertThrows(InternalRepositoryException.class, () -> new HgBranchCommand(cmdContext, repository, workdirFactory).deleteOrClose(branchToBeClosed));
}
private List<Branch> readBranches() { private List<Branch> readBranches() {
return new HgBranchesCommand(cmdContext, repository).getBranches(); return new HgBranchesCommand(cmdContext, repository).getBranches();
} }

View File

@@ -47,8 +47,6 @@ public class MapperModule extends AbstractModule {
bind(ScmViolationExceptionToErrorDtoMapper.class).to(Mappers.getMapper(ScmViolationExceptionToErrorDtoMapper.class).getClass()); bind(ScmViolationExceptionToErrorDtoMapper.class).to(Mappers.getMapper(ScmViolationExceptionToErrorDtoMapper.class).getClass());
bind(ExceptionWithContextToErrorDtoMapper.class).to(Mappers.getMapper(ExceptionWithContextToErrorDtoMapper.class).getClass()); bind(ExceptionWithContextToErrorDtoMapper.class).to(Mappers.getMapper(ExceptionWithContextToErrorDtoMapper.class).getClass());
bind(MergeResultToDtoMapper.class).to(Mappers.getMapper(MergeResultToDtoMapper.class).getClass());
// no mapstruct required // no mapstruct required
bind(MeDtoFactory.class); bind(MeDtoFactory.class);
bind(UIPluginDtoMapper.class); bind(UIPluginDtoMapper.class);

View File

@@ -187,6 +187,10 @@
"6eRhF9gU41": { "6eRhF9gU41": {
"displayName": "Nicht unterstützte Merge-Strategie", "displayName": "Nicht unterstützte Merge-Strategie",
"description": "Die gewählte Merge-Strategie wird von dem Repository nicht unterstützt." "description": "Die gewählte Merge-Strategie wird von dem Repository nicht unterstützt."
},
"78RhWxTIw1": {
"displayName": "Der Default-Branch kann nicht gelöscht werden",
"description": "Der Default-Branch kann nicht gelöscht werden. Bitte wählen Sie zuerst einen neuen Default-Branch."
} }
}, },
"namespaceStrategies": { "namespaceStrategies": {

View File

@@ -187,6 +187,10 @@
"6eRhF9gU41": { "6eRhF9gU41": {
"displayName": "Merge strategy not supported", "displayName": "Merge strategy not supported",
"description": "The selected merge strategy is not supported by the repository." "description": "The selected merge strategy is not supported by the repository."
},
"78RhWxTIw1": {
"displayName": "Default branch cannot be deleted",
"description": "The default branch of a repository cannot be deleted. Please select another default branch first."
} }
}, },
"namespaceStrategies": { "namespaceStrategies": {