1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.ignore;
44
45 import static java.nio.charset.StandardCharsets.UTF_8;
46 import static org.eclipse.jgit.junit.Assert.assertEquals;
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertFalse;
49 import static org.junit.Assert.assertNotNull;
50 import static org.junit.Assert.assertTrue;
51
52 import java.io.ByteArrayInputStream;
53 import java.io.File;
54 import java.io.IOException;
55 import java.io.InputStream;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58
59 import org.eclipse.jgit.ignore.IgnoreNode.MatchResult;
60 import org.eclipse.jgit.junit.RepositoryTestCase;
61 import org.eclipse.jgit.lib.FileMode;
62 import org.eclipse.jgit.treewalk.FileTreeIterator;
63 import org.eclipse.jgit.treewalk.TreeWalk;
64 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
65 import org.eclipse.jgit.util.FileUtils;
66 import org.eclipse.jgit.util.SystemReader;
67 import org.junit.Test;
68
69
70
71
72 public class IgnoreNodeTest extends RepositoryTestCase {
73 private static final FileMode D = FileMode.TREE;
74
75 private static final FileMode F = FileMode.REGULAR_FILE;
76
77 private static final boolean ignored = true;
78
79 private static final boolean tracked = false;
80
81 private TreeWalk walk;
82
83 @Test
84 public void testRules() throws IOException {
85 writeIgnoreFile(".git/info/exclude", "*~", "/out");
86
87 writeIgnoreFile(".gitignore", "*.o", "/config");
88 writeTrashFile("config/secret", "");
89 writeTrashFile("mylib.c", "");
90 writeTrashFile("mylib.c~", "");
91 writeTrashFile("mylib.o", "");
92
93 writeTrashFile("out/object/foo.exe", "");
94 writeIgnoreFile("src/config/.gitignore", "lex.out");
95 writeTrashFile("src/config/lex.out", "");
96 writeTrashFile("src/config/config.c", "");
97 writeTrashFile("src/config/config.c~", "");
98 writeTrashFile("src/config/old/lex.out", "");
99
100 beginWalk();
101 assertEntry(F, tracked, ".gitignore");
102 assertEntry(D, ignored, "config");
103 assertEntry(F, ignored, "config/secret");
104 assertEntry(F, tracked, "mylib.c");
105 assertEntry(F, ignored, "mylib.c~");
106 assertEntry(F, ignored, "mylib.o");
107
108 assertEntry(D, ignored, "out");
109 assertEntry(D, ignored, "out/object");
110 assertEntry(F, ignored, "out/object/foo.exe");
111
112 assertEntry(D, tracked, "src");
113 assertEntry(D, tracked, "src/config");
114 assertEntry(F, tracked, "src/config/.gitignore");
115 assertEntry(F, tracked, "src/config/config.c");
116 assertEntry(F, ignored, "src/config/config.c~");
117 assertEntry(F, ignored, "src/config/lex.out");
118 assertEntry(D, tracked, "src/config/old");
119 assertEntry(F, ignored, "src/config/old/lex.out");
120 endWalk();
121 }
122
123 @Test
124 public void testNegation() throws IOException {
125
126 writeIgnoreFile(".gitignore", "*.o", "d");
127
128
129 writeIgnoreFile("src/a/b/.gitignore", "!keep.o");
130 writeTrashFile("src/a/b/keep.o", "");
131 writeTrashFile("src/a/b/nothere.o", "");
132
133
134 writeIgnoreFile("src/c/.gitignore", "!d");
135
136 writeIgnoreFile("src/c/d/.gitignore", "!keep.o");
137 writeTrashFile("src/c/d/keep.o", "");
138 writeTrashFile("src/c/d/nothere.o", "");
139
140 beginWalk();
141 assertEntry(F, tracked, ".gitignore");
142 assertEntry(D, tracked, "src");
143 assertEntry(D, tracked, "src/a");
144 assertEntry(D, tracked, "src/a/b");
145 assertEntry(F, tracked, "src/a/b/.gitignore");
146 assertEntry(F, tracked, "src/a/b/keep.o");
147 assertEntry(F, ignored, "src/a/b/nothere.o");
148
149 assertEntry(D, tracked, "src/c");
150 assertEntry(F, tracked, "src/c/.gitignore");
151 assertEntry(D, tracked, "src/c/d");
152 assertEntry(F, tracked, "src/c/d/.gitignore");
153 assertEntry(F, tracked, "src/c/d/keep.o");
154
155 assertEntry(F, ignored, "src/c/d/nothere.o");
156 endWalk();
157 }
158
159
160
161
162 @Test
163 public void testNegateAllExceptJavaInSrc() throws IOException {
164
165 writeIgnoreFile(".gitignore", "/*", "!/src/");
166 writeTrashFile("nothere.o", "");
167
168
169 writeIgnoreFile("src/.gitignore", "*", "!*.java");
170
171 writeTrashFile("src/keep.java", "");
172 writeTrashFile("src/nothere.o", "");
173 writeTrashFile("src/a/nothere.o", "");
174
175 beginWalk();
176 assertEntry(F, ignored, ".gitignore");
177 assertEntry(F, ignored, "nothere.o");
178 assertEntry(D, tracked, "src");
179 assertEntry(F, ignored, "src/.gitignore");
180 assertEntry(D, ignored, "src/a");
181 assertEntry(F, ignored, "src/a/nothere.o");
182 assertEntry(F, tracked, "src/keep.java");
183 assertEntry(F, ignored, "src/nothere.o");
184 endWalk();
185 }
186
187
188
189
190 @Test
191 public void testNegationAllExceptJavaInSrcAndExceptChildDirInSrc()
192 throws IOException {
193
194 writeIgnoreFile(".gitignore", "/*", "!/src/");
195 writeTrashFile("nothere.o", "");
196
197
198
199 writeIgnoreFile("src/.gitignore", "*", "!*.java", "!*/");
200
201 writeTrashFile("src/keep.java", "");
202 writeTrashFile("src/nothere.o", "");
203 writeTrashFile("src/a/keep.java", "");
204 writeTrashFile("src/a/keep.o", "");
205
206 beginWalk();
207 assertEntry(F, ignored, ".gitignore");
208 assertEntry(F, ignored, "nothere.o");
209 assertEntry(D, tracked, "src");
210 assertEntry(F, ignored, "src/.gitignore");
211 assertEntry(D, tracked, "src/a");
212 assertEntry(F, tracked, "src/a/keep.java");
213 assertEntry(F, tracked, "src/a/keep.o");
214 assertEntry(F, tracked, "src/keep.java");
215 assertEntry(F, ignored, "src/nothere.o");
216 endWalk();
217 }
218
219
220
221
222 @Test
223 public void testRepeatedNegation() throws IOException {
224 writeIgnoreFile(".gitignore", "e", "!e", "e", "!e", "e");
225
226 writeTrashFile("e/nothere.o", "");
227
228 beginWalk();
229 assertEntry(F, tracked, ".gitignore");
230 assertEntry(D, ignored, "e");
231 assertEntry(F, ignored, "e/nothere.o");
232 endWalk();
233 }
234
235
236
237
238 @Test
239 public void testRepeatedNegationInDifferentFiles1() throws IOException {
240 writeIgnoreFile(".gitignore", "*.o", "e");
241
242 writeIgnoreFile("e/.gitignore", "!e");
243 writeTrashFile("e/nothere.o", "");
244
245 beginWalk();
246 assertEntry(F, tracked, ".gitignore");
247 assertEntry(D, ignored, "e");
248 assertEntry(F, ignored, "e/.gitignore");
249 assertEntry(F, ignored, "e/nothere.o");
250 endWalk();
251 }
252
253
254
255
256 @Test
257 public void testRepeatedNegationInDifferentFiles2() throws IOException {
258 writeIgnoreFile(".gitignore", "*.o", "e");
259
260 writeIgnoreFile("a/.gitignore", "!e");
261 writeTrashFile("a/e/nothere.o", "");
262
263 beginWalk();
264 assertEntry(F, tracked, ".gitignore");
265 assertEntry(D, tracked, "a");
266 assertEntry(F, tracked, "a/.gitignore");
267 assertEntry(D, tracked, "a/e");
268 assertEntry(F, ignored, "a/e/nothere.o");
269 endWalk();
270 }
271
272
273
274
275 @Test
276 public void testRepeatedNegationInDifferentFiles3() throws IOException {
277 writeIgnoreFile(".gitignore", "*.o");
278
279 writeIgnoreFile("a/.gitignore", "e");
280 writeIgnoreFile("a/b/.gitignore", "!e");
281 writeTrashFile("a/b/e/nothere.o", "");
282
283 beginWalk();
284 assertEntry(F, tracked, ".gitignore");
285 assertEntry(D, tracked, "a");
286 assertEntry(F, tracked, "a/.gitignore");
287 assertEntry(D, tracked, "a/b");
288 assertEntry(F, tracked, "a/b/.gitignore");
289 assertEntry(D, tracked, "a/b/e");
290 assertEntry(F, ignored, "a/b/e/nothere.o");
291 endWalk();
292 }
293
294 @Test
295 public void testRepeatedNegationInDifferentFiles4() throws IOException {
296 writeIgnoreFile(".gitignore", "*.o");
297
298 writeIgnoreFile("a/.gitignore", "e");
299
300
301 writeIgnoreFile("a/b/.gitignore", "#");
302 writeIgnoreFile("a/b/c/.gitignore", "!e");
303 writeTrashFile("a/b/c/e/nothere.o", "");
304
305 beginWalk();
306 assertEntry(F, tracked, ".gitignore");
307 assertEntry(D, tracked, "a");
308 assertEntry(F, tracked, "a/.gitignore");
309 assertEntry(D, tracked, "a/b");
310 assertEntry(F, tracked, "a/b/.gitignore");
311 assertEntry(D, tracked, "a/b/c");
312 assertEntry(F, tracked, "a/b/c/.gitignore");
313 assertEntry(D, tracked, "a/b/c/e");
314 assertEntry(F, ignored, "a/b/c/e/nothere.o");
315 endWalk();
316 }
317
318 @Test
319 public void testEmptyIgnoreNode() {
320
321
322
323 IgnoreNode node = new IgnoreNode();
324 assertEquals(MatchResult.CHECK_PARENT, node.isIgnored("", false));
325 assertEquals(MatchResult.CHECK_PARENT, node.isIgnored("", false, false));
326 assertEquals(MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH,
327 node.isIgnored("", false, true));
328 }
329
330 @Test
331 public void testEmptyIgnoreRules() throws IOException {
332 IgnoreNode node = new IgnoreNode();
333 node.parse(writeToString("", "#", "!", "[[=a=]]"));
334 assertEquals(new ArrayList<>(), node.getRules());
335 node.parse(writeToString(" ", " / "));
336 assertEquals(2, node.getRules().size());
337 }
338
339 @Test
340 public void testSlashOnlyMatchesDirectory() throws IOException {
341 writeIgnoreFile(".gitignore", "out/");
342 writeTrashFile("out", "");
343
344 beginWalk();
345 assertEntry(F, tracked, ".gitignore");
346 assertEntry(F, tracked, "out");
347
348 FileUtils.delete(new File(trash, "out"));
349 writeTrashFile("out/foo", "");
350
351 beginWalk();
352 assertEntry(F, tracked, ".gitignore");
353 assertEntry(D, ignored, "out");
354 assertEntry(F, ignored, "out/foo");
355 endWalk();
356 }
357
358 @Test
359 public void testSlashMatchesDirectory() throws IOException {
360 writeIgnoreFile(".gitignore", "out2/");
361
362 writeTrashFile("out1/out1", "");
363 writeTrashFile("out1/out2", "");
364 writeTrashFile("out2/out1", "");
365 writeTrashFile("out2/out2", "");
366
367 beginWalk();
368 assertEntry(F, tracked, ".gitignore");
369 assertEntry(D, tracked, "out1");
370 assertEntry(F, tracked, "out1/out1");
371 assertEntry(F, tracked, "out1/out2");
372 assertEntry(D, ignored, "out2");
373 assertEntry(F, ignored, "out2/out1");
374 assertEntry(F, ignored, "out2/out2");
375 endWalk();
376 }
377
378 @Test
379 public void testWildcardWithSlashMatchesDirectory() throws IOException {
380 writeIgnoreFile(".gitignore", "out2*/");
381
382 writeTrashFile("out1/out1.txt", "");
383 writeTrashFile("out1/out2", "");
384 writeTrashFile("out1/out2.txt", "");
385 writeTrashFile("out1/out2x/a", "");
386 writeTrashFile("out2/out1.txt", "");
387 writeTrashFile("out2/out2.txt", "");
388 writeTrashFile("out2x/out1.txt", "");
389 writeTrashFile("out2x/out2.txt", "");
390
391 beginWalk();
392 assertEntry(F, tracked, ".gitignore");
393 assertEntry(D, tracked, "out1");
394 assertEntry(F, tracked, "out1/out1.txt");
395 assertEntry(F, tracked, "out1/out2");
396 assertEntry(F, tracked, "out1/out2.txt");
397 assertEntry(D, ignored, "out1/out2x");
398 assertEntry(F, ignored, "out1/out2x/a");
399 assertEntry(D, ignored, "out2");
400 assertEntry(F, ignored, "out2/out1.txt");
401 assertEntry(F, ignored, "out2/out2.txt");
402 assertEntry(D, ignored, "out2x");
403 assertEntry(F, ignored, "out2x/out1.txt");
404 assertEntry(F, ignored, "out2x/out2.txt");
405 endWalk();
406 }
407
408 @Test
409 public void testWithSlashDoesNotMatchInSubDirectory() throws IOException {
410 writeIgnoreFile(".gitignore", "a/b");
411 writeTrashFile("a/a", "");
412 writeTrashFile("a/b", "");
413 writeTrashFile("src/a/a", "");
414 writeTrashFile("src/a/b", "");
415
416 beginWalk();
417 assertEntry(F, tracked, ".gitignore");
418 assertEntry(D, tracked, "a");
419 assertEntry(F, tracked, "a/a");
420 assertEntry(F, ignored, "a/b");
421 assertEntry(D, tracked, "src");
422 assertEntry(D, tracked, "src/a");
423 assertEntry(F, tracked, "src/a/a");
424 assertEntry(F, tracked, "src/a/b");
425 endWalk();
426 }
427
428 @Test
429 public void testNoPatterns() throws IOException {
430 writeIgnoreFile(".gitignore", "", " ", "# comment", "/");
431 writeTrashFile("a/a", "");
432
433 beginWalk();
434 assertEntry(F, tracked, ".gitignore");
435 assertEntry(D, tracked, "a");
436 assertEntry(F, tracked, "a/a");
437 endWalk();
438 }
439
440 @Test
441 public void testLeadingSpaces() throws IOException {
442 writeTrashFile(" a/ a", "");
443 writeTrashFile(" a/ a", "");
444 writeTrashFile(" a/a", "");
445 writeTrashFile(" a/ a", "");
446 writeTrashFile(" a/ a", "");
447 writeTrashFile(" a/a", "");
448 writeIgnoreFile(".gitignore", " a", " a");
449 writeTrashFile("a/ a", "");
450 writeTrashFile("a/ a", "");
451 writeTrashFile("a/a", "");
452
453 beginWalk();
454 assertEntry(D, ignored, " a");
455 assertEntry(F, ignored, " a/ a");
456 assertEntry(F, ignored, " a/ a");
457 assertEntry(F, ignored, " a/a");
458 assertEntry(D, ignored, " a");
459 assertEntry(F, ignored, " a/ a");
460 assertEntry(F, ignored, " a/ a");
461 assertEntry(F, ignored, " a/a");
462 assertEntry(F, tracked, ".gitignore");
463 assertEntry(D, tracked, "a");
464 assertEntry(F, ignored, "a/ a");
465 assertEntry(F, ignored, "a/ a");
466 assertEntry(F, tracked, "a/a");
467 endWalk();
468 }
469
470 @Test
471 public void testTrailingSpaces() throws IOException {
472
473
474 org.junit.Assume.assumeFalse(SystemReader.getInstance().isWindows());
475 writeTrashFile("a /a", "");
476 writeTrashFile("a /a ", "");
477 writeTrashFile("a /a ", "");
478 writeTrashFile("a /a", "");
479 writeTrashFile("a /a ", "");
480 writeTrashFile("a /a ", "");
481 writeTrashFile("a/a", "");
482 writeTrashFile("a/a ", "");
483 writeTrashFile("a/a ", "");
484 writeTrashFile("b/c", "");
485
486 writeIgnoreFile(".gitignore", "a\\ ", "a \\ ", "b/ ");
487
488 beginWalk();
489 assertEntry(F, tracked, ".gitignore");
490 assertEntry(D, ignored, "a ");
491 assertEntry(F, ignored, "a /a");
492 assertEntry(F, ignored, "a /a ");
493 assertEntry(F, ignored, "a /a ");
494 assertEntry(D, ignored, "a ");
495 assertEntry(F, ignored, "a /a");
496 assertEntry(F, ignored, "a /a ");
497 assertEntry(F, ignored, "a /a ");
498 assertEntry(D, tracked, "a");
499 assertEntry(F, tracked, "a/a");
500 assertEntry(F, ignored, "a/a ");
501 assertEntry(F, ignored, "a/a ");
502 assertEntry(D, ignored, "b");
503 assertEntry(F, ignored, "b/c");
504 endWalk();
505 }
506
507 @Test
508 public void testToString() throws Exception {
509 assertEquals(Arrays.asList("").toString(), new IgnoreNode().toString());
510 assertEquals(Arrays.asList("hello").toString(),
511 new IgnoreNode(Arrays.asList(new FastIgnoreRule("hello")))
512 .toString());
513 }
514
515 private void beginWalk() {
516 walk = new TreeWalk(db);
517 walk.addTree(new FileTreeIterator(db));
518 }
519
520 private void endWalk() throws IOException {
521 assertFalse("Not all files tested", walk.next());
522 }
523
524 private void assertEntry(FileMode type, boolean entryIgnored,
525 String pathName) throws IOException {
526 assertTrue("walk has entry", walk.next());
527 assertEquals(pathName, walk.getPathString());
528 assertEquals(type, walk.getFileMode(0));
529
530 WorkingTreeIterator itr = walk.getTree(0, WorkingTreeIterator.class);
531 assertNotNull("has tree", itr);
532 assertEquals("is ignored", entryIgnored, itr.isEntryIgnored());
533 if (D.equals(type))
534 walk.enterSubtree();
535 }
536
537 private void writeIgnoreFile(String name, String... rules)
538 throws IOException {
539 StringBuilder data = new StringBuilder();
540 for (String line : rules)
541 data.append(line + "\n");
542 writeTrashFile(name, data.toString());
543 }
544
545 private InputStream writeToString(String... rules) {
546 StringBuilder data = new StringBuilder();
547 for (String line : rules) {
548 data.append(line + "\n");
549 }
550 return new ByteArrayInputStream(data.toString().getBytes(UTF_8));
551 }
552 }