diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/CloseableWrapper.java b/scm-core/src/main/java/sonia/scm/repository/util/CloseableWrapper.java similarity index 50% rename from scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/CloseableWrapper.java rename to scm-core/src/main/java/sonia/scm/repository/util/CloseableWrapper.java index 553f0f5a00..17980ced36 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/CloseableWrapper.java +++ b/scm-core/src/main/java/sonia/scm/repository/util/CloseableWrapper.java @@ -1,18 +1,18 @@ -package sonia.scm.repository; +package sonia.scm.repository.util; import java.util.function.Consumer; -public class CloseableWrapper implements AutoCloseable { +public class CloseableWrapper implements AutoCloseable { - private final C wrapped; - private final Consumer cleanup; + private final T wrapped; + private final Consumer cleanup; - public CloseableWrapper(C wrapped, Consumer cleanup) { + public CloseableWrapper(T wrapped, Consumer cleanup) { this.wrapped = wrapped; this.cleanup = cleanup; } - public C get() { return wrapped; } + public T get() { return wrapped; } @Override public void close() { diff --git a/scm-core/src/main/java/sonia/scm/repository/util/SimpleWorkdirFactory.java b/scm-core/src/main/java/sonia/scm/repository/util/SimpleWorkdirFactory.java new file mode 100644 index 0000000000..f2d436dffd --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/util/SimpleWorkdirFactory.java @@ -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 { + + private static final Logger logger = LoggerFactory.getLogger(SimpleWorkdirFactory.class); + + private final File poolDirectory; + + private final CloneProvider cloneProvider; + private final Repository repository; + + public SimpleWorkdirFactory(Repository repository, CloneProvider cloneProvider) { + this(new File(System.getProperty("java.io.tmpdir"), "scmm-work-pool"), repository, cloneProvider); + } + + public SimpleWorkdirFactory(File poolDirectory, Repository repository, CloneProvider cloneProvider) { + this.poolDirectory = poolDirectory; + this.cloneProvider = cloneProvider; + this.repository = repository; + poolDirectory.mkdirs(); + } + + public WorkingCopy 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 cloneRepository(C context, File target) throws IOException; + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/util/WorkdirFactory.java b/scm-core/src/main/java/sonia/scm/repository/util/WorkdirFactory.java new file mode 100644 index 0000000000..1d3878b250 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/util/WorkdirFactory.java @@ -0,0 +1,5 @@ +package sonia.scm.repository.util; + +public interface WorkdirFactory { + WorkingCopy createWorkingCopy(C gitContext); +} diff --git a/scm-core/src/main/java/sonia/scm/repository/util/WorkingCopy.java b/scm-core/src/main/java/sonia/scm/repository/util/WorkingCopy.java new file mode 100644 index 0000000000..6271b8e199 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/util/WorkingCopy.java @@ -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 extends CloseableWrapper { + + private static final Logger LOG = LoggerFactory.getLogger(WorkingCopy.class); + + private final File directory; + + public WorkingCopy(T wrapped, Consumer 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); + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitWorkdirFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitWorkdirFactory.java index f93713a221..d3ed353677 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitWorkdirFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitWorkdirFactory.java @@ -1,8 +1,8 @@ package sonia.scm.repository; +import org.eclipse.jgit.lib.Repository; import sonia.scm.repository.spi.GitContext; -import sonia.scm.repository.spi.WorkingCopy; +import sonia.scm.repository.util.WorkdirFactory; -public interface GitWorkdirFactory { - WorkingCopy createWorkingCopy(GitContext gitContext); +public interface GitWorkdirFactory extends WorkdirFactory { } 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 23be8883fa..42667f6e51 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 @@ -35,26 +35,44 @@ package sonia.scm.repository.spi; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; 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.GitUtil; +import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.InternalRepositoryException; 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 { - GitBranchCommand(GitContext context, Repository repository) { + private final GitWorkdirFactory workdirFactory; + + GitBranchCommand(GitContext context, Repository repository, GitWorkdirFactory workdirFactory) { super(context, repository); + this.workdirFactory = workdirFactory; } @Override - public Branch branch(String name) throws IOException { - try (Git git = new Git(open())) { - Ref ref = git.branchCreate().setName(name).call(); + public Branch branch(String name) { + 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(); + 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())); } catch (GitAPIException ex) { throw new InternalRepositoryException(repository, "could not create branch " + name, ex); } } + + private void handlePushError(RemoteRefUpdate remoteRefUpdate) { + // TODO handle failed remote update + } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java index f328011815..f402d63c34 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -20,6 +20,7 @@ import sonia.scm.repository.Person; import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.api.MergeCommandResult; import sonia.scm.repository.api.MergeDryRunCommandResult; +import sonia.scm.repository.util.WorkingCopy; import sonia.scm.user.User; import java.io.IOException; @@ -48,7 +49,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand public MergeCommandResult merge(MergeCommandRequest request) { RepositoryPermissions.push(context.getRepository().getId()).check(); - try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(context)) { + try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(context)) { Repository repository = workingCopy.get(); logger.debug("cloned repository to folder {}", repository.getWorkTree()); return new MergeWorker(repository, request).merge(); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java index 898c5875d3..f4b19d1e85 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java @@ -129,7 +129,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider @Override public BranchCommand getBranchCommand() { - return new GitBranchCommand(context, repository); + return new GitBranchCommand(context, repository, handler.getWorkdirFactory()); } /** diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/SimpleGitWorkdirFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/SimpleGitWorkdirFactory.java index f12818aa80..bd38329ea3 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/SimpleGitWorkdirFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/SimpleGitWorkdirFactory.java @@ -9,59 +9,40 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.InternalRepositoryException; +import sonia.scm.repository.util.SimpleWorkdirFactory; +import sonia.scm.repository.util.WorkingCopy; import java.io.File; import java.io.IOException; import java.nio.file.Files; -public class SimpleGitWorkdirFactory implements GitWorkdirFactory { - - private static final Logger logger = LoggerFactory.getLogger(SimpleGitWorkdirFactory.class); - - private final File poolDirectory; +public class SimpleGitWorkdirFactory extends SimpleWorkdirFactory implements GitWorkdirFactory { public SimpleGitWorkdirFactory() { - this(new File(System.getProperty("java.io.tmpdir"), "scmm-git-pool")); + super(null, new GitCloneProvider()); } public SimpleGitWorkdirFactory(File poolDirectory) { - this.poolDirectory = poolDirectory; - poolDirectory.mkdirs(); + super(poolDirectory, null, new GitCloneProvider()); } - public WorkingCopy createWorkingCopy(GitContext gitContext) { - try { - Repository clone = cloneRepository(gitContext.getDirectory(), createNewWorkdir()); - return new WorkingCopy(clone, this::close); - } catch (GitAPIException e) { - throw new InternalRepositoryException(gitContext.getRepository(), "could not clone working copy of repository", e); - } catch (IOException e) { - throw new InternalRepositoryException(gitContext.getRepository(), "could not create temporary directory for clone of repository", e); + private static class GitCloneProvider implements CloneProvider { + + @Override + public Repository cloneRepository(GitContext context, File target) { + try { + return Git.cloneRepository() + .setURI(createScmTransportProtocolUri(context.getDirectory())) + .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 { - return Files.createTempDirectory(poolDirectory.toPath(),"workdir").toFile(); - } - - 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); + private String createScmTransportProtocolUri(File bareRepository) { + return ScmTransportProtocol.NAME + "://" + bareRepository.getAbsolutePath(); } } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/WorkingCopy.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/WorkingCopy.java deleted file mode 100644 index fd0cba510b..0000000000 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/WorkingCopy.java +++ /dev/null @@ -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 { - WorkingCopy(Repository wrapped, Consumer cleanup) { - super(wrapped, cleanup); - } -} diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/CloseableWrapperTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/CloseableWrapperTest.java index e92ee7abb5..3bf0cb8ef7 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/CloseableWrapperTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/CloseableWrapperTest.java @@ -1,6 +1,7 @@ package sonia.scm.repository; import org.junit.Test; +import sonia.scm.repository.util.CloseableWrapper; import java.util.function.Consumer; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java index f2a4ed4954..8c4b682b18 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java @@ -34,11 +34,19 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- +import org.eclipse.jgit.transport.ScmTransportProtocol; +import org.eclipse.jgit.transport.Transport; import org.junit.After; +import org.junit.Before; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.GitRepositoryConfig; +import sonia.scm.repository.PreProcessorUtil; +import sonia.scm.repository.api.HookContextFactory; import sonia.scm.store.InMemoryConfigurationStoreFactory; +import static com.google.inject.util.Providers.of; +import static org.mockito.Mockito.mock; + /** * * @author Sebastian Sdorra @@ -105,4 +113,5 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase /** Field description */ private GitContext context; + private ScmTransportProtocol scmTransportProtocol; } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/BindTransportProtocolRule.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/BindTransportProtocolRule.java new file mode 100644 index 0000000000..49800fc9e8 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/BindTransportProtocolRule.java @@ -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); + } +} 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 d5dfc17698..83f211fed8 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 @@ -1,6 +1,7 @@ package sonia.scm.repository.spi; import org.assertj.core.api.Assertions; +import org.junit.Rule; import org.junit.Test; import sonia.scm.repository.Branch; @@ -10,13 +11,16 @@ import java.util.List; public class GitBranchCommandTest extends AbstractGitCommandTestBase { + @Rule + public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule(); + @Test public void shouldCreateBranch() throws IOException { GitContext context = createContext(); 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(); } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java index c926935496..e8a62f5b86 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java @@ -41,27 +41,8 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase { @Rule public ShiroRule shiro = new ShiroRule(); - - private ScmTransportProtocol scmTransportProtocol; - - @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); - } + @Rule + public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule(); @Test public void shouldDetectMergeableBranches() { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/SimpleGitWorkdirFactoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/SimpleGitWorkdirFactoryTest.java index 70c34fa122..4b8e32cb09 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/SimpleGitWorkdirFactoryTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/SimpleGitWorkdirFactoryTest.java @@ -12,6 +12,7 @@ import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.PreProcessorUtil; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.api.HookContextFactory; +import sonia.scm.repository.util.WorkingCopy; import java.io.File; import java.io.IOException; @@ -44,7 +45,7 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase { SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder()); File masterRepo = createRepositoryDirectory(); - try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { assertThat(workingCopy.get().getDirectory()) .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 public void cloneFromPoolShouldNotBeReused() throws IOException { SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder()); File firstDirectory; - try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { firstDirectory = workingCopy.get().getDirectory(); } - try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { File secondDirectory = workingCopy.get().getDirectory(); assertThat(secondDirectory).isNotEqualTo(firstDirectory); } @@ -86,23 +77,9 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase { SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder()); File directory; - try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { directory = workingCopy.get().getWorkTree(); } 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; - } - } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index d2da936c48..4ccf13e738 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -53,6 +53,7 @@ import sonia.scm.io.INISection; import sonia.scm.plugin.Extension; import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.spi.HgRepositoryServiceProvider; +import sonia.scm.repository.spi.HgWorkdirFactory; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.util.IOUtil; import sonia.scm.util.SystemUtil; @@ -113,10 +114,11 @@ public class HgRepositoryHandler public HgRepositoryHandler(ConfigurationStoreFactory storeFactory, Provider hgContextProvider, RepositoryLocationResolver repositoryLocationResolver, - PluginLoader pluginLoader) + PluginLoader pluginLoader, HgWorkdirFactory workdirFactory) { super(storeFactory, repositoryLocationResolver, pluginLoader); this.hgContextProvider = hgContextProvider; + this.workdirFactory = workdirFactory; try { @@ -408,6 +410,10 @@ public class HgRepositoryHandler } } + public HgWorkdirFactory getWorkdirFactory() { + return workdirFactory; + } + //~--- fields --------------------------------------------------------------- /** Field description */ @@ -415,4 +421,6 @@ public class HgRepositoryHandler /** Field description */ private JAXBContext jaxbContext; + + private final HgWorkdirFactory workdirFactory; } 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 01c5c5fc98..99976f59df 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,10 +32,14 @@ package sonia.scm.repository.spi; import com.aragost.javahg.Changeset; import com.aragost.javahg.commands.CommitCommand; +import com.aragost.javahg.commands.PushCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.Branch; import sonia.scm.repository.Repository; +import sonia.scm.repository.util.WorkingCopy; + +import java.io.IOException; /** * 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); - HgBranchCommand(HgCommandContext context, Repository repository) { + private final HgWorkdirFactory workdirFactory; + + HgBranchCommand(HgCommandContext context, Repository repository, HgWorkdirFactory workdirFactory) { super(context, repository); + this.workdirFactory = workdirFactory; } @Override - public Branch branch(String name) { - com.aragost.javahg.Repository repository = open(); - com.aragost.javahg.commands.BranchCommand.on(repository).set(name); + public Branch branch(String name) 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); - Changeset emptyChangeset = CommitCommand - .on(repository) - .user("SCM-Manager") - .message("Create new branch " + name) - .execute(); + Changeset emptyChangeset = CommitCommand + .on(repository) + .user("SCM-Manager") + .message("Create new branch " + name) + .execute(); - LOG.debug("Created new branch '{}' in repository {} with changeset {}", - name, getRepository().getNamespaceAndName(), emptyChangeset.getNode()); + PushCommand pushCommand = PushCommand + .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()); + } } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java index 0b88c07cb1..d60e888cac 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java @@ -128,7 +128,7 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider @Override public BranchCommand getBranchCommand() { - return new HgBranchCommand(context, repository); + return new HgBranchCommand(context, repository, handler.getWorkdirFactory()); } /** diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgWorkdirFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgWorkdirFactory.java new file mode 100644 index 0000000000..b32b485cca --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgWorkdirFactory.java @@ -0,0 +1,6 @@ +package sonia.scm.repository.spi; + +import sonia.scm.repository.util.WorkdirFactory; + +public interface HgWorkdirFactory extends WorkdirFactory { +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkdirFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkdirFactory.java new file mode 100644 index 0000000000..b893b53fec --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkdirFactory.java @@ -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 implements HgWorkdirFactory { + public SimpleHgWorkdirFactory() { + super(null, new HgCloneProvider()); + } + + public SimpleHgWorkdirFactory(File poolDirectory) { + super(poolDirectory, null, new HgCloneProvider()); + } + + private static class HgCloneProvider implements CloneProvider { + + @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() { + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java index ba9ae3a0b9..7b66688036 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java @@ -46,6 +46,8 @@ import sonia.scm.plugin.Extension; import sonia.scm.repository.HgContext; import sonia.scm.repository.HgContextProvider; 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 serve(MAPPING_HOOK).with(HgHookCallbackServlet.class); + + bind(HgWorkdirFactory.class).to(SimpleHgWorkdirFactory.class); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java index ed222f5119..c3b66525f9 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java @@ -77,7 +77,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Override 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); HgTestUtil.checkForSkip(handler); @@ -87,7 +87,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Test 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.setHgBinary("hg"); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java index 131dad0837..1cecbb21be 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java @@ -105,7 +105,7 @@ public final class HgTestUtil RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver()); HgRepositoryHandler handler = - new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null); + new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null, null); Path repoDir = directory.toPath(); when(repoDao.getPath(any())).thenReturn(repoDir); handler.init(context); 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 8c0b1862d1..24b262948f 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 @@ -4,14 +4,15 @@ import org.assertj.core.api.Assertions; import org.junit.Test; import sonia.scm.repository.Branch; +import java.io.IOException; import java.util.List; public class HgBranchCommandTest extends AbstractHgCommandTestBase { @Test - public void x() { + public void x() throws IOException { 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(); }