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