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