1*616a8889SThomas Wolf /* 2*616a8889SThomas Wolf * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others 3*616a8889SThomas Wolf * 4*616a8889SThomas Wolf * This program and the accompanying materials are made available under the 5*616a8889SThomas Wolf * terms of the Eclipse Distribution License v. 1.0 which is available at 6*616a8889SThomas Wolf * https://www.eclipse.org/org/documents/edl-v10.php. 7*616a8889SThomas Wolf * 8*616a8889SThomas Wolf * SPDX-License-Identifier: BSD-3-Clause 9*616a8889SThomas Wolf */ 10*616a8889SThomas Wolf package org.eclipse.jgit.lfs; 11*616a8889SThomas Wolf 12*616a8889SThomas Wolf import static org.junit.Assert.assertEquals; 13*616a8889SThomas Wolf import static org.junit.Assert.assertTrue; 14*616a8889SThomas Wolf 15*616a8889SThomas Wolf import java.io.File; 16*616a8889SThomas Wolf import java.nio.charset.StandardCharsets; 17*616a8889SThomas Wolf import java.nio.file.Files; 18*616a8889SThomas Wolf 19*616a8889SThomas Wolf import org.eclipse.jgit.api.Git; 20*616a8889SThomas Wolf import org.eclipse.jgit.api.ResetCommand.ResetType; 21*616a8889SThomas Wolf import org.eclipse.jgit.attributes.FilterCommandRegistry; 22*616a8889SThomas Wolf import org.eclipse.jgit.junit.RepositoryTestCase; 23*616a8889SThomas Wolf import org.eclipse.jgit.lfs.lib.Constants; 24*616a8889SThomas Wolf import org.eclipse.jgit.lib.StoredConfig; 25*616a8889SThomas Wolf import org.junit.AfterClass; 26*616a8889SThomas Wolf import org.junit.Before; 27*616a8889SThomas Wolf import org.junit.BeforeClass; 28*616a8889SThomas Wolf import org.junit.Test; 29*616a8889SThomas Wolf 30*616a8889SThomas Wolf public class LfsGitTest extends RepositoryTestCase { 31*616a8889SThomas Wolf 32*616a8889SThomas Wolf private static final String SMUDGE_NAME = org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX 33*616a8889SThomas Wolf + Constants.ATTR_FILTER_DRIVER_PREFIX 34*616a8889SThomas Wolf + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_SMUDGE; 35*616a8889SThomas Wolf 36*616a8889SThomas Wolf private static final String CLEAN_NAME = org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX 37*616a8889SThomas Wolf + Constants.ATTR_FILTER_DRIVER_PREFIX 38*616a8889SThomas Wolf + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_CLEAN; 39*616a8889SThomas Wolf 40*616a8889SThomas Wolf @BeforeClass installLfs()41*616a8889SThomas Wolf public static void installLfs() { 42*616a8889SThomas Wolf FilterCommandRegistry.register(SMUDGE_NAME, SmudgeFilter.FACTORY); 43*616a8889SThomas Wolf FilterCommandRegistry.register(CLEAN_NAME, CleanFilter.FACTORY); 44*616a8889SThomas Wolf } 45*616a8889SThomas Wolf 46*616a8889SThomas Wolf @AfterClass removeLfs()47*616a8889SThomas Wolf public static void removeLfs() { 48*616a8889SThomas Wolf FilterCommandRegistry.unregister(SMUDGE_NAME); 49*616a8889SThomas Wolf FilterCommandRegistry.unregister(CLEAN_NAME); 50*616a8889SThomas Wolf } 51*616a8889SThomas Wolf 52*616a8889SThomas Wolf private Git git; 53*616a8889SThomas Wolf 54*616a8889SThomas Wolf @Override 55*616a8889SThomas Wolf @Before setUp()56*616a8889SThomas Wolf public void setUp() throws Exception { 57*616a8889SThomas Wolf super.setUp(); 58*616a8889SThomas Wolf git = new Git(db); 59*616a8889SThomas Wolf // commit something 60*616a8889SThomas Wolf writeTrashFile("Test.txt", "Hello world"); 61*616a8889SThomas Wolf git.add().addFilepattern("Test.txt").call(); 62*616a8889SThomas Wolf git.commit().setMessage("Initial commit").call(); 63*616a8889SThomas Wolf // prepare the config for LFS 64*616a8889SThomas Wolf StoredConfig config = git.getRepository().getConfig(); 65*616a8889SThomas Wolf config.setString("filter", "lfs", "clean", CLEAN_NAME); 66*616a8889SThomas Wolf config.setString("filter", "lfs", "smudge", SMUDGE_NAME); 67*616a8889SThomas Wolf config.save(); 68*616a8889SThomas Wolf } 69*616a8889SThomas Wolf 70*616a8889SThomas Wolf @Test checkoutNonLfsPointer()71*616a8889SThomas Wolf public void checkoutNonLfsPointer() throws Exception { 72*616a8889SThomas Wolf String content = "size_t\nsome_function(void* ptr);\n"; 73*616a8889SThomas Wolf File smallFile = writeTrashFile("Test.txt", content); 74*616a8889SThomas Wolf StringBuilder largeContent = new StringBuilder( 75*616a8889SThomas Wolf LfsPointer.SIZE_THRESHOLD * 4); 76*616a8889SThomas Wolf while (largeContent.length() < LfsPointer.SIZE_THRESHOLD * 4) { 77*616a8889SThomas Wolf largeContent.append(content); 78*616a8889SThomas Wolf } 79*616a8889SThomas Wolf File largeFile = writeTrashFile("large.txt", largeContent.toString()); 80*616a8889SThomas Wolf fsTick(largeFile); 81*616a8889SThomas Wolf git.add().addFilepattern("Test.txt").addFilepattern("large.txt").call(); 82*616a8889SThomas Wolf git.commit().setMessage("Text files").call(); 83*616a8889SThomas Wolf writeTrashFile(".gitattributes", "*.txt filter=lfs"); 84*616a8889SThomas Wolf git.add().addFilepattern(".gitattributes").call(); 85*616a8889SThomas Wolf git.commit().setMessage("attributes").call(); 86*616a8889SThomas Wolf assertTrue(smallFile.delete()); 87*616a8889SThomas Wolf assertTrue(largeFile.delete()); 88*616a8889SThomas Wolf // This reset will run the two text files through the smudge filter 89*616a8889SThomas Wolf git.reset().setMode(ResetType.HARD).call(); 90*616a8889SThomas Wolf assertTrue(smallFile.exists()); 91*616a8889SThomas Wolf assertTrue(largeFile.exists()); 92*616a8889SThomas Wolf checkFile(smallFile, content); 93*616a8889SThomas Wolf checkFile(largeFile, largeContent.toString()); 94*616a8889SThomas Wolf // Modify the large file 95*616a8889SThomas Wolf largeContent.append(content); 96*616a8889SThomas Wolf writeTrashFile("large.txt", largeContent.toString()); 97*616a8889SThomas Wolf // This should convert largeFile to an LFS pointer 98*616a8889SThomas Wolf git.add().addFilepattern("large.txt").call(); 99*616a8889SThomas Wolf git.commit().setMessage("Large modified").call(); 100*616a8889SThomas Wolf String lfsPtr = "version https://git-lfs.github.com/spec/v1\n" 101*616a8889SThomas Wolf + "oid sha256:d041ab19bd7edd899b3c0450d0f61819f96672f0b22d26c9753abc62e1261614\n" 102*616a8889SThomas Wolf + "size 858\n"; 103*616a8889SThomas Wolf assertEquals("[.gitattributes, mode:100644, content:*.txt filter=lfs]" 104*616a8889SThomas Wolf + "[Test.txt, mode:100644, content:" + content + ']' 105*616a8889SThomas Wolf + "[large.txt, mode:100644, content:" + lfsPtr + ']', 106*616a8889SThomas Wolf indexState(CONTENT)); 107*616a8889SThomas Wolf // Verify the file has been saved 108*616a8889SThomas Wolf File savedFile = new File(db.getDirectory(), "lfs"); 109*616a8889SThomas Wolf savedFile = new File(savedFile, "objects"); 110*616a8889SThomas Wolf savedFile = new File(savedFile, "d0"); 111*616a8889SThomas Wolf savedFile = new File(savedFile, "41"); 112*616a8889SThomas Wolf savedFile = new File(savedFile, 113*616a8889SThomas Wolf "d041ab19bd7edd899b3c0450d0f61819f96672f0b22d26c9753abc62e1261614"); 114*616a8889SThomas Wolf String saved = new String(Files.readAllBytes(savedFile.toPath()), 115*616a8889SThomas Wolf StandardCharsets.UTF_8); 116*616a8889SThomas Wolf assertEquals(saved, largeContent.toString()); 117*616a8889SThomas Wolf 118*616a8889SThomas Wolf assertTrue(smallFile.delete()); 119*616a8889SThomas Wolf assertTrue(largeFile.delete()); 120*616a8889SThomas Wolf git.reset().setMode(ResetType.HARD).call(); 121*616a8889SThomas Wolf assertTrue(smallFile.exists()); 122*616a8889SThomas Wolf assertTrue(largeFile.exists()); 123*616a8889SThomas Wolf checkFile(smallFile, content); 124*616a8889SThomas Wolf checkFile(largeFile, largeContent.toString()); 125*616a8889SThomas Wolf assertEquals("[.gitattributes, mode:100644, content:*.txt filter=lfs]" 126*616a8889SThomas Wolf + "[Test.txt, mode:100644, content:" + content + ']' 127*616a8889SThomas Wolf + "[large.txt, mode:100644, content:" + lfsPtr + ']', 128*616a8889SThomas Wolf indexState(CONTENT)); 129*616a8889SThomas Wolf git.add().addFilepattern("Test.txt").call(); 130*616a8889SThomas Wolf git.commit().setMessage("Small committed again").call(); 131*616a8889SThomas Wolf String lfsPtrSmall = "version https://git-lfs.github.com/spec/v1\n" 132*616a8889SThomas Wolf + "oid sha256:9110463275fb0e2f0e9fdeaf84e598e62915666161145cf08927079119cc7814\n" 133*616a8889SThomas Wolf + "size 33\n"; 134*616a8889SThomas Wolf assertEquals("[.gitattributes, mode:100644, content:*.txt filter=lfs]" 135*616a8889SThomas Wolf + "[Test.txt, mode:100644, content:" + lfsPtrSmall + ']' 136*616a8889SThomas Wolf + "[large.txt, mode:100644, content:" + lfsPtr + ']', 137*616a8889SThomas Wolf indexState(CONTENT)); 138*616a8889SThomas Wolf 139*616a8889SThomas Wolf assertTrue(git.status().call().isClean()); 140*616a8889SThomas Wolf } 141*616a8889SThomas Wolf } 142