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.attributes;
44
45 import static java.nio.charset.StandardCharsets.UTF_8;
46 import static org.junit.Assert.assertArrayEquals;
47 import static org.junit.Assert.assertEquals;
48
49 import java.io.BufferedInputStream;
50 import java.io.BufferedReader;
51 import java.io.ByteArrayInputStream;
52 import java.io.File;
53 import java.io.IOException;
54 import java.io.InputStreamReader;
55 import java.util.Iterator;
56 import java.util.LinkedHashMap;
57 import java.util.Map;
58 import java.util.Set;
59
60 import org.eclipse.jgit.junit.RepositoryTestCase;
61 import org.eclipse.jgit.lib.StoredConfig;
62 import org.eclipse.jgit.treewalk.FileTreeIterator;
63 import org.eclipse.jgit.treewalk.TreeWalk;
64 import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter;
65 import org.eclipse.jgit.util.FS;
66 import org.eclipse.jgit.util.FS.ExecutionResult;
67 import org.eclipse.jgit.util.RawParseUtils;
68 import org.eclipse.jgit.util.TemporaryBuffer;
69 import org.junit.Before;
70 import org.junit.Test;
71
72
73
74
75
76 public class CGitAttributesTest extends RepositoryTestCase {
77
78 @Before
79 public void initRepo() throws IOException {
80
81
82
83 StoredConfig config = db.getConfig();
84 File fakeUserGitignore = writeTrashFile(".fake_user_gitignore", "");
85 config.setString("core", null, "excludesFile",
86 fakeUserGitignore.getAbsolutePath());
87
88 config.setBoolean("core", null, "ignoreCase", false);
89
90 config.setString("core", null, "attributesFile",
91 fakeUserGitignore.getAbsolutePath());
92 config.save();
93 }
94
95 private void createFiles(String... paths) throws IOException {
96 for (String path : paths) {
97 writeTrashFile(path, "x");
98 }
99 }
100
101 private String toString(TemporaryBuffer b) throws IOException {
102 return RawParseUtils.decode(b.toByteArray());
103 }
104
105 private Attribute fromString(String key, String value) {
106 if ("set".equals(value)) {
107 return new Attribute(key, Attribute.State.SET);
108 }
109 if ("unset".equals(value)) {
110 return new Attribute(key, Attribute.State.UNSET);
111 }
112 if ("unspecified".equals(value)) {
113 return new Attribute(key, Attribute.State.UNSPECIFIED);
114 }
115 return new Attribute(key, value);
116 }
117
118 private LinkedHashMap<String, Attributes> cgitAttributes(
119 Set<String> allFiles) throws Exception {
120 FS fs = db.getFS();
121 StringBuilder input = new StringBuilder();
122 for (String filename : allFiles) {
123 input.append(filename).append('\n');
124 }
125 ProcessBuilder builder = fs.runInShell("git",
126 new String[] { "check-attr", "--stdin", "--all" });
127 builder.directory(db.getWorkTree());
128 builder.environment().put("HOME", fs.userHome().getAbsolutePath());
129 ExecutionResult result = fs.execute(builder, new ByteArrayInputStream(
130 input.toString().getBytes(UTF_8)));
131 String errorOut = toString(result.getStderr());
132 assertEquals("External git failed", "exit 0\n",
133 "exit " + result.getRc() + '\n' + errorOut);
134 LinkedHashMap<String, Attributes> map = new LinkedHashMap<>();
135 try (BufferedReader r = new BufferedReader(new InputStreamReader(
136 new BufferedInputStream(result.getStdout().openInputStream()),
137 UTF_8))) {
138 r.lines().forEach(line -> {
139
140 int start = 0;
141 int i = line.indexOf(':');
142 String path = line.substring(0, i).trim();
143 start = i + 1;
144 i = line.indexOf(':', start);
145 String key = line.substring(start, i).trim();
146 String value = line.substring(i + 1).trim();
147 Attribute attr = fromString(key, value);
148 Attributes attrs = map.get(path);
149 if (attrs == null) {
150 attrs = new Attributes(attr);
151 map.put(path, attrs);
152 } else {
153 attrs.put(attr);
154 }
155 });
156 }
157 return map;
158 }
159
160 private LinkedHashMap<String, Attributes> jgitAttributes()
161 throws IOException {
162
163
164 LinkedHashMap<String, Attributes> result = new LinkedHashMap<>();
165 try (TreeWalk walk = new TreeWalk(db)) {
166 walk.addTree(new FileTreeIterator(db));
167 walk.setFilter(new NotIgnoredFilter(0));
168 while (walk.next()) {
169 String path = walk.getPathString();
170 if (walk.isSubtree() && !path.endsWith("/")) {
171
172
173 path += '/';
174 }
175 Attributes attrs = walk.getAttributes();
176 if (attrs != null && !attrs.isEmpty()) {
177 result.put(path, attrs);
178 } else {
179 result.put(path, null);
180 }
181 if (walk.isSubtree()) {
182 walk.enterSubtree();
183 }
184 }
185 }
186 return result;
187 }
188
189 private void assertSameAsCGit() throws Exception {
190 LinkedHashMap<String, Attributes> jgit = jgitAttributes();
191 LinkedHashMap<String, Attributes> cgit = cgitAttributes(jgit.keySet());
192
193 Iterator<Map.Entry<String, Attributes>> iterator = jgit.entrySet()
194 .iterator();
195 while (iterator.hasNext()) {
196 Map.Entry<String, Attributes> entry = iterator.next();
197 if (entry.getValue() == null) {
198 iterator.remove();
199 }
200 }
201 assertArrayEquals("JGit attributes differ from C git",
202 cgit.entrySet().toArray(), jgit.entrySet().toArray());
203 }
204
205 @Test
206 public void testBug508568() throws Exception {
207 createFiles("foo.xml/bar.jar", "sub/foo.xml/bar.jar");
208 writeTrashFile(".gitattributes", "*.xml xml\n" + "*.jar jar\n");
209 assertSameAsCGit();
210 }
211
212 @Test
213 public void testRelativePath() throws Exception {
214 createFiles("sub/foo.txt");
215 writeTrashFile("sub/.gitattributes", "sub/** sub\n" + "*.txt txt\n");
216 assertSameAsCGit();
217 }
218
219 @Test
220 public void testRelativePaths() throws Exception {
221 createFiles("sub/foo.txt", "sub/sub/bar", "foo/sub/a.txt",
222 "foo/sub/bar/a.tmp");
223 writeTrashFile(".gitattributes", "sub/** sub\n" + "*.txt txt\n");
224 assertSameAsCGit();
225 }
226
227 @Test
228 public void testNestedMatchNot() throws Exception {
229 createFiles("foo.xml/bar.jar", "foo.xml/bar.xml", "sub/b.jar",
230 "sub/b.xml");
231 writeTrashFile("sub/.gitattributes", "*.xml xml\n" + "*.jar jar\n");
232 assertSameAsCGit();
233 }
234
235 @Test
236 public void testNestedMatch() throws Exception {
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256 createFiles("foo/bar.jar", "foo/bar.xml", "sub/b.jar", "sub/b.xml",
257 "sub/foo/b.jar");
258 writeTrashFile(".gitattributes",
259 "foo/ xml\n" + "sub/foo/ sub\n" + "*.jar jar\n");
260 assertSameAsCGit();
261 }
262
263 @Test
264 public void testNestedMatchWithWildcard() throws Exception {
265
266 createFiles("foo/bar.jar", "foo/bar.xml", "sub/b.jar", "sub/b.xml",
267 "sub/foo/b.jar");
268 writeTrashFile(".gitattributes",
269 "**/foo/ xml\n" + "*/foo/ sub\n" + "*.jar jar\n");
270 assertSameAsCGit();
271 }
272
273 @Test
274 public void testNestedMatchRecursive() throws Exception {
275 createFiles("foo/bar.jar", "foo/bar.xml", "sub/b.jar", "sub/b.xml",
276 "sub/foo/b.jar");
277 writeTrashFile(".gitattributes", "foo/** xml\n" + "*.jar jar\n");
278 assertSameAsCGit();
279 }
280
281 @Test
282 public void testStarMatchOnSlashNot() throws Exception {
283 createFiles("sub/a.txt", "foo/sext", "foo/s.txt");
284 writeTrashFile(".gitattributes", "s*xt bar");
285 assertSameAsCGit();
286 }
287
288 @Test
289 public void testPrefixMatchNot() throws Exception {
290 createFiles("src/new/foo.txt");
291 writeTrashFile(".gitattributes", "src/new bar\n");
292 assertSameAsCGit();
293 }
294
295 @Test
296 public void testComplexPathMatchNot() throws Exception {
297 createFiles("src/new/foo.txt", "src/ndw");
298 writeTrashFile(".gitattributes", "s[p-s]c/n[de]w bar\n");
299 assertSameAsCGit();
300 }
301
302 @Test
303 public void testStarPathMatchNot() throws Exception {
304 createFiles("src/new/foo.txt", "src/ndw");
305 writeTrashFile(".gitattributes", "src/* bar\n");
306 assertSameAsCGit();
307 }
308
309 @Test
310 public void testDirectoryMatchSubSimple() throws Exception {
311 createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new");
312 writeTrashFile(".gitattributes", "src/new/ bar\n");
313 assertSameAsCGit();
314 }
315
316 @Test
317 public void testDirectoryMatchSubRecursive() throws Exception {
318 createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new");
319 writeTrashFile(".gitattributes", "**/src/new/ bar\n");
320 assertSameAsCGit();
321 }
322
323 @Test
324 public void testDirectoryMatchSubRecursiveBacktrack() throws Exception {
325 createFiles("src/new/foo.txt", "src/src/new/foo.txt");
326 writeTrashFile(".gitattributes", "**/src/new/ bar\n");
327 assertSameAsCGit();
328 }
329
330 @Test
331 public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception {
332 createFiles("src/new/foo.txt", "src/src/new/foo.txt");
333 writeTrashFile(".gitattributes", "**/**/src/new/ bar\n");
334 assertSameAsCGit();
335 }
336
337 @Test
338 public void testDirectoryMatchSubRecursiveBacktrack3() throws Exception {
339 createFiles("src/new/src/new/foo.txt",
340 "foo/src/new/bar/src/new/foo.txt");
341 writeTrashFile(".gitattributes", "**/src/new/ bar\n");
342 assertSameAsCGit();
343 }
344
345 @Test
346 public void testDirectoryMatchSubRecursiveBacktrack4() throws Exception {
347 createFiles("src/src/src/new/foo.txt",
348 "foo/src/src/bar/src/new/foo.txt");
349 writeTrashFile(".gitattributes", "**/src/ bar\n");
350 assertSameAsCGit();
351 }
352
353 @Test
354 public void testDirectoryMatchSubRecursiveBacktrack5() throws Exception {
355 createFiles("x/a/a/b/foo.txt", "x/y/z/b/a/b/foo.txt",
356 "x/y/a/a/a/a/b/foo.txt", "x/y/a/a/a/a/b/a/b/foo.txt");
357 writeTrashFile(".gitattributes", "**/*/a/b bar\n");
358 assertSameAsCGit();
359 }
360
361 @Test
362 public void testDirectoryMatchSubRecursiveBacktrack6() throws Exception {
363 createFiles("x/a/a/b/foo.txt", "x/y/a/b/a/b/foo.txt");
364 writeTrashFile(".gitattributes", "**/*/**/a/b bar\n");
365 assertSameAsCGit();
366 }
367
368 @Test
369 public void testDirectoryWildmatchDoesNotMatchFiles1() throws Exception {
370 createFiles("a", "dir/b", "dir/sub/c");
371 writeTrashFile(".gitattributes", "**/ bar\n");
372 assertSameAsCGit();
373 }
374
375 @Test
376 public void testDirectoryWildmatchDoesNotMatchFiles2() throws Exception {
377 createFiles("a", "dir/b", "dir/sub/c");
378 writeTrashFile(".gitattributes", "**/**/ bar\n");
379 assertSameAsCGit();
380 }
381
382 @Test
383 public void testDirectoryWildmatchDoesNotMatchFiles3() throws Exception {
384 createFiles("a", "x/b", "sub/x/c", "sub/x/d/e");
385 writeTrashFile(".gitattributes", "x/**/ bar\n");
386 assertSameAsCGit();
387 }
388
389 @Test
390 public void testDirectoryWildmatchDoesNotMatchFiles4() throws Exception {
391 createFiles("a", "dir/x", "dir/sub1/x", "dir/sub2/x/y");
392 writeTrashFile(".gitattributes", "x/**/ bar\n");
393 assertSameAsCGit();
394 }
395
396 @Test
397 public void testDirectoryMatchSubComplex() throws Exception {
398 createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new");
399 writeTrashFile(".gitattributes", "s[rs]c/n*/ bar\n");
400 assertSameAsCGit();
401 }
402
403 @Test
404 public void testDirectoryMatch() throws Exception {
405 createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new");
406 writeTrashFile(".gitattributes", "new/ bar\n");
407 assertSameAsCGit();
408 }
409
410 @Test
411 public void testBracketsInGroup() throws Exception {
412 createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
413 writeTrashFile(".gitattributes", "[[]] bar1\n" + "[\\[]] bar2\n"
414 + "[[\\]] bar3\n" + "[\\[\\]] bar4\n");
415 assertSameAsCGit();
416 }
417 }