diff --git a/pom.xml b/pom.xml index 3ab3cb82a3..c14ce8b39b 100644 --- a/pom.xml +++ b/pom.xml @@ -843,7 +843,7 @@ 1.4.0 - v5.4.0.201906121030-r-scm1 + v5.4.0.201906121030-r-scm2 1.9.0-scm3 diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java new file mode 100644 index 0000000000..2faa2f550f --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java @@ -0,0 +1,36 @@ +package sonia.scm.repository.spi; + +import com.google.common.io.ByteStreams; +import com.google.inject.Binder; +import com.google.inject.Module; +import org.eclipse.jgit.attributes.FilterCommand; +import org.eclipse.jgit.attributes.FilterCommandRegistry; +import org.eclipse.jgit.lib.Repository; +import sonia.scm.plugin.Extension; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.regex.Pattern; + +@Extension +public class GitLfsFilterModule implements Module { + @Override + public void configure(Binder binder) { + FilterCommandRegistry.register(Pattern.compile("git-lfs (smudge|clean) -- .*"), NoOpFilterCommand::new); + } + + private static class NoOpFilterCommand extends FilterCommand { + NoOpFilterCommand(Repository db, InputStream in, OutputStream out) { + super(in, out); + } + + @Override + public int run() throws IOException { + ByteStreams.copy(in, out); + in.close(); + out.close(); + return -1; + } + } +} 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 19f18f0625..1b748a6f1e 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 @@ -1,5 +1,6 @@ package sonia.scm.repository.spi; +import com.google.common.util.concurrent.Striped; import org.apache.commons.lang.StringUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; @@ -22,6 +23,7 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Optional; +import java.util.concurrent.locks.Lock; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static sonia.scm.AlreadyExistsException.alreadyExists; @@ -31,6 +33,8 @@ import static sonia.scm.ScmConstraintViolationException.Builder.doThrow; public class GitModifyCommand extends AbstractGitCommand implements ModifyCommand { + private static final Striped REGISTER_LOCKS = Striped.lock(5); + private final GitWorkdirFactory workdirFactory; private final LfsBlobStoreFactory lfsBlobStoreFactory; @@ -92,17 +96,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman } } - LfsBlobStoreCleanFilterFactory cleanFilterFactory = new LfsBlobStoreCleanFilterFactory(lfsBlobStoreFactory, repository, targetFile); - - String registerKey = "git-lfs clean -- '" + toBeCreated + "'"; - FilterCommandRegistry.register(registerKey, cleanFilterFactory::createFilter); - try { - addFileToGit(toBeCreated); - } catch (GitAPIException e) { - throwInternalRepositoryException("could not add new file to index", e); - } finally { - FilterCommandRegistry.unregister(registerKey); - } + addToGitWithLfsSupport(toBeCreated, targetFile); } @Override @@ -113,10 +107,26 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman throw notFound(createFileContext(path)); } Files.move(file.toPath(), targetFile, REPLACE_EXISTING); + + addToGitWithLfsSupport(path, targetFile); + } + + private void addToGitWithLfsSupport(String path, Path targetFile) { + REGISTER_LOCKS.get(targetFile).lock(); try { - addFileToGit(path); - } catch (GitAPIException e) { - throwInternalRepositoryException("could not add new file to index", e); + LfsBlobStoreCleanFilterFactory cleanFilterFactory = new LfsBlobStoreCleanFilterFactory(lfsBlobStoreFactory, repository, targetFile); + + String registerKey = "git-lfs clean -- '" + path + "'"; + FilterCommandRegistry.register(registerKey, cleanFilterFactory::createFilter); + try { + addFileToGit(path); + } catch (GitAPIException e) { + throwInternalRepositoryException("could not add file to index", e); + } finally { + FilterCommandRegistry.unregister(registerKey); + } + } finally { + REGISTER_LOCKS.get(targetFile).unlock(); } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java index 9414497bcb..c7bbb2a71f 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java @@ -38,25 +38,8 @@ public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { @Test public void shouldCreateCommit() throws IOException, GitAPIException { - BlobStore blobStore = mock(BlobStore.class); - Blob blob = mock(Blob.class); - when(lfsBlobStoreFactory.getLfsBlobStore(any())).thenReturn(blobStore); - when(blobStore.create("fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601")).thenReturn(blob); - when(blobStore.get("fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601")).thenReturn(null, blob); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - when(blob.getOutputStream()).thenReturn(outputStream); - when(blob.getSize()).thenReturn((long) "new content".length()); - - 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_lfs.png", newFile, false)); - request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); - - String newRef = command.execute(request); + String newRef = createCommit("new_lfs.png", "new content", "fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601", outputStream); try (Git git = new Git(createContext().open())) { RevCommit lastCommit = getLastCommit(git); @@ -68,6 +51,45 @@ public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { assertThat(outputStream.toString()).isEqualTo("new content"); } + @Test + public void shouldCreateSecondCommits() throws IOException, GitAPIException { + new GitLfsFilterModule().configure(null); + createCommit("new_lfs.png", "new content", "fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601", new ByteArrayOutputStream()); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + String newRef = createCommit("more_lfs.png", "more content", "2c2316737c9313956dfc0083da3a2a62ce259f66484f3e26440f0d1b02dd4128", outputStream); + + try (Git git = new Git(createContext().open())) { + RevCommit lastCommit = getLastCommit(git); + assertThat(lastCommit.getFullMessage()).isEqualTo("test commit"); + assertThat(lastCommit.getAuthorIdent().getName()).isEqualTo("Dirk Gently"); + assertThat(newRef).isEqualTo(lastCommit.toObjectId().name()); + } + + assertThat(outputStream.toString()).isEqualTo("more content"); + } + + private String createCommit(String fileName, String content, String hashOfContent, ByteArrayOutputStream outputStream) throws IOException { + BlobStore blobStore = mock(BlobStore.class); + Blob blob = mock(Blob.class); + when(lfsBlobStoreFactory.getLfsBlobStore(any())).thenReturn(blobStore); + when(blobStore.create(hashOfContent)).thenReturn(blob); + when(blobStore.get(hashOfContent)).thenReturn(null, blob); + when(blob.getOutputStream()).thenReturn(outputStream); + when(blob.getSize()).thenReturn((long) content.length()); + + File newFile = Files.write(temporaryFolder.newFile().toPath(), content.getBytes()).toFile(); + + GitModifyCommand command = createCommand(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.setCommitMessage("test commit"); + request.addRequest(new ModifyCommandRequest.CreateFileRequest(fileName, newFile, false)); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + return command.execute(request); + } + private RevCommit getLastCommit(Git git) throws GitAPIException { return git.log().setMaxCount(1).call().iterator().next(); }