mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-07 22:15:45 +01:00
Use clone and push to create branches
Generalize workdir creation for git and hg and create branches in clones instead of the scm repository, so that hooks will be fired correctly once the changes are pushed back. Missing: - Evaluation of the git response from the push command - configuration of the hg environment and the authentication, so that the scmhooks.py script can be triggeret correctly and can callback the scm manager
This commit is contained in:
@@ -1,18 +1,18 @@
|
|||||||
package sonia.scm.repository;
|
package sonia.scm.repository.util;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class CloseableWrapper<C> implements AutoCloseable {
|
public class CloseableWrapper<T> implements AutoCloseable {
|
||||||
|
|
||||||
private final C wrapped;
|
private final T wrapped;
|
||||||
private final Consumer<C> cleanup;
|
private final Consumer<T> cleanup;
|
||||||
|
|
||||||
public CloseableWrapper(C wrapped, Consumer<C> cleanup) {
|
public CloseableWrapper(T wrapped, Consumer<T> cleanup) {
|
||||||
this.wrapped = wrapped;
|
this.wrapped = wrapped;
|
||||||
this.cleanup = cleanup;
|
this.cleanup = cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public C get() { return wrapped; }
|
public T get() { return wrapped; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package sonia.scm.repository.util;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sonia.scm.repository.InternalRepositoryException;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
|
public class SimpleWorkdirFactory<T extends AutoCloseable, C> {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SimpleWorkdirFactory.class);
|
||||||
|
|
||||||
|
private final File poolDirectory;
|
||||||
|
|
||||||
|
private final CloneProvider<T, C> cloneProvider;
|
||||||
|
private final Repository repository;
|
||||||
|
|
||||||
|
public SimpleWorkdirFactory(Repository repository, CloneProvider<T, C> cloneProvider) {
|
||||||
|
this(new File(System.getProperty("java.io.tmpdir"), "scmm-work-pool"), repository, cloneProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleWorkdirFactory(File poolDirectory, Repository repository, CloneProvider<T, C> cloneProvider) {
|
||||||
|
this.poolDirectory = poolDirectory;
|
||||||
|
this.cloneProvider = cloneProvider;
|
||||||
|
this.repository = repository;
|
||||||
|
poolDirectory.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkingCopy<T> createWorkingCopy(C context) {
|
||||||
|
try {
|
||||||
|
File directory = createNewWorkdir();
|
||||||
|
T clone = cloneProvider.cloneRepository(context, directory);
|
||||||
|
return new WorkingCopy<>(clone, this::close, directory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InternalRepositoryException(repository, "could not create temporary directory for clone of repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createNewWorkdir() throws IOException {
|
||||||
|
return Files.createTempDirectory(poolDirectory.toPath(),"workdir").toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close(T repository) {
|
||||||
|
try {
|
||||||
|
repository.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("could not close temporary repository clone", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CloneProvider<T, C> {
|
||||||
|
T cloneRepository(C context, File target) throws IOException;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package sonia.scm.repository.util;
|
||||||
|
|
||||||
|
public interface WorkdirFactory<T, C> {
|
||||||
|
WorkingCopy<T> createWorkingCopy(C gitContext);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package sonia.scm.repository.util;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class WorkingCopy<T> extends CloseableWrapper<T> {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(WorkingCopy.class);
|
||||||
|
|
||||||
|
private final File directory;
|
||||||
|
|
||||||
|
public WorkingCopy(T wrapped, Consumer<T> cleanup, File directory) {
|
||||||
|
super(wrapped, cleanup);
|
||||||
|
this.directory = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
super.close();
|
||||||
|
try {
|
||||||
|
IOUtil.delete(directory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("could not delete temporary workdir '{}'", directory, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import sonia.scm.repository.spi.GitContext;
|
import sonia.scm.repository.spi.GitContext;
|
||||||
import sonia.scm.repository.spi.WorkingCopy;
|
import sonia.scm.repository.util.WorkdirFactory;
|
||||||
|
|
||||||
public interface GitWorkdirFactory {
|
public interface GitWorkdirFactory extends WorkdirFactory<Repository, GitContext> {
|
||||||
WorkingCopy createWorkingCopy(GitContext gitContext);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,26 +35,44 @@ package sonia.scm.repository.spi;
|
|||||||
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.api.Git;
|
||||||
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 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.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.util.WorkingCopy;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
public class GitBranchCommand extends AbstractGitCommand implements BranchCommand {
|
public class GitBranchCommand extends AbstractGitCommand implements BranchCommand {
|
||||||
|
|
||||||
GitBranchCommand(GitContext context, Repository repository) {
|
private final GitWorkdirFactory workdirFactory;
|
||||||
|
|
||||||
|
GitBranchCommand(GitContext context, Repository repository, GitWorkdirFactory workdirFactory) {
|
||||||
super(context, repository);
|
super(context, repository);
|
||||||
|
this.workdirFactory = workdirFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Branch branch(String name) throws IOException {
|
public Branch branch(String name) {
|
||||||
try (Git git = new Git(open())) {
|
try (WorkingCopy<org.eclipse.jgit.lib.Repository> workingCopy = workdirFactory.createWorkingCopy(context)) {
|
||||||
Ref ref = git.branchCreate().setName(name).call();
|
Git clone = new Git(workingCopy.get());
|
||||||
|
Ref ref = clone.branchCreate().setName(name).call();
|
||||||
|
Iterable<PushResult> call = clone.push().add(name).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(name, 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 " + name, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handlePushError(RemoteRefUpdate remoteRefUpdate) {
|
||||||
|
// TODO handle failed remote update
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import sonia.scm.repository.Person;
|
|||||||
import sonia.scm.repository.RepositoryPermissions;
|
import sonia.scm.repository.RepositoryPermissions;
|
||||||
import sonia.scm.repository.api.MergeCommandResult;
|
import sonia.scm.repository.api.MergeCommandResult;
|
||||||
import sonia.scm.repository.api.MergeDryRunCommandResult;
|
import sonia.scm.repository.api.MergeDryRunCommandResult;
|
||||||
|
import sonia.scm.repository.util.WorkingCopy;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -48,7 +49,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand
|
|||||||
public MergeCommandResult merge(MergeCommandRequest request) {
|
public MergeCommandResult merge(MergeCommandRequest request) {
|
||||||
RepositoryPermissions.push(context.getRepository().getId()).check();
|
RepositoryPermissions.push(context.getRepository().getId()).check();
|
||||||
|
|
||||||
try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(context)) {
|
try (WorkingCopy<Repository> workingCopy = workdirFactory.createWorkingCopy(context)) {
|
||||||
Repository repository = workingCopy.get();
|
Repository repository = workingCopy.get();
|
||||||
logger.debug("cloned repository to folder {}", repository.getWorkTree());
|
logger.debug("cloned repository to folder {}", repository.getWorkTree());
|
||||||
return new MergeWorker(repository, request).merge();
|
return new MergeWorker(repository, request).merge();
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
@Override
|
@Override
|
||||||
public BranchCommand getBranchCommand()
|
public BranchCommand getBranchCommand()
|
||||||
{
|
{
|
||||||
return new GitBranchCommand(context, repository);
|
return new GitBranchCommand(context, repository, handler.getWorkdirFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,59 +9,40 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.repository.GitWorkdirFactory;
|
import sonia.scm.repository.GitWorkdirFactory;
|
||||||
import sonia.scm.repository.InternalRepositoryException;
|
import sonia.scm.repository.InternalRepositoryException;
|
||||||
|
import sonia.scm.repository.util.SimpleWorkdirFactory;
|
||||||
|
import sonia.scm.repository.util.WorkingCopy;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
||||||
public class SimpleGitWorkdirFactory implements GitWorkdirFactory {
|
public class SimpleGitWorkdirFactory extends SimpleWorkdirFactory<Repository, GitContext> implements GitWorkdirFactory {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SimpleGitWorkdirFactory.class);
|
|
||||||
|
|
||||||
private final File poolDirectory;
|
|
||||||
|
|
||||||
public SimpleGitWorkdirFactory() {
|
public SimpleGitWorkdirFactory() {
|
||||||
this(new File(System.getProperty("java.io.tmpdir"), "scmm-git-pool"));
|
super(null, new GitCloneProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleGitWorkdirFactory(File poolDirectory) {
|
public SimpleGitWorkdirFactory(File poolDirectory) {
|
||||||
this.poolDirectory = poolDirectory;
|
super(poolDirectory, null, new GitCloneProvider());
|
||||||
poolDirectory.mkdirs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorkingCopy createWorkingCopy(GitContext gitContext) {
|
private static class GitCloneProvider implements CloneProvider<Repository, GitContext> {
|
||||||
try {
|
|
||||||
Repository clone = cloneRepository(gitContext.getDirectory(), createNewWorkdir());
|
@Override
|
||||||
return new WorkingCopy(clone, this::close);
|
public Repository cloneRepository(GitContext context, File target) {
|
||||||
} catch (GitAPIException e) {
|
try {
|
||||||
throw new InternalRepositoryException(gitContext.getRepository(), "could not clone working copy of repository", e);
|
return Git.cloneRepository()
|
||||||
} catch (IOException e) {
|
.setURI(createScmTransportProtocolUri(context.getDirectory()))
|
||||||
throw new InternalRepositoryException(gitContext.getRepository(), "could not create temporary directory for clone of repository", e);
|
.setDirectory(target)
|
||||||
|
.call()
|
||||||
|
.getRepository();
|
||||||
|
} catch (GitAPIException e) {
|
||||||
|
throw new InternalRepositoryException(context.getRepository(), "could not clone working copy of repository", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private File createNewWorkdir() throws IOException {
|
private String createScmTransportProtocolUri(File bareRepository) {
|
||||||
return Files.createTempDirectory(poolDirectory.toPath(),"workdir").toFile();
|
return ScmTransportProtocol.NAME + "://" + bareRepository.getAbsolutePath();
|
||||||
}
|
|
||||||
|
|
||||||
protected Repository cloneRepository(File bareRepository, File target) throws GitAPIException {
|
|
||||||
return Git.cloneRepository()
|
|
||||||
.setURI(createScmTransportProtocolUri(bareRepository))
|
|
||||||
.setDirectory(target)
|
|
||||||
.call()
|
|
||||||
.getRepository();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String createScmTransportProtocolUri(File bareRepository) {
|
|
||||||
return ScmTransportProtocol.NAME + "://" + bareRepository.getAbsolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close(Repository repository) {
|
|
||||||
repository.close();
|
|
||||||
try {
|
|
||||||
FileUtils.delete(repository.getWorkTree(), FileUtils.RECURSIVE);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.warn("could not delete temporary git workdir '{}'", repository.getWorkTree(), e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package sonia.scm.repository.spi;
|
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.Repository;
|
|
||||||
import sonia.scm.repository.CloseableWrapper;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class WorkingCopy extends CloseableWrapper<Repository> {
|
|
||||||
WorkingCopy(Repository wrapped, Consumer<Repository> cleanup) {
|
|
||||||
super(wrapped, cleanup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import sonia.scm.repository.util.CloseableWrapper;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
|||||||
@@ -34,11 +34,19 @@ package sonia.scm.repository.spi;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
|
import org.eclipse.jgit.transport.ScmTransportProtocol;
|
||||||
|
import org.eclipse.jgit.transport.Transport;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.GitRepositoryConfig;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
import sonia.scm.repository.PreProcessorUtil;
|
||||||
|
import sonia.scm.repository.api.HookContextFactory;
|
||||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
|
import static com.google.inject.util.Providers.of;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
@@ -105,4 +113,5 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase
|
|||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private GitContext context;
|
private GitContext context;
|
||||||
|
private ScmTransportProtocol scmTransportProtocol;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.transport.ScmTransportProtocol;
|
||||||
|
import org.eclipse.jgit.transport.Transport;
|
||||||
|
import org.junit.rules.ExternalResource;
|
||||||
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
|
import sonia.scm.repository.PreProcessorUtil;
|
||||||
|
import sonia.scm.repository.RepositoryManager;
|
||||||
|
import sonia.scm.repository.api.HookContextFactory;
|
||||||
|
|
||||||
|
import static com.google.inject.util.Providers.of;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class BindTransportProtocolRule extends ExternalResource {
|
||||||
|
|
||||||
|
private ScmTransportProtocol scmTransportProtocol;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void before() throws Throwable {
|
||||||
|
HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
||||||
|
RepositoryManager repositoryManager = mock(RepositoryManager.class);
|
||||||
|
HookEventFacade hookEventFacade = new HookEventFacade(of(repositoryManager), hookContextFactory);
|
||||||
|
GitRepositoryHandler gitRepositoryHandler = mock(GitRepositoryHandler.class);
|
||||||
|
scmTransportProtocol = new ScmTransportProtocol(of(hookEventFacade), of(gitRepositoryHandler));
|
||||||
|
|
||||||
|
Transport.register(scmTransportProtocol);
|
||||||
|
|
||||||
|
when(gitRepositoryHandler.getRepositoryId(any())).thenReturn("1");
|
||||||
|
when(repositoryManager.get("1")).thenReturn(new sonia.scm.repository.Repository());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void after() {
|
||||||
|
Transport.unregister(scmTransportProtocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions;
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sonia.scm.repository.Branch;
|
import sonia.scm.repository.Branch;
|
||||||
|
|
||||||
@@ -10,13 +11,16 @@ import java.util.List;
|
|||||||
|
|
||||||
public class GitBranchCommandTest extends AbstractGitCommandTestBase {
|
public class GitBranchCommandTest extends AbstractGitCommandTestBase {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
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();
|
Assertions.assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isEmpty();
|
||||||
|
|
||||||
new GitBranchCommand(context, repository).branch("new_branch");
|
new GitBranchCommand(context, repository, new SimpleGitWorkdirFactory()).branch("new_branch");
|
||||||
|
|
||||||
Assertions.assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
|
Assertions.assertThat(readBranches(context)).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,27 +41,8 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShiroRule shiro = new ShiroRule();
|
public ShiroRule shiro = new ShiroRule();
|
||||||
|
@Rule
|
||||||
private ScmTransportProtocol scmTransportProtocol;
|
public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule();
|
||||||
|
|
||||||
@Before
|
|
||||||
public void bindScmProtocol() {
|
|
||||||
HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
|
||||||
RepositoryManager repositoryManager = mock(RepositoryManager.class);
|
|
||||||
HookEventFacade hookEventFacade = new HookEventFacade(of(repositoryManager), hookContextFactory);
|
|
||||||
GitRepositoryHandler gitRepositoryHandler = mock(GitRepositoryHandler.class);
|
|
||||||
scmTransportProtocol = new ScmTransportProtocol(of(hookEventFacade), of(gitRepositoryHandler));
|
|
||||||
|
|
||||||
Transport.register(scmTransportProtocol);
|
|
||||||
|
|
||||||
when(gitRepositoryHandler.getRepositoryId(any())).thenReturn("1");
|
|
||||||
when(repositoryManager.get("1")).thenReturn(new sonia.scm.repository.Repository());
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void unregisterScmProtocol() {
|
|
||||||
Transport.unregister(scmTransportProtocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldDetectMergeableBranches() {
|
public void shouldDetectMergeableBranches() {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import sonia.scm.repository.GitRepositoryHandler;
|
|||||||
import sonia.scm.repository.PreProcessorUtil;
|
import sonia.scm.repository.PreProcessorUtil;
|
||||||
import sonia.scm.repository.RepositoryManager;
|
import sonia.scm.repository.RepositoryManager;
|
||||||
import sonia.scm.repository.api.HookContextFactory;
|
import sonia.scm.repository.api.HookContextFactory;
|
||||||
|
import sonia.scm.repository.util.WorkingCopy;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -44,7 +45,7 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
|
|||||||
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder());
|
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder());
|
||||||
File masterRepo = createRepositoryDirectory();
|
File masterRepo = createRepositoryDirectory();
|
||||||
|
|
||||||
try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) {
|
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext())) {
|
||||||
|
|
||||||
assertThat(workingCopy.get().getDirectory())
|
assertThat(workingCopy.get().getDirectory())
|
||||||
.exists()
|
.exists()
|
||||||
@@ -57,25 +58,15 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void cloneFromPoolShouldBeClosed() throws IOException {
|
|
||||||
PoolWithSpy factory = new PoolWithSpy(temporaryFolder.newFolder());
|
|
||||||
|
|
||||||
try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) {
|
|
||||||
assertThat(workingCopy).isNotNull();
|
|
||||||
}
|
|
||||||
verify(factory.createdClone).close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void cloneFromPoolShouldNotBeReused() throws IOException {
|
public void cloneFromPoolShouldNotBeReused() throws IOException {
|
||||||
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder());
|
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder());
|
||||||
|
|
||||||
File firstDirectory;
|
File firstDirectory;
|
||||||
try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) {
|
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext())) {
|
||||||
firstDirectory = workingCopy.get().getDirectory();
|
firstDirectory = workingCopy.get().getDirectory();
|
||||||
}
|
}
|
||||||
try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) {
|
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext())) {
|
||||||
File secondDirectory = workingCopy.get().getDirectory();
|
File secondDirectory = workingCopy.get().getDirectory();
|
||||||
assertThat(secondDirectory).isNotEqualTo(firstDirectory);
|
assertThat(secondDirectory).isNotEqualTo(firstDirectory);
|
||||||
}
|
}
|
||||||
@@ -86,23 +77,9 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
|
|||||||
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder());
|
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder());
|
||||||
|
|
||||||
File directory;
|
File directory;
|
||||||
try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) {
|
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext())) {
|
||||||
directory = workingCopy.get().getWorkTree();
|
directory = workingCopy.get().getWorkTree();
|
||||||
}
|
}
|
||||||
assertThat(directory).doesNotExist();
|
assertThat(directory).doesNotExist();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PoolWithSpy extends SimpleGitWorkdirFactory {
|
|
||||||
PoolWithSpy(File poolDirectory) {
|
|
||||||
super(poolDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
Repository createdClone;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Repository cloneRepository(File bareRepository, File destination) throws GitAPIException {
|
|
||||||
createdClone = spy(super.cloneRepository(bareRepository, destination));
|
|
||||||
return createdClone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import sonia.scm.io.INISection;
|
|||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
import sonia.scm.plugin.PluginLoader;
|
import sonia.scm.plugin.PluginLoader;
|
||||||
import sonia.scm.repository.spi.HgRepositoryServiceProvider;
|
import sonia.scm.repository.spi.HgRepositoryServiceProvider;
|
||||||
|
import sonia.scm.repository.spi.HgWorkdirFactory;
|
||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
import sonia.scm.util.IOUtil;
|
import sonia.scm.util.IOUtil;
|
||||||
import sonia.scm.util.SystemUtil;
|
import sonia.scm.util.SystemUtil;
|
||||||
@@ -113,10 +114,11 @@ public class HgRepositoryHandler
|
|||||||
public HgRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
public HgRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
||||||
Provider<HgContext> hgContextProvider,
|
Provider<HgContext> hgContextProvider,
|
||||||
RepositoryLocationResolver repositoryLocationResolver,
|
RepositoryLocationResolver repositoryLocationResolver,
|
||||||
PluginLoader pluginLoader)
|
PluginLoader pluginLoader, HgWorkdirFactory workdirFactory)
|
||||||
{
|
{
|
||||||
super(storeFactory, repositoryLocationResolver, pluginLoader);
|
super(storeFactory, repositoryLocationResolver, pluginLoader);
|
||||||
this.hgContextProvider = hgContextProvider;
|
this.hgContextProvider = hgContextProvider;
|
||||||
|
this.workdirFactory = workdirFactory;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -408,6 +410,10 @@ public class HgRepositoryHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HgWorkdirFactory getWorkdirFactory() {
|
||||||
|
return workdirFactory;
|
||||||
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
@@ -415,4 +421,6 @@ public class HgRepositoryHandler
|
|||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private JAXBContext jaxbContext;
|
private JAXBContext jaxbContext;
|
||||||
|
|
||||||
|
private final HgWorkdirFactory workdirFactory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,10 +32,14 @@ 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.PushCommand;
|
||||||
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.util.WorkingCopy;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mercurial implementation of the {@link BranchCommand}.
|
* Mercurial implementation of the {@link BranchCommand}.
|
||||||
@@ -45,24 +49,37 @@ public class HgBranchCommand extends AbstractCommand implements BranchCommand {
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(HgBranchCommand.class);
|
private static final Logger LOG = LoggerFactory.getLogger(HgBranchCommand.class);
|
||||||
|
|
||||||
HgBranchCommand(HgCommandContext context, Repository repository) {
|
private final HgWorkdirFactory workdirFactory;
|
||||||
|
|
||||||
|
HgBranchCommand(HgCommandContext context, Repository repository, HgWorkdirFactory workdirFactory) {
|
||||||
super(context, repository);
|
super(context, repository);
|
||||||
|
this.workdirFactory = workdirFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Branch branch(String name) {
|
public Branch branch(String name) throws IOException {
|
||||||
com.aragost.javahg.Repository repository = open();
|
try (WorkingCopy<RepositoryCloseableWrapper> workingCopy = workdirFactory.createWorkingCopy(getContext())) {
|
||||||
com.aragost.javahg.commands.BranchCommand.on(repository).set(name);
|
com.aragost.javahg.Repository repository = workingCopy.get().get();
|
||||||
|
com.aragost.javahg.commands.BranchCommand.on(repository).set(name);
|
||||||
|
|
||||||
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 " + name)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
LOG.debug("Created new branch '{}' in repository {} with changeset {}",
|
PushCommand pushCommand = PushCommand
|
||||||
name, getRepository().getNamespaceAndName(), emptyChangeset.getNode());
|
.on(repository)
|
||||||
|
.branch(name)
|
||||||
|
.newBranch();
|
||||||
|
pushCommand
|
||||||
|
.cmdAppend("--config", "");
|
||||||
|
pushCommand .execute();
|
||||||
|
|
||||||
return Branch.normalBranch(name, emptyChangeset.getNode());
|
LOG.debug("Created new branch '{}' in repository {} with changeset {}",
|
||||||
|
name, getRepository().getNamespaceAndName(), emptyChangeset.getNode());
|
||||||
|
|
||||||
|
return Branch.normalBranch(name, emptyChangeset.getNode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BranchCommand getBranchCommand() {
|
public BranchCommand getBranchCommand() {
|
||||||
return new HgBranchCommand(context, repository);
|
return new HgBranchCommand(context, repository, handler.getWorkdirFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
|
import sonia.scm.repository.util.WorkdirFactory;
|
||||||
|
|
||||||
|
public interface HgWorkdirFactory extends WorkdirFactory<RepositoryCloseableWrapper, HgCommandContext> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
|
import com.aragost.javahg.Repository;
|
||||||
|
import com.aragost.javahg.commands.CloneCommand;
|
||||||
|
import sonia.scm.repository.util.SimpleWorkdirFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class SimpleHgWorkdirFactory extends SimpleWorkdirFactory<RepositoryCloseableWrapper, HgCommandContext> implements HgWorkdirFactory {
|
||||||
|
public SimpleHgWorkdirFactory() {
|
||||||
|
super(null, new HgCloneProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleHgWorkdirFactory(File poolDirectory) {
|
||||||
|
super(poolDirectory, null, new HgCloneProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HgCloneProvider implements CloneProvider<RepositoryCloseableWrapper, HgCommandContext> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepositoryCloseableWrapper cloneRepository(HgCommandContext context, File target) throws IOException {
|
||||||
|
String execute = CloneCommand.on(context.open()).execute(target.getAbsolutePath());
|
||||||
|
return new RepositoryCloseableWrapper(Repository.open(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RepositoryCloseableWrapper implements AutoCloseable {
|
||||||
|
private final Repository delegate;
|
||||||
|
|
||||||
|
RepositoryCloseableWrapper(Repository delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
Repository get() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,8 @@ import sonia.scm.plugin.Extension;
|
|||||||
import sonia.scm.repository.HgContext;
|
import sonia.scm.repository.HgContext;
|
||||||
import sonia.scm.repository.HgContextProvider;
|
import sonia.scm.repository.HgContextProvider;
|
||||||
import sonia.scm.repository.HgHookManager;
|
import sonia.scm.repository.HgHookManager;
|
||||||
|
import sonia.scm.repository.spi.HgWorkdirFactory;
|
||||||
|
import sonia.scm.repository.spi.SimpleHgWorkdirFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -81,5 +83,7 @@ public class HgServletModule extends ServletModule
|
|||||||
|
|
||||||
// bind servlets
|
// bind servlets
|
||||||
serve(MAPPING_HOOK).with(HgHookCallbackServlet.class);
|
serve(MAPPING_HOOK).with(HgHookCallbackServlet.class);
|
||||||
|
|
||||||
|
bind(HgWorkdirFactory.class).to(SimpleHgWorkdirFactory.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) {
|
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) {
|
||||||
HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver, null);
|
HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver, null, null);
|
||||||
|
|
||||||
handler.init(contextProvider);
|
handler.init(contextProvider);
|
||||||
HgTestUtil.checkForSkip(handler);
|
HgTestUtil.checkForSkip(handler);
|
||||||
@@ -87,7 +87,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getDirectory() {
|
public void getDirectory() {
|
||||||
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver, null);
|
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver, null, null);
|
||||||
|
|
||||||
HgConfig hgConfig = new HgConfig();
|
HgConfig hgConfig = new HgConfig();
|
||||||
hgConfig.setHgBinary("hg");
|
hgConfig.setHgBinary("hg");
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public final class HgTestUtil
|
|||||||
|
|
||||||
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver());
|
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver());
|
||||||
HgRepositoryHandler handler =
|
HgRepositoryHandler handler =
|
||||||
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null);
|
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null, null);
|
||||||
Path repoDir = directory.toPath();
|
Path repoDir = directory.toPath();
|
||||||
when(repoDao.getPath(any())).thenReturn(repoDir);
|
when(repoDao.getPath(any())).thenReturn(repoDir);
|
||||||
handler.init(context);
|
handler.init(context);
|
||||||
|
|||||||
@@ -4,14 +4,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 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 x() {
|
public void x() throws IOException {
|
||||||
Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isEmpty();
|
Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isEmpty();
|
||||||
|
|
||||||
new HgBranchCommand(cmdContext, repository).branch("new_branch");
|
new HgBranchCommand(cmdContext, repository, new SimpleHgWorkdirFactory()).branch("new_branch");
|
||||||
|
|
||||||
Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
|
Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user