1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.ignore;
11
12 import static java.nio.charset.StandardCharsets.UTF_8;
13 import static org.junit.Assert.assertArrayEquals;
14 import static org.junit.Assert.assertEquals;
15 import static org.junit.Assert.assertFalse;
16
17 import java.io.BufferedInputStream;
18 import java.io.BufferedReader;
19 import java.io.ByteArrayInputStream;
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.util.LinkedHashSet;
24 import java.util.Set;
25
26 import org.eclipse.jgit.junit.RepositoryTestCase;
27 import org.eclipse.jgit.lib.StoredConfig;
28 import org.eclipse.jgit.treewalk.FileTreeIterator;
29 import org.eclipse.jgit.treewalk.TreeWalk;
30 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
31 import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter;
32 import org.eclipse.jgit.util.FS;
33 import org.eclipse.jgit.util.FS.ExecutionResult;
34 import org.eclipse.jgit.util.RawParseUtils;
35 import org.eclipse.jgit.util.TemporaryBuffer;
36 import org.junit.Before;
37 import org.junit.Test;
38
39
40
41
42
43 public class CGitIgnoreTest extends RepositoryTestCase {
44
45 @Before
46 public void initRepo() throws IOException {
47
48
49
50
51 File fakeUserGitignore = writeTrashFile(".fake_user_gitignore", "");
52 StoredConfig config = db.getConfig();
53 config.setString("core", null, "excludesFile",
54 fakeUserGitignore.getAbsolutePath());
55
56 config.setBoolean("core", null, "ignoreCase", false);
57 config.save();
58 }
59
60 private void createFiles(String... paths) throws IOException {
61 for (String path : paths) {
62 writeTrashFile(path, "x");
63 }
64 }
65
66 private String toString(TemporaryBuffer b) throws IOException {
67 return RawParseUtils.decode(b.toByteArray());
68 }
69
70 private String[] cgitIgnored() throws Exception {
71 FS fs = db.getFS();
72 ProcessBuilder builder = fs.runInShell("git", new String[] { "ls-files",
73 "--ignored", "--exclude-standard", "-o" });
74 builder.directory(db.getWorkTree());
75 builder.environment().put("HOME", fs.userHome().getAbsolutePath());
76 ExecutionResult result = fs.execute(builder,
77 new ByteArrayInputStream(new byte[0]));
78 String errorOut = toString(result.getStderr());
79 assertEquals("External git failed", "exit 0\n",
80 "exit " + result.getRc() + '\n' + errorOut);
81 try (BufferedReader r = new BufferedReader(new InputStreamReader(
82 new BufferedInputStream(result.getStdout().openInputStream()),
83 UTF_8))) {
84 return r.lines().toArray(String[]::new);
85 }
86 }
87
88 private String[] cgitUntracked() throws Exception {
89 FS fs = db.getFS();
90 ProcessBuilder builder = fs.runInShell("git",
91 new String[] { "ls-files", "--exclude-standard", "-o" });
92 builder.directory(db.getWorkTree());
93 builder.environment().put("HOME", fs.userHome().getAbsolutePath());
94 ExecutionResult result = fs.execute(builder,
95 new ByteArrayInputStream(new byte[0]));
96 String errorOut = toString(result.getStderr());
97 assertEquals("External git failed", "exit 0\n",
98 "exit " + result.getRc() + '\n' + errorOut);
99 try (BufferedReader r = new BufferedReader(new InputStreamReader(
100 new BufferedInputStream(result.getStdout().openInputStream()),
101 UTF_8))) {
102 return r.lines().toArray(String[]::new);
103 }
104 }
105
106 private void jgitIgnoredAndUntracked(LinkedHashSet<String> ignored,
107 LinkedHashSet<String> untracked) throws IOException {
108
109
110 try (TreeWalk walk = new TreeWalk(db)) {
111 FileTreeIterator iter = new FileTreeIterator(db);
112 iter.setWalkIgnoredDirectories(true);
113 walk.addTree(iter);
114 walk.setRecursive(true);
115 while (walk.next()) {
116 if (walk.getTree(WorkingTreeIterator.class).isEntryIgnored()) {
117 ignored.add(walk.getPathString());
118 } else {
119
120
121 untracked.add(walk.getPathString());
122 }
123 }
124 }
125 }
126
127 private void assertNoIgnoredVisited(Set<String> ignored) throws Exception {
128
129
130 try (TreeWalk walk = new TreeWalk(db)) {
131 walk.addTree(new FileTreeIterator(db));
132 walk.setFilter(new NotIgnoredFilter(0));
133 walk.setRecursive(true);
134 while (walk.next()) {
135 String path = walk.getPathString();
136 assertFalse("File " + path + " is ignored, should not appear",
137 ignored.contains(path));
138 }
139 }
140 }
141
142 private void assertSameAsCGit(String... notIgnored) throws Exception {
143 LinkedHashSet<String> ignored = new LinkedHashSet<>();
144 LinkedHashSet<String> untracked = new LinkedHashSet<>();
145 jgitIgnoredAndUntracked(ignored, untracked);
146 String[] cgit = cgitIgnored();
147 String[] cgitUntracked = cgitUntracked();
148 assertArrayEquals(cgit, ignored.toArray());
149 assertArrayEquals(cgitUntracked, untracked.toArray());
150 for (String notExcluded : notIgnored) {
151 assertFalse("File " + notExcluded + " should not be ignored",
152 ignored.contains(notExcluded));
153 }
154 assertNoIgnoredVisited(ignored);
155 }
156
157 @Test
158 public void testSimpleIgnored() throws Exception {
159 createFiles("a.txt", "a.tmp", "src/sub/a.txt", "src/a.tmp",
160 "src/a.txt/b.tmp", "ignored/a.tmp", "ignored/not_ignored/a.tmp",
161 "ignored/other/a.tmp");
162 writeTrashFile(".gitignore",
163 "*.txt\n" + "/ignored/*\n" + "!/ignored/not_ignored");
164 assertSameAsCGit("ignored/not_ignored/a.tmp");
165 }
166
167 @Test
168 public void testDirOnlyMatch() throws Exception {
169 createFiles("a.txt", "src/foo/a.txt", "src/a.txt", "foo/a.txt");
170 writeTrashFile(".gitignore", "foo/");
171 assertSameAsCGit();
172 }
173
174 @Test
175 public void testDirOnlyMatchDeep() throws Exception {
176 createFiles("a.txt", "src/foo/a.txt", "src/a.txt", "foo/a.txt");
177 writeTrashFile(".gitignore", "**/foo/");
178 assertSameAsCGit();
179 }
180
181 @Test
182 public void testStarMatchOnSlashNot() throws Exception {
183 createFiles("sub/a.txt", "foo/sext", "foo/s.txt");
184 writeTrashFile(".gitignore", "s*xt");
185 assertSameAsCGit("sub/a.txt");
186 }
187
188 @Test
189 public void testPrefixMatch() throws Exception {
190 createFiles("src/new/foo.txt");
191 writeTrashFile(".gitignore", "src/new");
192 assertSameAsCGit();
193 }
194
195 @Test
196 public void testDirectoryMatchSubRecursive() throws Exception {
197 createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new");
198 writeTrashFile(".gitignore", "**/src/new/");
199 assertSameAsCGit();
200 }
201
202 @Test
203 public void testDirectoryMatchSubRecursiveBacktrack() throws Exception {
204 createFiles("src/new/foo.txt", "src/src/new/foo.txt");
205 writeTrashFile(".gitignore", "**/src/new/");
206 assertSameAsCGit();
207 }
208
209 @Test
210 public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception {
211 createFiles("src/new/foo.txt", "src/src/new/foo.txt");
212 writeTrashFile(".gitignore", "**/**/src/new/");
213 assertSameAsCGit();
214 }
215
216 @Test
217 public void testDirectoryMatchSubRecursiveBacktrack3() throws Exception {
218 createFiles("x/a/a/b/foo.txt");
219 writeTrashFile(".gitignore", "**/*/a/b/");
220 assertSameAsCGit();
221 }
222
223 @Test
224 public void testDirectoryMatchSubRecursiveBacktrack4() throws Exception {
225 createFiles("x/a/a/b/foo.txt", "x/y/z/b/a/b/foo.txt",
226 "x/y/a/a/a/a/b/foo.txt", "x/y/a/a/a/a/b/a/b/foo.txt");
227 writeTrashFile(".gitignore", "**/*/a/b bar\n");
228 assertSameAsCGit();
229 }
230
231 @Test
232 public void testDirectoryMatchSubRecursiveBacktrack5() throws Exception {
233 createFiles("x/a/a/b/foo.txt", "x/y/a/b/a/b/foo.txt");
234 writeTrashFile(".gitignore", "**/*/**/a/b bar\n");
235 assertSameAsCGit();
236 }
237
238 @Test
239 public void testDirectoryWildmatchDoesNotMatchFiles1() throws Exception {
240 createFiles("a", "dir/b", "dir/sub/c");
241 writeTrashFile(".gitignore", "**/\n");
242 assertSameAsCGit();
243 }
244
245 @Test
246 public void testDirectoryWildmatchDoesNotMatchFiles2() throws Exception {
247 createFiles("a", "dir/b", "dir/sub/c");
248 writeTrashFile(".gitignore", "**/**/\n");
249 assertSameAsCGit();
250 }
251
252 @Test
253 public void testDirectoryWildmatchDoesNotMatchFiles3() throws Exception {
254 createFiles("a", "x/b", "sub/x/c", "sub/x/d/e");
255 writeTrashFile(".gitignore", "x/**/\n");
256 assertSameAsCGit();
257 }
258
259 @Test
260 public void testDirectoryWildmatchDoesNotMatchFiles4() throws Exception {
261 createFiles("a", "dir/x", "dir/sub1/x", "dir/sub2/x/y");
262 writeTrashFile(".gitignore", "**/x/\n");
263 assertSameAsCGit();
264 }
265
266 @Test
267 public void testUnescapedBracketsInGroup() throws Exception {
268 createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
269 writeTrashFile(".gitignore", "[[]]\n");
270 assertSameAsCGit();
271 }
272
273 @Test
274 public void testEscapedFirstBracketInGroup() throws Exception {
275 createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
276 writeTrashFile(".gitignore", "[\\[]]\n");
277 assertSameAsCGit();
278 }
279
280 @Test
281 public void testEscapedSecondBracketInGroup() throws Exception {
282 createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
283 writeTrashFile(".gitignore", "[[\\]]\n");
284 assertSameAsCGit();
285 }
286
287 @Test
288 public void testEscapedBothBracketsInGroup() throws Exception {
289 createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
290 writeTrashFile(".gitignore", "[\\[\\]]\n");
291 assertSameAsCGit();
292 }
293
294 @Test
295 public void testSimpleRootGitIgnoreGlobalNegation1() throws Exception {
296
297 createFiles("x1", "a/x2", "x3/y");
298 writeTrashFile(".gitignore", "*\n!x*");
299 assertSameAsCGit();
300 }
301
302 @Test
303 public void testRepeatedNegationInDifferentFiles5() throws Exception {
304
305 createFiles("a/b/e/nothere.o");
306 writeTrashFile(".gitignore", "e");
307 writeTrashFile("a/.gitignore", "e");
308 writeTrashFile("a/b/.gitignore", "!e");
309 assertSameAsCGit();
310 }
311
312 @Test
313 public void testRepeatedNegationInDifferentFilesWithWildmatcher1()
314 throws Exception {
315 createFiles("e", "x/e/f", "a/e/x1", "a/e/x2", "a/e/y", "a/e/sub/y");
316 writeTrashFile(".gitignore", "a/e/**");
317 writeTrashFile("a/.gitignore", "!e/x*");
318 assertSameAsCGit();
319 }
320
321 @Test
322 public void testRepeatedNegationInDifferentFilesWithWildmatcher2()
323 throws Exception {
324 createFiles("e", "dir/f", "dir/g/h", "a/dir/i", "a/dir/j/k",
325 "a/b/dir/l", "a/b/dir/m/n", "a/b/dir/m/o/p", "a/q/dir/r",
326 "a/q/dir/dir/s", "c/d/dir/x", "c/d/dir/y");
327 writeTrashFile(".gitignore", "**/dir/*");
328 writeTrashFile("a/.gitignore", "!dir/*");
329 writeTrashFile("a/b/.gitignore", "!**/dir/*");
330 writeTrashFile("c/.gitignore", "!d/dir/x");
331 assertSameAsCGit();
332 }
333
334 @Test
335 public void testNegationForSubDirectoryWithinIgnoredDirectoryHasNoEffect1()
336 throws Exception {
337 createFiles("e", "a/f", "a/b/g", "a/b/h/i");
338 writeTrashFile(".gitignore", "a/b");
339 writeTrashFile("a/.gitignore", "!b/*");
340 assertSameAsCGit();
341 }
342
343
344
345
346 @Test
347 public void testNegationAllExceptJavaInSrcAndExceptChildDirInSrc()
348 throws Exception {
349
350
351 createFiles("nothere.o", "src/keep.java", "src/nothere.o",
352 "src/a/keep.java", "src/a/keep.o");
353 writeTrashFile(".gitignore", "/*\n!/src/");
354 writeTrashFile("src/.gitignore", "*\n!*.java\n!*/");
355 assertSameAsCGit();
356 }
357 }