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
44 package org.eclipse.jgit.treewalk;
45
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.File;
52 import java.io.IOException;
53 import java.security.MessageDigest;
54
55 import org.eclipse.jgit.api.Git;
56 import org.eclipse.jgit.dircache.DirCache;
57 import org.eclipse.jgit.dircache.DirCacheCheckout;
58 import org.eclipse.jgit.dircache.DirCacheEditor;
59 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
60 import org.eclipse.jgit.dircache.DirCacheEntry;
61 import org.eclipse.jgit.dircache.DirCacheIterator;
62 import org.eclipse.jgit.errors.CorruptObjectException;
63 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
64 import org.eclipse.jgit.errors.MissingObjectException;
65 import org.eclipse.jgit.junit.RepositoryTestCase;
66 import org.eclipse.jgit.lib.ConfigConstants;
67 import org.eclipse.jgit.lib.Constants;
68 import org.eclipse.jgit.lib.FileMode;
69 import org.eclipse.jgit.lib.ObjectId;
70 import org.eclipse.jgit.lib.ObjectReader;
71 import org.eclipse.jgit.revwalk.RevCommit;
72 import org.eclipse.jgit.treewalk.WorkingTreeIterator.MetadataDiff;
73 import org.eclipse.jgit.treewalk.filter.PathFilter;
74 import org.eclipse.jgit.util.FileUtils;
75 import org.eclipse.jgit.util.RawParseUtils;
76 import org.junit.Before;
77 import org.junit.Test;
78
79 public class FileTreeIteratorTest extends RepositoryTestCase {
80 private final String[] paths = { "a,", "a,b", "a/b", "a0b" };
81
82 private long[] mtime;
83
84 @Before
85 public void setUp() throws Exception {
86 super.setUp();
87
88
89
90
91
92
93
94 mtime = new long[paths.length];
95 for (int i = paths.length - 1; i >= 0; i--) {
96 final String s = paths[i];
97 writeTrashFile(s, s);
98 mtime[i] = new File(trash, s).lastModified();
99 }
100 }
101
102 @Test
103 public void testGetEntryContentLength() throws Exception {
104 final FileTreeIterator fti = new FileTreeIterator(db);
105 fti.next(1);
106 assertEquals(3, fti.getEntryContentLength());
107 fti.back(1);
108 assertEquals(2, fti.getEntryContentLength());
109 fti.next(1);
110 assertEquals(3, fti.getEntryContentLength());
111 fti.reset();
112 assertEquals(2, fti.getEntryContentLength());
113 }
114
115 @Test
116 public void testEmptyIfRootIsFile() throws Exception {
117 final File r = new File(trash, paths[0]);
118 assertTrue(r.isFile());
119 final FileTreeIterator fti = new FileTreeIterator(r, db.getFS(),
120 db.getConfig().get(WorkingTreeOptions.KEY));
121 assertTrue(fti.first());
122 assertTrue(fti.eof());
123 }
124
125 @Test
126 public void testEmptyIfRootDoesNotExist() throws Exception {
127 final File r = new File(trash, "not-existing-file");
128 assertFalse(r.exists());
129 final FileTreeIterator fti = new FileTreeIterator(r, db.getFS(),
130 db.getConfig().get(WorkingTreeOptions.KEY));
131 assertTrue(fti.first());
132 assertTrue(fti.eof());
133 }
134
135 @Test
136 public void testEmptyIfRootIsEmpty() throws Exception {
137 final File r = new File(trash, "not-existing-file");
138 assertFalse(r.exists());
139 FileUtils.mkdir(r);
140
141 final FileTreeIterator fti = new FileTreeIterator(r, db.getFS(),
142 db.getConfig().get(WorkingTreeOptions.KEY));
143 assertTrue(fti.first());
144 assertTrue(fti.eof());
145 }
146
147 @Test
148 public void testEmptyIteratorOnEmptyDirectory() throws Exception {
149 String nonExistingFileName = "not-existing-file";
150 final File r = new File(trash, nonExistingFileName);
151 assertFalse(r.exists());
152 FileUtils.mkdir(r);
153
154 final FileTreeIterator parent = new FileTreeIterator(db);
155
156 while (!parent.getEntryPathString().equals(nonExistingFileName))
157 parent.next(1);
158
159 final FileTreeIterator childIter = new FileTreeIterator(parent, r,
160 db.getFS());
161 assertTrue(childIter.first());
162 assertTrue(childIter.eof());
163
164 String parentPath = parent.getEntryPathString();
165 assertEquals(nonExistingFileName, parentPath);
166
167
168
169 String childPath = childIter.getEntryPathString();
170
171
172 EmptyTreeIterator e = childIter.createEmptyTreeIterator();
173 assertNotNull(e);
174
175
176
177
178 assertEquals(parentPath, parent.getEntryPathString());
179 assertEquals(parentPath + "/", childPath);
180 assertEquals(parentPath + "/", childIter.getEntryPathString());
181 assertEquals(childPath + "/", e.getEntryPathString());
182 }
183
184 @Test
185 public void testSimpleIterate() throws Exception {
186 final FileTreeIterator top = new FileTreeIterator(trash, db.getFS(),
187 db.getConfig().get(WorkingTreeOptions.KEY));
188
189 assertTrue(top.first());
190 assertFalse(top.eof());
191 assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode);
192 assertEquals(paths[0], nameOf(top));
193 assertEquals(paths[0].length(), top.getEntryLength());
194 assertEquals(mtime[0], top.getEntryLastModified());
195
196 top.next(1);
197 assertFalse(top.first());
198 assertFalse(top.eof());
199 assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode);
200 assertEquals(paths[1], nameOf(top));
201 assertEquals(paths[1].length(), top.getEntryLength());
202 assertEquals(mtime[1], top.getEntryLastModified());
203
204 top.next(1);
205 assertFalse(top.first());
206 assertFalse(top.eof());
207 assertEquals(FileMode.TREE.getBits(), top.mode);
208
209 final ObjectReader reader = db.newObjectReader();
210 final AbstractTreeIterator sub = top.createSubtreeIterator(reader);
211 assertTrue(sub instanceof FileTreeIterator);
212 final FileTreeIterator subfti = (FileTreeIterator) sub;
213 assertTrue(sub.first());
214 assertFalse(sub.eof());
215 assertEquals(paths[2], nameOf(sub));
216 assertEquals(paths[2].length(), subfti.getEntryLength());
217 assertEquals(mtime[2], subfti.getEntryLastModified());
218
219 sub.next(1);
220 assertTrue(sub.eof());
221
222 top.next(1);
223 assertFalse(top.first());
224 assertFalse(top.eof());
225 assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode);
226 assertEquals(paths[3], nameOf(top));
227 assertEquals(paths[3].length(), top.getEntryLength());
228 assertEquals(mtime[3], top.getEntryLastModified());
229
230 top.next(1);
231 assertTrue(top.eof());
232 }
233
234 @Test
235 public void testComputeFileObjectId() throws Exception {
236 final FileTreeIterator top = new FileTreeIterator(trash, db.getFS(),
237 db.getConfig().get(WorkingTreeOptions.KEY));
238
239 final MessageDigest md = Constants.newMessageDigest();
240 md.update(Constants.encodeASCII(Constants.TYPE_BLOB));
241 md.update((byte) ' ');
242 md.update(Constants.encodeASCII(paths[0].length()));
243 md.update((byte) 0);
244 md.update(Constants.encode(paths[0]));
245 final ObjectId expect = ObjectId.fromRaw(md.digest());
246
247 assertEquals(expect, top.getEntryObjectId());
248
249
250
251 FileUtils.delete(new File(trash, paths[0]));
252 assertEquals(expect, top.getEntryObjectId());
253 }
254
255 @Test
256 public void testDirCacheMatchingId() throws Exception {
257 File f = writeTrashFile("file", "content");
258 Git git = new Git(db);
259 writeTrashFile("file", "content");
260 fsTick(f);
261 git.add().addFilepattern("file").call();
262 DirCacheEntry dce = db.readDirCache().getEntry("file");
263 TreeWalk tw = new TreeWalk(db);
264 FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db
265 .getConfig().get(WorkingTreeOptions.KEY));
266 tw.addTree(fti);
267 DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
268 tw.addTree(dci);
269 fti.setDirCacheIterator(tw, 1);
270 while (tw.next() && !tw.getPathString().equals("file")) {
271
272 }
273 assertEquals(MetadataDiff.EQUAL, fti.compareMetadata(dce));
274 ObjectId fromRaw = ObjectId.fromRaw(fti.idBuffer(), fti.idOffset());
275 assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
276 fromRaw.getName());
277 try (ObjectReader objectReader = db.newObjectReader()) {
278 assertFalse(fti.isModified(dce, false, objectReader));
279 }
280 }
281
282 @Test
283 public void testIsModifiedSymlinkAsFile() throws Exception {
284 writeTrashFile("symlink", "content");
285 Git git = new Git(db);
286 db.getConfig().setString(ConfigConstants.CONFIG_CORE_SECTION, null,
287 ConfigConstants.CONFIG_KEY_SYMLINKS, "false");
288 git.add().addFilepattern("symlink").call();
289 git.commit().setMessage("commit").call();
290
291
292 DirCacheEntry dce = db.readDirCache().getEntry("symlink");
293 dce.setFileMode(FileMode.SYMLINK);
294 try (ObjectReader objectReader = db.newObjectReader()) {
295 DirCacheCheckout.checkoutEntry(db, dce, objectReader);
296
297 FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(),
298 db.getConfig().get(WorkingTreeOptions.KEY));
299 while (!fti.getEntryPathString().equals("symlink"))
300 fti.next(1);
301 assertFalse(fti.isModified(dce, false, objectReader));
302 }
303 }
304
305 @Test
306 public void testIsModifiedFileSmudged() throws Exception {
307 File f = writeTrashFile("file", "content");
308 Git git = new Git(db);
309
310
311 fsTick(f);
312 writeTrashFile("file", "content");
313 long lastModified = f.lastModified();
314 git.add().addFilepattern("file").call();
315 writeTrashFile("file", "conten2");
316 f.setLastModified(lastModified);
317
318
319
320
321 db.getIndexFile().setLastModified(lastModified);
322 DirCacheEntry dce = db.readDirCache().getEntry("file");
323 FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db
324 .getConfig().get(WorkingTreeOptions.KEY));
325 while (!fti.getEntryPathString().equals("file"))
326 fti.next(1);
327
328
329 assertEquals(MetadataDiff.SMUDGED, fti.compareMetadata(dce));
330 try (ObjectReader objectReader = db.newObjectReader()) {
331 assertTrue(fti.isModified(dce, false, objectReader));
332 }
333 }
334
335 @Test
336 public void submoduleHeadMatchesIndex() throws Exception {
337 Git git = new Git(db);
338 writeTrashFile("file.txt", "content");
339 git.add().addFilepattern("file.txt").call();
340 final RevCommit id = git.commit().setMessage("create file").call();
341 final String path = "sub";
342 DirCache cache = db.lockDirCache();
343 DirCacheEditor editor = cache.editor();
344 editor.add(new PathEdit(path) {
345
346 public void apply(DirCacheEntry ent) {
347 ent.setFileMode(FileMode.GITLINK);
348 ent.setObjectId(id);
349 }
350 });
351 editor.commit();
352
353 Git.cloneRepository().setURI(db.getDirectory().toURI().toString())
354 .setDirectory(new File(db.getWorkTree(), path)).call()
355 .getRepository().close();
356
357 TreeWalk walk = new TreeWalk(db);
358 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
359 FileTreeIterator workTreeIter = new FileTreeIterator(db);
360 walk.addTree(indexIter);
361 walk.addTree(workTreeIter);
362 walk.setFilter(PathFilter.create(path));
363
364 assertTrue(walk.next());
365 assertTrue(indexIter.idEqual(workTreeIter));
366 }
367
368 @Test
369 public void submoduleWithNoGitDirectory() throws Exception {
370 Git git = new Git(db);
371 writeTrashFile("file.txt", "content");
372 git.add().addFilepattern("file.txt").call();
373 final RevCommit id = git.commit().setMessage("create file").call();
374 final String path = "sub";
375 DirCache cache = db.lockDirCache();
376 DirCacheEditor editor = cache.editor();
377 editor.add(new PathEdit(path) {
378
379 public void apply(DirCacheEntry ent) {
380 ent.setFileMode(FileMode.GITLINK);
381 ent.setObjectId(id);
382 }
383 });
384 editor.commit();
385
386 File submoduleRoot = new File(db.getWorkTree(), path);
387 assertTrue(submoduleRoot.mkdir());
388 assertTrue(new File(submoduleRoot, Constants.DOT_GIT).mkdir());
389
390 TreeWalk walk = new TreeWalk(db);
391 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
392 FileTreeIterator workTreeIter = new FileTreeIterator(db);
393 walk.addTree(indexIter);
394 walk.addTree(workTreeIter);
395 walk.setFilter(PathFilter.create(path));
396
397 assertTrue(walk.next());
398 assertFalse(indexIter.idEqual(workTreeIter));
399 assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId());
400 }
401
402 @Test
403 public void submoduleWithNoHead() throws Exception {
404 Git git = new Git(db);
405 writeTrashFile("file.txt", "content");
406 git.add().addFilepattern("file.txt").call();
407 final RevCommit id = git.commit().setMessage("create file").call();
408 final String path = "sub";
409 DirCache cache = db.lockDirCache();
410 DirCacheEditor editor = cache.editor();
411 editor.add(new PathEdit(path) {
412
413 public void apply(DirCacheEntry ent) {
414 ent.setFileMode(FileMode.GITLINK);
415 ent.setObjectId(id);
416 }
417 });
418 editor.commit();
419
420 assertNotNull(Git.init().setDirectory(new File(db.getWorkTree(), path))
421 .call().getRepository());
422
423 TreeWalk walk = new TreeWalk(db);
424 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
425 FileTreeIterator workTreeIter = new FileTreeIterator(db);
426 walk.addTree(indexIter);
427 walk.addTree(workTreeIter);
428 walk.setFilter(PathFilter.create(path));
429
430 assertTrue(walk.next());
431 assertFalse(indexIter.idEqual(workTreeIter));
432 assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId());
433 }
434
435 @Test
436 public void submoduleDirectoryIterator() throws Exception {
437 Git git = new Git(db);
438 writeTrashFile("file.txt", "content");
439 git.add().addFilepattern("file.txt").call();
440 final RevCommit id = git.commit().setMessage("create file").call();
441 final String path = "sub";
442 DirCache cache = db.lockDirCache();
443 DirCacheEditor editor = cache.editor();
444 editor.add(new PathEdit(path) {
445
446 public void apply(DirCacheEntry ent) {
447 ent.setFileMode(FileMode.GITLINK);
448 ent.setObjectId(id);
449 }
450 });
451 editor.commit();
452
453 Git.cloneRepository().setURI(db.getDirectory().toURI().toString())
454 .setDirectory(new File(db.getWorkTree(), path)).call()
455 .getRepository().close();
456
457 TreeWalk walk = new TreeWalk(db);
458 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
459 FileTreeIterator workTreeIter = new FileTreeIterator(db.getWorkTree(),
460 db.getFS(), db.getConfig().get(WorkingTreeOptions.KEY));
461 walk.addTree(indexIter);
462 walk.addTree(workTreeIter);
463 walk.setFilter(PathFilter.create(path));
464
465 assertTrue(walk.next());
466 assertTrue(indexIter.idEqual(workTreeIter));
467 }
468
469 @Test
470 public void submoduleNestedWithHeadMatchingIndex() throws Exception {
471 Git git = new Git(db);
472 writeTrashFile("file.txt", "content");
473 git.add().addFilepattern("file.txt").call();
474 final RevCommit id = git.commit().setMessage("create file").call();
475 final String path = "sub/dir1/dir2";
476 DirCache cache = db.lockDirCache();
477 DirCacheEditor editor = cache.editor();
478 editor.add(new PathEdit(path) {
479
480 public void apply(DirCacheEntry ent) {
481 ent.setFileMode(FileMode.GITLINK);
482 ent.setObjectId(id);
483 }
484 });
485 editor.commit();
486
487 Git.cloneRepository().setURI(db.getDirectory().toURI().toString())
488 .setDirectory(new File(db.getWorkTree(), path)).call()
489 .getRepository().close();
490
491 TreeWalk walk = new TreeWalk(db);
492 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
493 FileTreeIterator workTreeIter = new FileTreeIterator(db);
494 walk.addTree(indexIter);
495 walk.addTree(workTreeIter);
496 walk.setFilter(PathFilter.create(path));
497
498 assertTrue(walk.next());
499 assertTrue(indexIter.idEqual(workTreeIter));
500 }
501
502 @Test
503 public void idOffset() throws Exception {
504 Git git = new Git(db);
505 writeTrashFile("fileAinfsonly", "A");
506 File fileBinindex = writeTrashFile("fileBinindex", "B");
507 fsTick(fileBinindex);
508 git.add().addFilepattern("fileBinindex").call();
509 writeTrashFile("fileCinfsonly", "C");
510 TreeWalk tw = new TreeWalk(db);
511 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
512 FileTreeIterator workTreeIter = new FileTreeIterator(db);
513 tw.addTree(indexIter);
514 tw.addTree(workTreeIter);
515 workTreeIter.setDirCacheIterator(tw, 0);
516 assertEntry("d46c305e85b630558ee19cc47e73d2e5c8c64cdc", "a,", tw);
517 assertEntry("58ee403f98538ec02409538b3f80adf610accdec", "a,b", tw);
518 assertEntry("0000000000000000000000000000000000000000", "a", tw);
519 assertEntry("b8d30ff397626f0f1d3538d66067edf865e201d6", "a0b", tw);
520
521
522 assertEntry("8c7e5a667f1b771847fe88c01c3de34413a1b220",
523 "fileAinfsonly", tw);
524 assertEntry("7371f47a6f8bd23a8fa1a8b2a9479cdd76380e54", "fileBinindex",
525 tw);
526 assertEntry("96d80cd6c4e7158dbebd0849f4fb7ce513e5828c",
527 "fileCinfsonly", tw);
528 assertFalse(tw.next());
529 }
530
531 private static void assertEntry(String sha1string, String path, TreeWalk tw)
532 throws MissingObjectException, IncorrectObjectTypeException,
533 CorruptObjectException, IOException {
534 assertTrue(tw.next());
535 assertEquals(path, tw.getPathString());
536 assertEquals(sha1string, tw.getObjectId(1).getName() );
537 }
538
539 private static String nameOf(final AbstractTreeIterator i) {
540 return RawParseUtils.decode(Constants.CHARSET, i.path, 0, i.pathLen);
541 }
542 }