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