mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-06 21:45:43 +01:00
Implement first steps for merge
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class CloseableWrapper<C> implements AutoCloseable {
|
||||||
|
|
||||||
|
private final C wrapped;
|
||||||
|
private final Consumer<C> cleanup;
|
||||||
|
|
||||||
|
public CloseableWrapper(C wrapped, Consumer<C> cleanup) {
|
||||||
|
this.wrapped = wrapped;
|
||||||
|
this.cleanup = cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public C get() { return wrapped; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
try {
|
||||||
|
cleanup.accept(wrapped);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new RuntimeException(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,6 +91,8 @@ public class GitRepositoryHandler
|
|||||||
|
|
||||||
private final Scheduler scheduler;
|
private final Scheduler scheduler;
|
||||||
|
|
||||||
|
private final GitWorkdirPool workdirPool;
|
||||||
|
|
||||||
private Task task;
|
private Task task;
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
@@ -104,10 +106,11 @@ public class GitRepositoryHandler
|
|||||||
* @param scheduler
|
* @param scheduler
|
||||||
*/
|
*/
|
||||||
@Inject
|
@Inject
|
||||||
public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, FileSystem fileSystem, Scheduler scheduler)
|
public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, FileSystem fileSystem, Scheduler scheduler, GitWorkdirPool workdirPool)
|
||||||
{
|
{
|
||||||
super(storeFactory, fileSystem);
|
super(storeFactory, fileSystem);
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
|
this.workdirPool = workdirPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
//~--- get methods ----------------------------------------------------------
|
||||||
@@ -235,4 +238,8 @@ public class GitRepositoryHandler
|
|||||||
{
|
{
|
||||||
return new File(directory, DIRECTORY_REFS).exists();
|
return new File(directory, DIRECTORY_REFS).exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GitWorkdirPool getWorkdirPool() {
|
||||||
|
return workdirPool;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config:
|
||||||
|
*
|
||||||
|
* 1. Overall and absolute maximum of temp work directories
|
||||||
|
* 2. Maximum number of temp work directories pooled overall
|
||||||
|
* 3. Maximum number of temp work directories pooled for one master repository
|
||||||
|
*/
|
||||||
|
public class GitWorkdirPool {
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
private final File poolDirectory;
|
||||||
|
|
||||||
|
public GitWorkdirPool() {
|
||||||
|
this(new File(System.getProperty("java.io.tmpdir")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitWorkdirPool(File poolDirectory) {
|
||||||
|
this.poolDirectory = poolDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CloseableWrapper<Repository> getWorkingCopy(File bareRepository) {
|
||||||
|
try {
|
||||||
|
Git clone = cloneRepository(bareRepository, new File(poolDirectory, Long.toString(random.nextLong())));
|
||||||
|
return new CloseableWrapper<>(clone.getRepository(), r -> clone.close());
|
||||||
|
} catch (GitAPIException e) {
|
||||||
|
throw new InternalRepositoryException("could not clone working copy of repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Git cloneRepository(File bareRepository, File target) throws GitAPIException {
|
||||||
|
return Git.cloneRepository()
|
||||||
|
.setURI(bareRepository.getAbsolutePath())
|
||||||
|
.setDirectory(target)
|
||||||
|
.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.merge.MergeStrategy;
|
||||||
|
import org.eclipse.jgit.merge.ResolveMerger;
|
||||||
|
import sonia.scm.repository.CloseableWrapper;
|
||||||
|
import sonia.scm.repository.GitWorkdirPool;
|
||||||
|
import sonia.scm.repository.InternalRepositoryException;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class GitMergeCommand extends AbstractGitCommand implements MergeCommand {
|
||||||
|
|
||||||
|
private final GitWorkdirPool workdirPool;
|
||||||
|
|
||||||
|
GitMergeCommand(GitContext context, Repository repository, GitWorkdirPool workdirPool) {
|
||||||
|
super(context, repository);
|
||||||
|
this.workdirPool = workdirPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean merge(MergeCommandRequest request) {
|
||||||
|
try (CloseableWrapper<org.eclipse.jgit.lib.Repository> workingCopy = workdirPool.getWorkingCopy(context.open().getDirectory())) {
|
||||||
|
org.eclipse.jgit.lib.Repository repository = workingCopy.get();
|
||||||
|
ResolveMerger merger = (ResolveMerger) MergeStrategy.RECURSIVE.newMerger(repository);
|
||||||
|
boolean mergeResult = merger.merge(repository.resolve(request.getBranchToMerge()), repository.resolve(request.getTargetBranch()));
|
||||||
|
if (mergeResult) {
|
||||||
|
// TODO push and verify push was successful
|
||||||
|
}
|
||||||
|
return mergeResult;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InternalRepositoryException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -245,6 +245,12 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
public MergeDryRunCommand getMergeDryRunCommand() {
|
public MergeDryRunCommand getMergeDryRunCommand() {
|
||||||
return new GitMergeDryRunCommand(context, repository);
|
return new GitMergeDryRunCommand(context, repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MergeCommand getMergeCommand() {
|
||||||
|
return new GitMergeCommand(context, repository, handler.getWorkdirPool());
|
||||||
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
public class CloseableWrapperTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void x() {
|
||||||
|
Consumer<String> wrapped = new Consumer<String>() {
|
||||||
|
// no this cannot be replaced with a lambda because otherwise we could not use Mockito#spy
|
||||||
|
@Override
|
||||||
|
public void accept(String s) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Consumer<String> closer = spy(wrapped);
|
||||||
|
|
||||||
|
try (CloseableWrapper<String> wrapper = new CloseableWrapper<>("test", closer)) {
|
||||||
|
// nothing to do here
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(closer).accept("test");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
import sonia.scm.repository.spi.AbstractGitCommandTestBase;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
public class GitWorkdirPoolTest extends AbstractGitCommandTestBase {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void emptyPoolShouldCreateNewWorkdir() throws IOException {
|
||||||
|
GitWorkdirPool pool = new GitWorkdirPool(temporaryFolder.newFolder());
|
||||||
|
File masterRepo = createRepositoryDirectory();
|
||||||
|
|
||||||
|
CloseableWrapper<Repository> workingCopy = pool.getWorkingCopy(masterRepo);
|
||||||
|
|
||||||
|
assertThat(workingCopy)
|
||||||
|
.isNotNull()
|
||||||
|
.extracting(w -> w.get().getDirectory())
|
||||||
|
.isNotEqualTo(masterRepo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cloneFromPoolShouldBeClosed() throws IOException {
|
||||||
|
PoolWithSpy pool = new PoolWithSpy(temporaryFolder.newFolder());
|
||||||
|
File masterRepo = createRepositoryDirectory();
|
||||||
|
|
||||||
|
try (CloseableWrapper<Repository> workingCopy = pool.getWorkingCopy(masterRepo)) {
|
||||||
|
assertThat(workingCopy).isNotNull();
|
||||||
|
}
|
||||||
|
verify(pool.createdClone).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PoolWithSpy extends GitWorkdirPool {
|
||||||
|
PoolWithSpy(File poolDirectory) {
|
||||||
|
super(poolDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
Git createdClone;
|
||||||
|
@Override
|
||||||
|
protected Git cloneRepository(File bareRepository, File destination) throws GitAPIException {
|
||||||
|
createdClone = spy(super.cloneRepository(bareRepository, destination));
|
||||||
|
return createdClone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,8 +50,10 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase
|
|||||||
@After
|
@After
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
|
if (context != null) {
|
||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method description
|
* Method description
|
||||||
|
|||||||
Reference in New Issue
Block a user