/* * Copyright (C) 2022, Matthias Fromme * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at * https://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.lfs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.attributes.FilterCommand; import org.eclipse.jgit.attributes.FilterCommandRegistry; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lfs.internal.LfsConnectionFactory; import org.eclipse.jgit.lfs.lib.Constants; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.transport.http.HttpConnection; import org.eclipse.jgit.util.HttpSupport; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; /** * Test if the lfs config is used in the correct way during checkout. * * Two lfs-files are created, one that comes before .gitattributes and * .lfsconfig in git order (".aaa.txt") and one that comes after ("zzz.txt"). * * During checkout/reset it is tested if the correct version of the lfs config * is used. * * TODO: The current behavior seems a little bit strange/unintuitive. Some files * are checked out before and some after the config files. This leads to the * behavior, that during a single command the config changes. Since this seems * to be the same way in native git, the behavior is accepted for now. * */ public class LfsConfigGitTest extends RepositoryTestCase { private static final String SMUDGE_NAME = org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX + Constants.ATTR_FILTER_DRIVER_PREFIX + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_SMUDGE; private static final String LFS_SERVER_URI1 = "https://lfs.server1/test/uri"; private static final String EXPECTED_SERVER_URL1 = LFS_SERVER_URI1 + Protocol.OBJECTS_LFS_ENDPOINT; private static final String LFS_SERVER_URI2 = "https://lfs.server2/test/uri"; private static final String EXPECTED_SERVER_URL2 = LFS_SERVER_URI2 + Protocol.OBJECTS_LFS_ENDPOINT; private static final String LFS_SERVER_URI3 = "https://lfs.server3/test/uri"; private static final String EXPECTED_SERVER_URL3 = LFS_SERVER_URI3 + Protocol.OBJECTS_LFS_ENDPOINT; private static final String FAKE_LFS_POINTER1 = "version https://git-lfs.github.com/spec/v1\n" + "oid sha256:6ce9fab52ee9a6c4c097def4e049c6acdeba44c99d26e83ba80adec1473c9b2d\n" + "size 253952\n"; private static final String FAKE_LFS_POINTER2 = "version https://git-lfs.github.com/spec/v1\n" + "oid sha256:a4b711cd989863ae2038758a62672138347abbbae4076a7ad3a545fda7d08f82\n" + "size 67072\n"; private static List checkoutURLs = new ArrayList<>(); static class SmudgeFilterMock extends FilterCommand { public SmudgeFilterMock(Repository db, InputStream in, OutputStream out) throws IOException { super(in, out); HttpConnection lfsServerConn = LfsConnectionFactory.getLfsConnection(db, HttpSupport.METHOD_POST, Protocol.OPERATION_DOWNLOAD); checkoutURLs.add(lfsServerConn.getURL().toString()); } @Override public int run() throws IOException { // Stupid no impl in.transferTo(out); return -1; } } @BeforeClass public static void installLfs() { FilterCommandRegistry.register(SMUDGE_NAME, SmudgeFilterMock::new); } @AfterClass public static void removeLfs() { FilterCommandRegistry.unregister(SMUDGE_NAME); } private Git git; @Override @Before public void setUp() throws Exception { super.setUp(); git = new Git(db); // commit something writeTrashFile("Test.txt", "Hello world"); git.add().addFilepattern("Test.txt").call(); git.commit().setMessage("Initial commit").call(); // prepare the config for LFS StoredConfig config = git.getRepository().getConfig(); config.setString("filter", "lfs", "smudge", SMUDGE_NAME); config.setString(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_AUTOCRLF, "false"); config.save(); fileBefore = null; fileAfter = null; configFile = null; gitAttributesFile = null; } File fileBefore; File fileAfter; File configFile; File gitAttributesFile; private void createLfsFiles(String lfsPointer) throws Exception { //File to be checked out before lfs config String fileNameBefore = ".aaa.txt"; fileBefore = writeTrashFile(fileNameBefore, lfsPointer); git.add().addFilepattern(fileNameBefore).call(); // File to be checked out after lfs config String fileNameAfter = "zzz.txt"; fileAfter = writeTrashFile(fileNameAfter, lfsPointer); git.add().addFilepattern(fileNameAfter).call(); git.commit().setMessage("Commit LFS Pointer files").call(); } private String addLfsConfigFiles(String lfsServerUrl) throws Exception { // Add config files to the repo String lfsConfig1 = createLfsConfig(lfsServerUrl); git.add().addFilepattern(Constants.DOT_LFS_CONFIG).call(); // Modify gitattributes on second call, to force checkout too. if (gitAttributesFile == null) { gitAttributesFile = writeTrashFile(".gitattributes", "*.txt filter=lfs"); } else { gitAttributesFile = writeTrashFile(".gitattributes", "*.txt filter=lfs\n"); } git.add().addFilepattern(".gitattributes").call(); git.commit().setMessage("Commit config files").call(); return lfsConfig1; } private String createLfsConfig(String lfsServerUrl) throws IOException { String lfsConfig1 = "[lfs]\n url = " + lfsServerUrl; configFile = writeTrashFile(Constants.DOT_LFS_CONFIG, lfsConfig1); return lfsConfig1; } @Test public void checkoutLfsObjects_reset() throws Exception { createLfsFiles(FAKE_LFS_POINTER1); String lfsConfig1 = addLfsConfigFiles(LFS_SERVER_URI1); // Delete files to force action on reset assertTrue(configFile.delete()); assertTrue(fileBefore.delete()); assertTrue(fileAfter.delete()); assertTrue(gitAttributesFile.delete()); // create config file with different url createLfsConfig(LFS_SERVER_URI3); checkoutURLs.clear(); git.reset().setMode(ResetType.HARD).call(); checkFile(configFile, lfsConfig1); checkFile(fileBefore, FAKE_LFS_POINTER1); checkFile(fileAfter, FAKE_LFS_POINTER1); assertEquals(2, checkoutURLs.size()); // TODO: Should may be EXPECTED_SERVR_URL1 assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0)); assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(1)); } @Test public void checkoutLfsObjects_BranchSwitch() throws Exception { // Create a new branch "URL1" and add config files git.checkout().setCreateBranch(true).setName("URL1").call(); createLfsFiles(FAKE_LFS_POINTER1); String lfsConfig1 = addLfsConfigFiles(LFS_SERVER_URI1); // Create a second new branch "URL2" and add config files git.checkout().setCreateBranch(true).setName("URL2").call(); createLfsFiles(FAKE_LFS_POINTER2); String lfsConfig2 = addLfsConfigFiles(LFS_SERVER_URI2); checkFile(configFile, lfsConfig2); checkFile(fileBefore, FAKE_LFS_POINTER2); checkFile(fileAfter, FAKE_LFS_POINTER2); checkoutURLs.clear(); git.checkout().setName("URL1").call(); checkFile(configFile, lfsConfig1); checkFile(fileBefore, FAKE_LFS_POINTER1); checkFile(fileAfter, FAKE_LFS_POINTER1); assertEquals(2, checkoutURLs.size()); // TODO: Should may be EXPECTED_SERVR_URL1 assertEquals(EXPECTED_SERVER_URL2, checkoutURLs.get(0)); assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(1)); checkoutURLs.clear(); git.checkout().setName("URL2").call(); checkFile(configFile, lfsConfig2); checkFile(fileBefore, FAKE_LFS_POINTER2); checkFile(fileAfter, FAKE_LFS_POINTER2); assertEquals(2, checkoutURLs.size()); // TODO: Should may be EXPECTED_SERVR_URL2 assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(0)); assertEquals(EXPECTED_SERVER_URL2, checkoutURLs.get(1)); } @Test public void checkoutLfsObjects_BranchSwitch_ModifiedLocal() throws Exception { // Create a new branch "URL1" and add config files git.checkout().setCreateBranch(true).setName("URL1").call(); createLfsFiles(FAKE_LFS_POINTER1); addLfsConfigFiles(LFS_SERVER_URI1); // Create a second new branch "URL2" and add config files git.checkout().setCreateBranch(true).setName("URL2").call(); createLfsFiles(FAKE_LFS_POINTER2); addLfsConfigFiles(LFS_SERVER_URI1); // create config file with different url assertTrue(configFile.delete()); String lfsConfig3 = createLfsConfig(LFS_SERVER_URI3); checkFile(configFile, lfsConfig3); checkFile(fileBefore, FAKE_LFS_POINTER2); checkFile(fileAfter, FAKE_LFS_POINTER2); checkoutURLs.clear(); git.checkout().setName("URL1").call(); checkFile(fileBefore, FAKE_LFS_POINTER1); checkFile(fileAfter, FAKE_LFS_POINTER1); checkFile(configFile, lfsConfig3); assertEquals(2, checkoutURLs.size()); assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0)); assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(1)); checkoutURLs.clear(); git.checkout().setName("URL2").call(); checkFile(fileBefore, FAKE_LFS_POINTER2); checkFile(fileAfter, FAKE_LFS_POINTER2); checkFile(configFile, lfsConfig3); assertEquals(2, checkoutURLs.size()); assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0)); assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(1)); } }