diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContext.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContext.java index c7f0327312..f623928087 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContext.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContext.java @@ -27,6 +27,7 @@ package sonia.scm.repository.spi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; +import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitRepositoryConfig; import sonia.scm.repository.GitUtil; import sonia.scm.repository.Repository; @@ -51,19 +52,7 @@ public class GitContext implements Closeable, RepositoryProvider //~--- constructors --------------------------------------------------------- - /** - * Constructs ... - * - * - * @param directory - * @param repository - */ - public GitContext(File directory, Repository repository, GitRepositoryConfigStoreProvider storeProvider) - { - this.directory = directory; - this.repository = repository; - this.storeProvider = storeProvider; - } + private final GitConfig config; //~--- methods -------------------------------------------------------------- @@ -122,6 +111,22 @@ public class GitContext implements Closeable, RepositoryProvider } } + /** + * Constructs ... + * + * + * @param directory + * @param repository + * @param config + */ + public GitContext(File directory, Repository repository, GitRepositoryConfigStoreProvider storeProvider, GitConfig config) + { + this.directory = directory; + this.repository = repository; + this.storeProvider = storeProvider; + this.config = config; + } + void setConfig(GitRepositoryConfig newConfig) { storeProvider.get(repository).set(newConfig); } @@ -133,6 +138,10 @@ public class GitContext implements Closeable, RepositoryProvider private final Repository repository; private final GitRepositoryConfigStoreProvider storeProvider; + GitConfig getGlobalConfig() { + return config; + } + /** Field description */ private org.eclipse.jgit.lib.Repository gitRepository; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContextFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContextFactory.java index 04bb37bed0..214eb090b8 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContextFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContextFactory.java @@ -42,7 +42,7 @@ class GitContextFactory { } GitContext create(Repository repository) { - return new GitContext(handler.getDirectory(repository.getId()), repository, storeProvider); + return new GitContext(handler.getDirectory(repository.getId()), repository, storeProvider, handler.getConfig()); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index 95e3af0aa9..3ff65871c1 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -86,6 +86,9 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman @Override String run() throws IOException { getClone().getRepository().getFullBranch(); + + boolean initialCommit = getClone().getRepository().getRefDatabase().getRefs().isEmpty(); + if (!StringUtils.isEmpty(request.getExpectedRevision()) && !request.getExpectedRevision().equals(getCurrentRevision().getName())) { throw new ConcurrentModificationException(ContextEntry.ContextBuilder.entity("Branch", request.getBranch() == null ? "default" : request.getBranch()).in(repository).build()); @@ -95,10 +98,23 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman } failIfNotChanged(() -> new NoChangesMadeException(repository, ModifyWorker.this.request.getBranch())); Optional revCommit = doCommit(request.getCommitMessage(), request.getAuthor(), request.isSign()); + + if (initialCommit && StringUtils.isNotBlank(context.getGlobalConfig().getDefaultBranch())) { + createDefaultBranch(); + } + push(); return revCommit.orElseThrow(() -> new NoChangesMadeException(repository, ModifyWorker.this.request.getBranch())).name(); } + private void createDefaultBranch() { + try { + getClone().checkout().setName(context.getGlobalConfig().getDefaultBranch()).setCreateBranch(true).call(); + } catch (GitAPIException e) { + throw new InternalRepositoryException(repository, "could not create default branch for initial commit", e); + } + } + @Override public void addFileToScm(String name, Path file) { addToGitWithLfsSupport(name, file); 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 d593a58678..2c2ee26f80 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 @@ -21,24 +21,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + 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.GitConfig; 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 @@ -69,7 +62,7 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase { if (context == null) { - context = new GitContext(repositoryDirectory, repository, new GitRepositoryConfigStoreProvider(InMemoryConfigurationStoreFactory.create())); + context = new GitContext(repositoryDirectory, repository, new GitRepositoryConfigStoreProvider(InMemoryConfigurationStoreFactory.create()), new GitConfig()); } return context; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitIncomingCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitIncomingCommandTest.java index 5cfab8de24..270c548120 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitIncomingCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitIncomingCommandTest.java @@ -32,7 +32,7 @@ import org.junit.Ignore; import org.junit.Test; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.GitChangesetConverterFactory; +import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitTestHelper; import sonia.scm.store.InMemoryConfigurationStoreFactory; @@ -99,7 +99,7 @@ public class GitIncomingCommandTest commit(outgoing, "added a"); - GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory, incomingRepository, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory()))); + GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory, incomingRepository, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory()), new GitConfig())); PullCommandRequest req = new PullCommandRequest(); req.setRemoteRepository(outgoingRepository); pull.pull(req); @@ -177,7 +177,7 @@ public class GitIncomingCommandTest private GitIncomingCommand createCommand() { return new GitIncomingCommand( - new GitContext(incomingDirectory, incomingRepository, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())), + new GitContext(incomingDirectory, incomingRepository, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory()), new GitConfig()), handler, GitTestHelper.createConverterFactory() ); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModificationsCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModificationsCommandTest.java index 1657191d21..28ddd0a234 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModificationsCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModificationsCommandTest.java @@ -27,6 +27,7 @@ package sonia.scm.repository.spi; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Before; import org.junit.Test; +import sonia.scm.repository.GitConfig; import sonia.scm.repository.Modifications; import java.io.File; @@ -42,8 +43,8 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase { @Before public void init() { - incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory, incomingRepository, null)); - outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory, outgoingRepository, null)); + incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory, incomingRepository, null, new GitConfig())); + outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory, outgoingRepository, null, new GitConfig())); } @Test @@ -106,11 +107,11 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase { } void pushOutgoingAndPullIncoming() throws IOException { - GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory, outgoingRepository, null)); + GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory, outgoingRepository, null, new GitConfig())); PushCommandRequest request = new PushCommandRequest(); request.setRemoteRepository(incomingRepository); cmd.push(request); - GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory, incomingRepository, null)); + GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory, incomingRepository, null, new GitConfig())); PullCommandRequest pullRequest = new PullCommandRequest(); pullRequest.setRemoteRepository(incomingRepository); pullCommand.pull(pullRequest); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_InitialCommitTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_InitialCommitTest.java new file mode 100644 index 0000000000..8b8d4fae6c --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_InitialCommitTest.java @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +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.lib.GpgSigner; +import org.eclipse.jgit.lib.Ref; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import sonia.scm.repository.GitTestHelper; +import sonia.scm.repository.Person; +import sonia.scm.repository.work.NoneCachingWorkingCopyPool; +import sonia.scm.repository.work.WorkdirProvider; +import sonia.scm.web.lfs.LfsBlobStoreFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +@SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret") +public class GitModifyCommand_InitialCommitTest extends AbstractGitCommandTestBase { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public ShiroRule shiro = new ShiroRule(); + @Rule + public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule(); + + @BeforeClass + public static void setSigner() { + GpgSigner.setDefault(new GitTestHelper.SimpleGpgSigner()); + } + + @Test + public void shouldCreateCommitOnMasterByDkefault() throws IOException, GitAPIException { + createContext().getGlobalConfig().setDefaultBranch(""); + + executeModifyCommand(); + + try (Git git = new Git(createContext().open())) { + List branches = git.branchList().call(); + assertThat(branches).extracting("name").containsExactly("refs/heads/master"); + } + } + + @Test + public void shouldCreateCommitWithConfiguredDefaultBranch() throws IOException, GitAPIException { + createContext().getGlobalConfig().setDefaultBranch("main"); + + executeModifyCommand(); + + try (Git git = new Git(createContext().open())) { + List branches = git.branchList().call(); + assertThat(branches).extracting("name").containsExactly("refs/heads/main"); + } + } + + private void executeModifyCommand() throws IOException { + File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + + GitModifyCommand command = createCommand(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.setCommitMessage("initial commit"); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("new_file", newFile, false)); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + command.execute(request); + } + + private GitModifyCommand createCommand() { + return new GitModifyCommand(createContext(), new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())), mock(LfsBlobStoreFactory.class)); + } + + @Override + protected String getZippedRepositoryResource() { + return "sonia/scm/repository/spi/scm-git-spi-empty-repo-test.zip"; + } +} diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java index 22ccb94e1c..5609a222f9 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java @@ -31,7 +31,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.GitChangesetConverterFactory; +import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitTestHelper; import sonia.scm.store.InMemoryConfigurationStoreFactory; @@ -99,7 +99,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase commit(outgoing, "added a"); GitPushCommand push = new GitPushCommand(handler, - new GitContext(outgoingDirectory, outgoingRepository, null) + new GitContext(outgoingDirectory, outgoingRepository, null, new GitConfig()) ); PushCommandRequest req = new PushCommandRequest(); @@ -154,7 +154,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase private GitOutgoingCommand createCommand() { return new GitOutgoingCommand( - new GitContext(outgoingDirectory, outgoingRepository, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())), + new GitContext(outgoingDirectory, outgoingRepository, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory()), new GitConfig()), handler, GitTestHelper.createConverterFactory() ); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPushCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPushCommandTest.java index 1c5796a44f..6deb3afe53 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPushCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPushCommandTest.java @@ -29,6 +29,7 @@ package sonia.scm.repository.spi; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; +import sonia.scm.repository.GitConfig; import sonia.scm.repository.api.PushResponse; import java.io.IOException; @@ -89,6 +90,6 @@ public class GitPushCommandTest extends AbstractRemoteCommandTestBase */ private GitPushCommand createCommand() { - return new GitPushCommand(handler, new GitContext(outgoingDirectory, outgoingRepository, null)); + return new GitPushCommand(handler, new GitContext(outgoingDirectory, outgoingRepository, null, new GitConfig())); } } diff --git a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-empty-repo-test.zip b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-empty-repo-test.zip new file mode 100644 index 0000000000..80f2dbd18b Binary files /dev/null and b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-empty-repo-test.zip differ