Checkout target branch while cloning repository

This will prevent the checkout of a wrong initial branch and therefore
safe some unnecessary io
This commit is contained in:
René Pfeuffer
2019-10-06 16:29:50 +02:00
parent d83bc4ea86
commit 3dea971e10
14 changed files with 188 additions and 73 deletions

View File

@@ -19,10 +19,10 @@ public abstract class SimpleWorkdirFactory<R, C> implements WorkdirFactory<R, C>
} }
@Override @Override
public WorkingCopy<R> createWorkingCopy(C context) { public WorkingCopy<R> createWorkingCopy(C context, String initialBranch) {
try { try {
File directory = workdirProvider.createNewWorkdir(); File directory = workdirProvider.createNewWorkdir();
ParentAndClone<R> parentAndClone = cloneRepository(context, directory); ParentAndClone<R> parentAndClone = cloneRepository(context, directory, initialBranch);
return new WorkingCopy<>(parentAndClone.getClone(), parentAndClone.getParent(), this::close, directory); return new WorkingCopy<>(parentAndClone.getClone(), parentAndClone.getParent(), this::close, directory);
} catch (IOException e) { } catch (IOException e) {
throw new InternalRepositoryException(getScmRepository(context), "could not clone repository in temporary directory", e); throw new InternalRepositoryException(getScmRepository(context), "could not clone repository in temporary directory", e);
@@ -35,7 +35,7 @@ public abstract class SimpleWorkdirFactory<R, C> implements WorkdirFactory<R, C>
// We do allow implementations to throw arbitrary exceptions here, so that we can handle them in close // We do allow implementations to throw arbitrary exceptions here, so that we can handle them in close
protected abstract void closeRepository(R repository) throws Exception; protected abstract void closeRepository(R repository) throws Exception;
protected abstract ParentAndClone<R> cloneRepository(C context, File target) throws IOException; protected abstract ParentAndClone<R> cloneRepository(C context, File target, String initialBranch) throws IOException;
private void close(R repository) { private void close(R repository) {
try { try {

View File

@@ -1,5 +1,5 @@
package sonia.scm.repository.util; package sonia.scm.repository.util;
public interface WorkdirFactory<R, C> { public interface WorkdirFactory<R, C> {
WorkingCopy<R> createWorkingCopy(C context); WorkingCopy<R> createWorkingCopy(C context, String initialBranch);
} }

View File

@@ -26,6 +26,8 @@ public class SimpleWorkdirFactoryTest {
public TemporaryFolder temporaryFolder = new TemporaryFolder(); public TemporaryFolder temporaryFolder = new TemporaryFolder();
private SimpleWorkdirFactory<Closeable, Context> simpleWorkdirFactory; private SimpleWorkdirFactory<Closeable, Context> simpleWorkdirFactory;
private String initialBranchForLastCloneCall;
@Before @Before
public void initFactory() throws IOException { public void initFactory() throws IOException {
WorkdirProvider workdirProvider = new WorkdirProvider(temporaryFolder.newFolder()); WorkdirProvider workdirProvider = new WorkdirProvider(temporaryFolder.newFolder());
@@ -41,7 +43,8 @@ public class SimpleWorkdirFactoryTest {
} }
@Override @Override
protected ParentAndClone<Closeable> cloneRepository(Context context, File target) { protected ParentAndClone<Closeable> cloneRepository(Context context, File target, String initialBranch) {
initialBranchForLastCloneCall = initialBranch;
return new ParentAndClone<>(parent, clone); return new ParentAndClone<>(parent, clone);
} }
}; };
@@ -50,7 +53,7 @@ public class SimpleWorkdirFactoryTest {
@Test @Test
public void shouldCreateParentAndClone() { public void shouldCreateParentAndClone() {
Context context = new Context(); Context context = new Context();
try (WorkingCopy<Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context)) { try (WorkingCopy<Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context, null)) {
assertThat(workingCopy.getCentralRepository()).isSameAs(parent); assertThat(workingCopy.getCentralRepository()).isSameAs(parent);
assertThat(workingCopy.getWorkingRepository()).isSameAs(clone); assertThat(workingCopy.getWorkingRepository()).isSameAs(clone);
} }
@@ -59,7 +62,7 @@ public class SimpleWorkdirFactoryTest {
@Test @Test
public void shouldCloseParent() throws IOException { public void shouldCloseParent() throws IOException {
Context context = new Context(); Context context = new Context();
try (WorkingCopy<Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context)) {} try (WorkingCopy<Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context, null)) {}
verify(parent).close(); verify(parent).close();
} }
@@ -67,10 +70,18 @@ public class SimpleWorkdirFactoryTest {
@Test @Test
public void shouldCloseClone() throws IOException { public void shouldCloseClone() throws IOException {
Context context = new Context(); Context context = new Context();
try (WorkingCopy<Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context)) {} try (WorkingCopy<Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context, null)) {}
verify(clone).close(); verify(clone).close();
} }
@Test
public void shouldPropagateInitialBranch() {
Context context = new Context();
try (WorkingCopy<Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context, "some")) {
assertThat(initialBranchForLastCloneCall).isEqualTo("some");
}
}
private static class Context {} private static class Context {}
} }

View File

@@ -140,8 +140,8 @@ class AbstractGitCommand
} }
} }
<R, W extends GitCloneWorker<R>> R inClone(Function<Git, W> workerSupplier, GitWorkdirFactory workdirFactory) { <R, W extends GitCloneWorker<R>> R inClone(Function<Git, W> workerSupplier, GitWorkdirFactory workdirFactory, String initialBranch) {
try (WorkingCopy<Repository> workingCopy = workdirFactory.createWorkingCopy(context)) { try (WorkingCopy<Repository> workingCopy = workdirFactory.createWorkingCopy(context, initialBranch)) {
Repository repository = workingCopy.getWorkingRepository(); Repository repository = workingCopy.getWorkingRepository();
logger.debug("cloned repository to folder {}", repository.getWorkTree()); logger.debug("cloned repository to folder {}", repository.getWorkTree());
return workerSupplier.apply(new Git(repository)).run(); return workerSupplier.apply(new Git(repository)).run();

View File

@@ -58,11 +58,8 @@ public class GitBranchCommand extends AbstractGitCommand implements BranchComman
@Override @Override
public Branch branch(BranchRequest request) { public Branch branch(BranchRequest request) {
try (WorkingCopy<org.eclipse.jgit.lib.Repository> workingCopy = workdirFactory.createWorkingCopy(context)) { try (WorkingCopy<org.eclipse.jgit.lib.Repository> workingCopy = workdirFactory.createWorkingCopy(context, request.getParentBranch())) {
Git clone = new Git(workingCopy.getWorkingRepository()); Git clone = new Git(workingCopy.getWorkingRepository());
if (request.getParentBranch() != null) {
clone.checkout().setName("origin/" + request.getParentBranch()).call();
}
Ref ref = clone.branchCreate().setName(request.getNewBranch()).call(); Ref ref = clone.branchCreate().setName(request.getNewBranch()).call();
Iterable<PushResult> call = clone.push().add(request.getNewBranch()).call(); Iterable<PushResult> call = clone.push().add(request.getNewBranch()).call();
StreamSupport.stream(call.spliterator(), false) StreamSupport.stream(call.spliterator(), false)

View File

@@ -38,7 +38,7 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand
@Override @Override
public MergeCommandResult merge(MergeCommandRequest request) { public MergeCommandResult merge(MergeCommandRequest request) {
return inClone(clone -> new MergeWorker(clone, request), workdirFactory); return inClone(clone -> new MergeWorker(clone, request), workdirFactory, request.getTargetBranch());
} }
@Override @Override
@@ -72,7 +72,6 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand
@Override @Override
MergeCommandResult run() throws IOException { MergeCommandResult run() throws IOException {
checkOutTargetBranch();
MergeResult result = doMergeInClone(); MergeResult result = doMergeInClone();
if (result.getMergeStatus().isSuccessful()) { if (result.getMergeStatus().isSuccessful()) {
doCommit(); doCommit();
@@ -83,10 +82,6 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand
} }
} }
private void checkOutTargetBranch() throws IOException {
checkOutBranch(target);
}
private MergeResult doMergeInClone() throws IOException { private MergeResult doMergeInClone() throws IOException {
MergeResult result; MergeResult result;
try { try {

View File

@@ -3,8 +3,6 @@ package sonia.scm.repository.spi;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
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.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import sonia.scm.BadRequestException; import sonia.scm.BadRequestException;
import sonia.scm.ConcurrentModificationException; import sonia.scm.ConcurrentModificationException;
@@ -25,7 +23,6 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static sonia.scm.AlreadyExistsException.alreadyExists; import static sonia.scm.AlreadyExistsException.alreadyExists;
import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound; import static sonia.scm.NotFoundException.notFound;
import static sonia.scm.ScmConstraintViolationException.Builder.doThrow;
public class GitModifyCommand extends AbstractGitCommand implements ModifyCommand { public class GitModifyCommand extends AbstractGitCommand implements ModifyCommand {
@@ -38,7 +35,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman
@Override @Override
public String execute(ModifyCommandRequest request) { public String execute(ModifyCommandRequest request) {
return inClone(clone -> new ModifyWorker(clone, request), workdirFactory); return inClone(clone -> new ModifyWorker(clone, request), workdirFactory, request.getBranch());
} }
private class ModifyWorker extends GitCloneWorker<String> implements Worker { private class ModifyWorker extends GitCloneWorker<String> implements Worker {
@@ -54,11 +51,6 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman
@Override @Override
String run() throws IOException { String run() throws IOException {
if (!StringUtils.isEmpty(request.getBranch())) {
checkOutBranch(request.getBranch());
}
Ref head = getClone().getRepository().exactRef(Constants.HEAD);
doThrow().violation("branch has to be a valid branch, no revision", "branch", request.getBranch()).when(head == null || !head.isSymbolic());
getClone().getRepository().getFullBranch(); getClone().getRepository().getFullBranch();
if (!StringUtils.isEmpty(request.getExpectedRevision())) { if (!StringUtils.isEmpty(request.getExpectedRevision())) {
if (!request.getExpectedRevision().equals(getCurrentRevision().getName())) { if (!request.getExpectedRevision().equals(getCurrentRevision().getName())) {

View File

@@ -2,6 +2,8 @@ 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.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.ScmTransportProtocol; import org.eclipse.jgit.transport.ScmTransportProtocol;
import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.GitWorkdirFactory;
@@ -11,6 +13,10 @@ import sonia.scm.repository.util.WorkdirProvider;
import javax.inject.Inject; import javax.inject.Inject;
import java.io.File; import java.io.File;
import java.io.IOException;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
public class SimpleGitWorkdirFactory extends SimpleWorkdirFactory<Repository, GitContext> implements GitWorkdirFactory { public class SimpleGitWorkdirFactory extends SimpleWorkdirFactory<Repository, GitContext> implements GitWorkdirFactory {
@@ -20,14 +26,23 @@ public class SimpleGitWorkdirFactory extends SimpleWorkdirFactory<Repository, Gi
} }
@Override @Override
public ParentAndClone<Repository> cloneRepository(GitContext context, File target) { public ParentAndClone<Repository> cloneRepository(GitContext context, File target, String initialBranch) {
try { try {
return new ParentAndClone<>(null, Git.cloneRepository() Repository clone = Git.cloneRepository()
.setURI(createScmTransportProtocolUri(context.getDirectory())) .setURI(createScmTransportProtocolUri(context.getDirectory()))
.setDirectory(target) .setDirectory(target)
.setBranch(initialBranch)
.call() .call()
.getRepository()); .getRepository();
} catch (GitAPIException e) {
Ref head = clone.exactRef(Constants.HEAD);
if (head == null || !head.isSymbolic() || (initialBranch != null && !head.getTarget().getName().endsWith(initialBranch))) {
throw notFound(entity("Branch", initialBranch).in(context.getRepository()));
}
return new ParentAndClone<>(null, clone);
} catch (GitAPIException | IOException e) {
throw new InternalRepositoryException(context.getRepository(), "could not clone working copy of repository", e); throw new InternalRepositoryException(context.getRepository(), "could not clone working copy of repository", e);
} }
} }

View File

@@ -263,8 +263,8 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase {
command.execute(request); command.execute(request);
} }
@Test(expected = ScmConstraintViolationException.class) @Test(expected = NotFoundException.class)
public void shouldFailWithConstraintViolationIfBranchIsNoBranch() throws IOException { public void shouldFailWithNotFoundExceptionIfBranchIsNoBranch() throws IOException {
File newFile = Files.write(temporaryFolder.newFile().toPath(), "irrelevant\n".getBytes()).toFile(); File newFile = Files.write(temporaryFolder.newFile().toPath(), "irrelevant\n".getBytes()).toFile();
GitModifyCommand command = createCommand(); GitModifyCommand command = createCommand();

View File

@@ -0,0 +1,84 @@
package sonia.scm.repository.spi;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import sonia.scm.ScmConstraintViolationException;
import sonia.scm.repository.Person;
import sonia.scm.repository.util.WorkdirProvider;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import static org.assertj.core.api.Assertions.assertThat;
@SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret")
public class GitModifyCommand_withEmptyRepositoryTest extends AbstractGitCommandTestBase {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Rule
public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule();
@Rule
public ShiroRule shiro = new ShiroRule();
@Test
public void shouldCreateNewFileInEmptyRepository() throws IOException, GitAPIException {
File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile();
GitModifyCommand command = createCommand();
ModifyCommandRequest request = new ModifyCommandRequest();
request.setCommitMessage("test commit");
request.addRequest(new ModifyCommandRequest.CreateFileRequest("new_file", newFile, false));
request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det"));
command.execute(request);
TreeAssertions assertions = canonicalTreeParser -> assertThat(canonicalTreeParser.findFile("new_file")).isTrue();
assertInTree(assertions);
}
@Override
protected String getZippedRepositoryResource() {
return "sonia/scm/repository/spi/scm-git-empty-repo.zip";
}
private void assertInTree(TreeAssertions assertions) throws IOException, GitAPIException {
try (Git git = new Git(createContext().open())) {
RevCommit lastCommit = getLastCommit(git);
try (RevWalk walk = new RevWalk(git.getRepository())) {
RevCommit commit = walk.parseCommit(lastCommit);
ObjectId treeId = commit.getTree().getId();
try (ObjectReader reader = git.getRepository().newObjectReader()) {
assertions.checkAssertions(new CanonicalTreeParser(null, reader, treeId));
}
}
}
}
private RevCommit getLastCommit(Git git) throws GitAPIException {
return git.log().setMaxCount(1).call().iterator().next();
}
private GitModifyCommand createCommand() {
return new GitModifyCommand(createContext(), repository, new SimpleGitWorkdirFactory(new WorkdirProvider()));
}
@FunctionalInterface
private interface TreeAssertions {
void checkAssertions(CanonicalTreeParser treeParser) throws CorruptObjectException;
}
}

View File

@@ -20,8 +20,6 @@ import java.io.IOException;
import static com.google.inject.util.Providers.of; import static com.google.inject.util.Providers.of;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase { public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
@@ -43,11 +41,11 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
} }
@Test @Test
public void emptyPoolShouldCreateNewWorkdir() throws IOException { public void emptyPoolShouldCreateNewWorkdir() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider); SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
File masterRepo = createRepositoryDirectory(); File masterRepo = createRepositoryDirectory();
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext())) { try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext(), null)) {
assertThat(workingCopy.getDirectory()) assertThat(workingCopy.getDirectory())
.exists() .exists()
@@ -61,25 +59,37 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
} }
@Test @Test
public void cloneFromPoolShouldNotBeReused() throws IOException { public void shouldCheckoutInitialBranch() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext(), "test-branch")) {
assertThat(new File(workingCopy.getWorkingRepository().getWorkTree(), "a.txt"))
.exists()
.isFile()
.hasContent("a and b");
}
}
@Test
public void cloneFromPoolShouldNotBeReused() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider); SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
File firstDirectory; File firstDirectory;
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext())) { try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext(), null)) {
firstDirectory = workingCopy.getDirectory(); firstDirectory = workingCopy.getDirectory();
} }
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext())) { try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext(), null)) {
File secondDirectory = workingCopy.getDirectory(); File secondDirectory = workingCopy.getDirectory();
assertThat(secondDirectory).isNotEqualTo(firstDirectory); assertThat(secondDirectory).isNotEqualTo(firstDirectory);
} }
} }
@Test @Test
public void cloneFromPoolShouldBeDeletedOnClose() throws IOException { public void cloneFromPoolShouldBeDeletedOnClose() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider); SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
File directory; File directory;
try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext())) { try (WorkingCopy<Repository> workingCopy = factory.createWorkingCopy(createContext(), null)) {
directory = workingCopy.getWorkingRepository().getWorkTree(); directory = workingCopy.getWorkingRepository().getWorkTree();
} }
assertThat(directory).doesNotExist(); assertThat(directory).doesNotExist();

View File

@@ -33,19 +33,15 @@ 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.PullCommand; import com.aragost.javahg.commands.PullCommand;
import com.aragost.javahg.commands.UpdateCommand;
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.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;
import sonia.scm.user.User; import sonia.scm.user.User;
import java.io.IOException;
/** /**
* Mercurial implementation of the {@link BranchCommand}. * Mercurial implementation of the {@link BranchCommand}.
* Note that this creates an empty commit to "persist" the new branch. * Note that this creates an empty commit to "persist" the new branch.
@@ -63,11 +59,9 @@ public class HgBranchCommand extends AbstractCommand implements BranchCommand {
@Override @Override
public Branch branch(BranchRequest request) { public Branch branch(BranchRequest request) {
try (WorkingCopy<com.aragost.javahg.Repository> workingCopy = workdirFactory.createWorkingCopy(getContext())) { try (WorkingCopy<com.aragost.javahg.Repository> workingCopy = workdirFactory.createWorkingCopy(getContext(), request.getParentBranch())) {
com.aragost.javahg.Repository repository = workingCopy.getWorkingRepository(); com.aragost.javahg.Repository repository = workingCopy.getWorkingRepository();
checkoutParentBranchIfSpecified(request, repository);
Changeset emptyChangeset = createNewBranchWithEmptyCommit(request, repository); Changeset emptyChangeset = createNewBranchWithEmptyCommit(request, repository);
LOG.debug("Created new branch '{}' in repository {} with changeset {}", LOG.debug("Created new branch '{}' in repository {} with changeset {}",
@@ -79,16 +73,6 @@ public class HgBranchCommand extends AbstractCommand implements BranchCommand {
} }
} }
private void checkoutParentBranchIfSpecified(BranchRequest request, com.aragost.javahg.Repository repository) {
if (request.getParentBranch() != null) {
try {
UpdateCommand.on(repository).rev(request.getParentBranch()).execute();
} catch (IOException e) {
throw new InternalRepositoryException(getRepository(), "Could not check out parent branch " + request.getParentBranch(), e);
}
}
}
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);

View File

@@ -1,8 +1,10 @@
package sonia.scm.repository.spi; package sonia.scm.repository.spi;
import com.aragost.javahg.BaseRepository;
import com.aragost.javahg.Repository; import com.aragost.javahg.Repository;
import com.aragost.javahg.commands.CloneCommand; import com.aragost.javahg.commands.CloneCommand;
import com.aragost.javahg.commands.PullCommand; import com.aragost.javahg.commands.PullCommand;
import com.aragost.javahg.commands.flags.CloneCommandFlags;
import sonia.scm.repository.util.SimpleWorkdirFactory; import sonia.scm.repository.util.SimpleWorkdirFactory;
import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.web.HgRepositoryEnvironmentBuilder; import sonia.scm.web.HgRepositoryEnvironmentBuilder;
@@ -24,12 +26,19 @@ public class SimpleHgWorkdirFactory extends SimpleWorkdirFactory<Repository, HgC
this.hgRepositoryEnvironmentBuilder = hgRepositoryEnvironmentBuilder; this.hgRepositoryEnvironmentBuilder = hgRepositoryEnvironmentBuilder;
} }
@Override @Override
public ParentAndClone<Repository> cloneRepository(HgCommandContext context, File target) throws IOException { public ParentAndClone<Repository> cloneRepository(HgCommandContext context, File target, String initialBranch) throws IOException {
BiConsumer<sonia.scm.repository.Repository, Map<String, String>> repositoryMapBiConsumer = BiConsumer<sonia.scm.repository.Repository, Map<String, String>> repositoryMapBiConsumer =
(repository, environment) -> hgRepositoryEnvironmentBuilder.get().buildFor(repository, null, environment); (repository, environment) -> hgRepositoryEnvironmentBuilder.get().buildFor(repository, null, environment);
Repository centralRepository = context.openWithSpecialEnvironment(repositoryMapBiConsumer); Repository centralRepository = context.openWithSpecialEnvironment(repositoryMapBiConsumer);
CloneCommand.on(centralRepository).execute(target.getAbsolutePath()); CloneCommand cloneCommand = CloneCommandFlags.on(centralRepository);
return new ParentAndClone<>(centralRepository, Repository.open(target)); if (initialBranch != null) {
cloneCommand.updaterev(initialBranch);
}
cloneCommand.execute(target.getAbsolutePath());
BaseRepository clone = Repository.open(target);
return new ParentAndClone<>(centralRepository, clone);
} }
@Override @Override

View File

@@ -2,7 +2,7 @@ package sonia.scm.repository.spi;
import com.aragost.javahg.commands.PullCommand; import com.aragost.javahg.commands.PullCommand;
import com.google.inject.util.Providers; import com.google.inject.util.Providers;
import org.assertj.core.api.Assertions; 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;
@@ -10,30 +10,48 @@ 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;
import java.io.IOException;
import java.util.List; import java.util.List;
public class HgBranchCommandTest extends AbstractHgCommandTestBase { import static org.assertj.core.api.Assertions.assertThat;
@Test
public void shouldCreateBranch() throws IOException {
Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isEmpty();
public class HgBranchCommandTest extends AbstractHgCommandTestBase {
private SimpleHgWorkdirFactory workdirFactory;
@Before
public void initWorkdirFactory() {
HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder = HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder =
new HgRepositoryEnvironmentBuilder(handler, HgTestUtil.createHookManager()); new HgRepositoryEnvironmentBuilder(handler, HgTestUtil.createHookManager());
SimpleHgWorkdirFactory workdirFactory = new SimpleHgWorkdirFactory(Providers.of(hgRepositoryEnvironmentBuilder), new WorkdirProvider()) { workdirFactory = new SimpleHgWorkdirFactory(Providers.of(hgRepositoryEnvironmentBuilder), new WorkdirProvider()) {
@Override @Override
public void configure(PullCommand pullCommand) { public void configure(PullCommand pullCommand) {
// we do not want to configure http hooks in this unit test // we do not want to configure http hooks in this unit test
} }
}; };
}
@Test
public void shouldCreateBranch() {
BranchRequest branchRequest = new BranchRequest(); BranchRequest branchRequest = new BranchRequest();
branchRequest.setNewBranch("new_branch"); branchRequest.setNewBranch("new_branch");
new HgBranchCommand(cmdContext, repository, workdirFactory).branch(branchRequest); Branch newBranch = new HgBranchCommand(cmdContext, repository, workdirFactory).branch(branchRequest);
Assertions.assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty(); assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
assertThat(cmdContext.open().changeset(newBranch.getRevision()).getParent1().getBranch()).isEqualTo("default");
}
@Test
public void shouldCreateBranchOnSpecificParent() {
BranchRequest branchRequest = new BranchRequest();
branchRequest.setParentBranch("test-branch");
branchRequest.setNewBranch("new_branch");
Branch newBranch = new HgBranchCommand(cmdContext, repository, workdirFactory).branch(branchRequest);
assertThat(readBranches()).filteredOn(b -> b.getName().equals("new_branch")).isNotEmpty();
assertThat(cmdContext.open().changeset(newBranch.getRevision()).getParent1().getBranch()).isEqualTo("test-branch");
} }
private List<Branch> readBranches() { private List<Branch> readBranches() {