View Javadoc
1   /*
2    * Copyright (C) 2016, 2022 Christian Halstrick <christian.halstrick@sap.com> 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.util;
11  
12  import static org.junit.Assert.assertEquals;
13  import static org.junit.Assert.assertFalse;
14  
15  import java.io.File;
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.io.OutputStream;
19  import java.util.HashSet;
20  import java.util.Set;
21  
22  import org.eclipse.jgit.api.Git;
23  import org.eclipse.jgit.api.MergeResult;
24  import org.eclipse.jgit.api.errors.GitAPIException;
25  import org.eclipse.jgit.attributes.FilterCommand;
26  import org.eclipse.jgit.attributes.FilterCommandFactory;
27  import org.eclipse.jgit.attributes.FilterCommandRegistry;
28  import org.eclipse.jgit.junit.RepositoryTestCase;
29  import org.eclipse.jgit.lib.Constants;
30  import org.eclipse.jgit.lib.RefUpdate;
31  import org.eclipse.jgit.lib.Repository;
32  import org.eclipse.jgit.lib.StoredConfig;
33  import org.eclipse.jgit.revwalk.RevCommit;
34  import org.junit.Before;
35  import org.junit.Test;
36  
37  public class FilterCommandsTest extends RepositoryTestCase {
38  	private Git git;
39  
40  	RevCommit initialCommit;
41  
42  	RevCommit secondCommit;
43  
44  	class TestCommandFactory implements FilterCommandFactory {
45  		private int prefix;
46  
47  		public TestCommandFactory(int prefix) {
48  			this.prefix = prefix;
49  		}
50  
51  		@Override
52  		public FilterCommand create(Repository repo, InputStream in,
53  				final OutputStream out) {
54  			FilterCommand cmd = new FilterCommand(in, out) {
55  
56  				@Override
57  				public int run() throws IOException {
58  					int b = in.read();
59  					if (b == -1) {
60  						in.close();
61  						out.close();
62  						return b;
63  					}
64  					out.write(prefix);
65  					out.write(b);
66  					return 1;
67  				}
68  			};
69  			return cmd;
70  		}
71  	}
72  
73  	@Override
74  	@Before
75  	public void setUp() throws Exception {
76  		super.setUp();
77  		git = new Git(db);
78  		// commit something
79  		writeTrashFile("Test.txt", "Hello world");
80  		git.add().addFilepattern("Test.txt").call();
81  		initialCommit = git.commit().setMessage("Initial commit").call();
82  
83  		// create a master branch and switch to it
84  		git.branchCreate().setName("test").call();
85  		RefUpdate rup = db.updateRef(Constants.HEAD);
86  		rup.link("refs/heads/test");
87  
88  		// commit something on the test branch
89  		writeTrashFile("Test.txt", "Some change");
90  		git.add().addFilepattern("Test.txt").call();
91  		secondCommit = git.commit().setMessage("Second commit").call();
92  	}
93  
94  	@Override
95  	public void tearDown() throws Exception {
96  		Set<String> existingFilters = new HashSet<>(
97  				FilterCommandRegistry.getRegisteredFilterCommands());
98  		existingFilters.forEach(FilterCommandRegistry::unregister);
99  		super.tearDown();
100 	}
101 
102 	@Test
103 	public void testBuiltinCleanFilter()
104 			throws IOException, GitAPIException {
105 		String builtinCommandName = "jgit://builtin/test/clean";
106 		FilterCommandRegistry.register(builtinCommandName,
107 				new TestCommandFactory('c'));
108 		StoredConfig config = git.getRepository().getConfig();
109 		config.setString("filter", "test", "clean", builtinCommandName);
110 		config.save();
111 
112 		writeTrashFile(".gitattributes", "*.txt filter=test");
113 		git.add().addFilepattern(".gitattributes").call();
114 		git.commit().setMessage("add filter").call();
115 
116 		writeTrashFile("Test.txt", "Hello again");
117 		git.add().addFilepattern("Test.txt").call();
118 		assertEquals(
119 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.txt, mode:100644, content:cHceclclcoc cacgcacicn]",
120 				indexState(CONTENT));
121 
122 		writeTrashFile("Test.bin", "Hello again");
123 		git.add().addFilepattern("Test.bin").call();
124 		assertEquals(
125 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.bin, mode:100644, content:Hello again][Test.txt, mode:100644, content:cHceclclcoc cacgcacicn]",
126 				indexState(CONTENT));
127 
128 		config.setString("filter", "test", "clean", null);
129 		config.save();
130 
131 		git.add().addFilepattern("Test.txt").call();
132 		assertEquals(
133 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.bin, mode:100644, content:Hello again][Test.txt, mode:100644, content:Hello again]",
134 				indexState(CONTENT));
135 
136 		config.setString("filter", "test", "clean", null);
137 		config.save();
138 	}
139 
140 	@Test
141 	public void testBuiltinSmudgeFilter() throws IOException, GitAPIException {
142 		String builtinCommandName = "jgit://builtin/test/smudge";
143 		FilterCommandRegistry.register(builtinCommandName,
144 				new TestCommandFactory('s'));
145 		StoredConfig config = git.getRepository().getConfig();
146 		config.setString("filter", "test", "smudge", builtinCommandName);
147 		config.save();
148 
149 		writeTrashFile(".gitattributes", "*.txt filter=test");
150 		git.add().addFilepattern(".gitattributes").call();
151 		git.commit().setMessage("add filter").call();
152 
153 		writeTrashFile("Test.txt", "Hello again");
154 		git.add().addFilepattern("Test.txt").call();
155 		assertEquals(
156 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.txt, mode:100644, content:Hello again]",
157 				indexState(CONTENT));
158 		assertEquals("Hello again", read("Test.txt"));
159 		deleteTrashFile("Test.txt");
160 		git.checkout().addPath("Test.txt").call();
161 		assertEquals("sHseslslsos sasgsasisn", read("Test.txt"));
162 
163 		writeTrashFile("Test.bin", "Hello again");
164 		git.add().addFilepattern("Test.bin").call();
165 		assertEquals(
166 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.bin, mode:100644, content:Hello again][Test.txt, mode:100644, content:Hello again]",
167 				indexState(CONTENT));
168 		deleteTrashFile("Test.bin");
169 		git.checkout().addPath("Test.bin").call();
170 		assertEquals("Hello again", read("Test.bin"));
171 
172 		config.setString("filter", "test", "clean", null);
173 		config.save();
174 
175 		git.add().addFilepattern("Test.txt").call();
176 		assertEquals(
177 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.bin, mode:100644, content:Hello again][Test.txt, mode:100644, content:sHseslslsos sasgsasisn]",
178 				indexState(CONTENT));
179 
180 		config.setString("filter", "test", "clean", null);
181 		config.save();
182 	}
183 
184 	@Test
185 	public void testBuiltinCleanAndSmudgeFilter() throws IOException, GitAPIException {
186 		String builtinCommandPrefix = "jgit://builtin/test/";
187 		FilterCommandRegistry.register(builtinCommandPrefix + "smudge",
188 				new TestCommandFactory('s'));
189 		FilterCommandRegistry.register(builtinCommandPrefix + "clean",
190 				new TestCommandFactory('c'));
191 		StoredConfig config = git.getRepository().getConfig();
192 		config.setString("filter", "test", "smudge", builtinCommandPrefix+"smudge");
193 		config.setString("filter", "test", "clean",
194 				builtinCommandPrefix + "clean");
195 		config.save();
196 
197 		writeTrashFile(".gitattributes", "*.txt filter=test");
198 		git.add().addFilepattern(".gitattributes").call();
199 		git.commit().setMessage("add filter").call();
200 
201 		writeTrashFile("Test.txt", "Hello again");
202 		git.add().addFilepattern("Test.txt").call();
203 		assertEquals(
204 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.txt, mode:100644, content:cHceclclcoc cacgcacicn]",
205 				indexState(CONTENT));
206 		assertEquals("Hello again", read("Test.txt"));
207 		deleteTrashFile("Test.txt");
208 		git.checkout().addPath("Test.txt").call();
209 		assertEquals("scsHscsescslscslscsoscs scsascsgscsascsiscsn",
210 				read("Test.txt"));
211 
212 		writeTrashFile("Test.bin", "Hello again");
213 		git.add().addFilepattern("Test.bin").call();
214 		assertEquals(
215 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.bin, mode:100644, content:Hello again][Test.txt, mode:100644, content:cHceclclcoc cacgcacicn]",
216 				indexState(CONTENT));
217 		deleteTrashFile("Test.bin");
218 		git.checkout().addPath("Test.bin").call();
219 		assertEquals("Hello again", read("Test.bin"));
220 
221 		config.setString("filter", "test", "clean", null);
222 		config.save();
223 
224 		git.add().addFilepattern("Test.txt").call();
225 		assertEquals(
226 				"[.gitattributes, mode:100644, content:*.txt filter=test][Test.bin, mode:100644, content:Hello again][Test.txt, mode:100644, content:scsHscsescslscslscsoscs scsascsgscsascsiscsn]",
227 				indexState(CONTENT));
228 
229 		config.setString("filter", "test", "clean", null);
230 		config.save();
231 	}
232 
233 	@Test
234 	public void testBranchSwitch() throws Exception {
235 		String builtinCommandPrefix = "jgit://builtin/test/";
236 		FilterCommandRegistry.register(builtinCommandPrefix + "smudge",
237 				new TestCommandFactory('s'));
238 		FilterCommandRegistry.register(builtinCommandPrefix + "clean",
239 				new TestCommandFactory('c'));
240 		StoredConfig config = git.getRepository().getConfig();
241 		config.setString("filter", "test", "smudge",
242 				builtinCommandPrefix + "smudge");
243 		config.setString("filter", "test", "clean",
244 				builtinCommandPrefix + "clean");
245 		config.save();
246 		// We're on the test branch
247 		File aFile = writeTrashFile("a.txt", "a");
248 		writeTrashFile(".gitattributes", "a.txt filter=test");
249 		File cFile = writeTrashFile("cc/c.txt", "C");
250 		writeTrashFile("cc/.gitattributes", "c.txt filter=test");
251 		git.add().addFilepattern(".").call();
252 		git.commit().setMessage("On test").call();
253 		git.checkout().setName("master").call();
254 		git.branchCreate().setName("other").call();
255 		git.checkout().setName("other").call();
256 		writeTrashFile("b.txt", "b");
257 		writeTrashFile(".gitattributes", "b.txt filter=test");
258 		git.add().addFilepattern(".").call();
259 		git.commit().setMessage("On other").call();
260 		git.checkout().setName("test").call();
261 		checkFile(aFile, "scsa");
262 		checkFile(cFile, "scsC");
263 	}
264 
265 	@Test
266 	public void testCheckoutSingleFile() throws Exception {
267 		String builtinCommandPrefix = "jgit://builtin/test/";
268 		FilterCommandRegistry.register(builtinCommandPrefix + "smudge",
269 				new TestCommandFactory('s'));
270 		FilterCommandRegistry.register(builtinCommandPrefix + "clean",
271 				new TestCommandFactory('c'));
272 		StoredConfig config = git.getRepository().getConfig();
273 		config.setString("filter", "test", "smudge",
274 				builtinCommandPrefix + "smudge");
275 		config.setString("filter", "test", "clean",
276 				builtinCommandPrefix + "clean");
277 		config.save();
278 		// We're on the test branch
279 		File aFile = writeTrashFile("a.txt", "a");
280 		File attributes = writeTrashFile(".gitattributes", "a.txt filter=test");
281 		git.add().addFilepattern(".").call();
282 		git.commit().setMessage("On test").call();
283 		git.checkout().setName("master").call();
284 		git.branchCreate().setName("other").call();
285 		git.checkout().setName("other").call();
286 		writeTrashFile("b.txt", "b");
287 		writeTrashFile(".gitattributes", "b.txt filter=test");
288 		git.add().addFilepattern(".").call();
289 		git.commit().setMessage("On other").call();
290 		git.checkout().setName("master").call();
291 		assertFalse(aFile.exists());
292 		assertFalse(attributes.exists());
293 		git.checkout().setStartPoint("test").addPath("a.txt").call();
294 		checkFile(aFile, "scsa");
295 	}
296 
297 	@Test
298 	public void testCheckoutSingleFile2() throws Exception {
299 		String builtinCommandPrefix = "jgit://builtin/test/";
300 		FilterCommandRegistry.register(builtinCommandPrefix + "smudge",
301 				new TestCommandFactory('s'));
302 		FilterCommandRegistry.register(builtinCommandPrefix + "clean",
303 				new TestCommandFactory('c'));
304 		StoredConfig config = git.getRepository().getConfig();
305 		config.setString("filter", "test", "smudge",
306 				builtinCommandPrefix + "smudge");
307 		config.setString("filter", "test", "clean",
308 				builtinCommandPrefix + "clean");
309 		config.save();
310 		// We're on the test branch
311 		File aFile = writeTrashFile("a.txt", "a");
312 		File attributes = writeTrashFile(".gitattributes", "a.txt filter=test");
313 		git.add().addFilepattern(".").call();
314 		git.commit().setMessage("On test").call();
315 		git.checkout().setName("master").call();
316 		git.branchCreate().setName("other").call();
317 		git.checkout().setName("other").call();
318 		writeTrashFile("b.txt", "b");
319 		writeTrashFile(".gitattributes", "b.txt filter=test");
320 		git.add().addFilepattern(".").call();
321 		git.commit().setMessage("On other").call();
322 		git.checkout().setName("master").call();
323 		assertFalse(aFile.exists());
324 		assertFalse(attributes.exists());
325 		writeTrashFile(".gitattributes", "");
326 		git.checkout().setStartPoint("test").addPath("a.txt").call();
327 		checkFile(aFile, "scsa");
328 	}
329 
330 	@Test
331 	public void testMerge() throws Exception {
332 		String builtinCommandPrefix = "jgit://builtin/test/";
333 		FilterCommandRegistry.register(builtinCommandPrefix + "smudge",
334 				new TestCommandFactory('s'));
335 		FilterCommandRegistry.register(builtinCommandPrefix + "clean",
336 				new TestCommandFactory('c'));
337 		StoredConfig config = git.getRepository().getConfig();
338 		config.setString("filter", "test", "smudge",
339 				builtinCommandPrefix + "smudge");
340 		config.setString("filter", "test", "clean",
341 				builtinCommandPrefix + "clean");
342 		config.save();
343 		// We're on the test branch. Set up two branches that are expected to
344 		// merge cleanly.
345 		File aFile = writeTrashFile("a.txt", "a");
346 		writeTrashFile(".gitattributes", "a.txt filter=test");
347 		git.add().addFilepattern(".").call();
348 		RevCommit aCommit = git.commit().setMessage("On test").call();
349 		git.checkout().setName("master").call();
350 		assertFalse(aFile.exists());
351 		git.branchCreate().setName("other").call();
352 		git.checkout().setName("other").call();
353 		writeTrashFile("b/b.txt", "b");
354 		writeTrashFile("b/.gitattributes", "b.txt filter=test");
355 		git.add().addFilepattern(".").call();
356 		git.commit().setMessage("On other").call();
357 		MergeResult result = git.merge().include(aCommit).call();
358 		assertEquals(MergeResult.MergeStatus.MERGED, result.getMergeStatus());
359 		checkFile(aFile, "scsa");
360 	}
361 
362 }