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 java.nio.charset.StandardCharsets.UTF_8;
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertFalse;
49 import static org.junit.Assert.assertNotNull;
50 import static org.junit.Assert.assertTrue;
51
52 import java.io.File;
53 import java.io.IOException;
54 import java.security.MessageDigest;
55 import java.time.Instant;
56
57 import org.eclipse.jgit.api.Git;
58 import org.eclipse.jgit.api.ResetCommand.ResetType;
59 import org.eclipse.jgit.dircache.DirCache;
60 import org.eclipse.jgit.dircache.DirCacheCheckout;
61 import org.eclipse.jgit.dircache.DirCacheEditor;
62 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
63 import org.eclipse.jgit.dircache.DirCacheEntry;
64 import org.eclipse.jgit.dircache.DirCacheIterator;
65 import org.eclipse.jgit.errors.CorruptObjectException;
66 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
67 import org.eclipse.jgit.errors.MissingObjectException;
68 import org.eclipse.jgit.junit.JGitTestUtil;
69 import org.eclipse.jgit.junit.RepositoryTestCase;
70 import org.eclipse.jgit.lib.ConfigConstants;
71 import org.eclipse.jgit.lib.Constants;
72 import org.eclipse.jgit.lib.FileMode;
73 import org.eclipse.jgit.lib.ObjectId;
74 import org.eclipse.jgit.lib.ObjectInserter;
75 import org.eclipse.jgit.lib.ObjectReader;
76 import org.eclipse.jgit.lib.Repository;
77 import org.eclipse.jgit.revwalk.RevCommit;
78 import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
79 import org.eclipse.jgit.treewalk.WorkingTreeIterator.MetadataDiff;
80 import org.eclipse.jgit.treewalk.filter.PathFilter;
81 import org.eclipse.jgit.util.FS;
82 import org.eclipse.jgit.util.FileUtils;
83 import org.eclipse.jgit.util.RawParseUtils;
84 import org.junit.Before;
85 import org.junit.Test;
86
87 public class FileTreeIteratorTest extends RepositoryTestCase {
88 private final String[] paths = { "a,", "a,b", "a/b", "a0b" };
89
90 private Instant[] mtime;
91
92 @Override
93 @Before
94 public void setUp() throws Exception {
95 super.setUp();
96
97
98
99
100
101
102
103 mtime = new Instant[paths.length];
104 for (int i = paths.length - 1; i >= 0; i--) {
105 final String s = paths[i];
106 writeTrashFile(s, s);
107 mtime[i] = db.getFS().lastModifiedInstant(new File(trash, s));
108 }
109 }
110
111 @Test
112 public void testGetEntryContentLength() throws Exception {
113 final FileTreeIterator fti = new FileTreeIterator(db);
114 fti.next(1);
115 assertEquals(3, fti.getEntryContentLength());
116 fti.back(1);
117 assertEquals(2, fti.getEntryContentLength());
118 fti.next(1);
119 assertEquals(3, fti.getEntryContentLength());
120 fti.reset();
121 assertEquals(2, fti.getEntryContentLength());
122 }
123
124 @Test
125 public void testEmptyIfRootIsFile() throws Exception {
126 final File r = new File(trash, paths[0]);
127 assertTrue(r.isFile());
128 final FileTreeIterator fti = new FileTreeIterator(r, db.getFS(),
129 db.getConfig().get(WorkingTreeOptions.KEY));
130 assertTrue(fti.first());
131 assertTrue(fti.eof());
132 }
133
134 @Test
135 public void testEmptyIfRootDoesNotExist() throws Exception {
136 final File r = new File(trash, "not-existing-file");
137 assertFalse(r.exists());
138 final FileTreeIterator fti = new FileTreeIterator(r, db.getFS(),
139 db.getConfig().get(WorkingTreeOptions.KEY));
140 assertTrue(fti.first());
141 assertTrue(fti.eof());
142 }
143
144 @Test
145 public void testEmptyIfRootIsEmpty() throws Exception {
146 final File r = new File(trash, "not-existing-file");
147 assertFalse(r.exists());
148 FileUtils.mkdir(r);
149
150 final FileTreeIterator fti = new FileTreeIterator(r, db.getFS(),
151 db.getConfig().get(WorkingTreeOptions.KEY));
152 assertTrue(fti.first());
153 assertTrue(fti.eof());
154 }
155
156 @Test
157 public void testEmptyIteratorOnEmptyDirectory() throws Exception {
158 String nonExistingFileName = "not-existing-file";
159 final File r = new File(trash, nonExistingFileName);
160 assertFalse(r.exists());
161 FileUtils.mkdir(r);
162
163 final FileTreeIterator parent = new FileTreeIterator(db);
164
165 while (!parent.getEntryPathString().equals(nonExistingFileName))
166 parent.next(1);
167
168 final FileTreeIterator childIter = new FileTreeIterator(parent, r,
169 db.getFS());
170 assertTrue(childIter.first());
171 assertTrue(childIter.eof());
172
173 String parentPath = parent.getEntryPathString();
174 assertEquals(nonExistingFileName, parentPath);
175
176
177
178 String childPath = childIter.getEntryPathString();
179
180
181 EmptyTreeIterator e = childIter.createEmptyTreeIterator();
182 assertNotNull(e);
183
184
185
186
187 assertEquals(parentPath, parent.getEntryPathString());
188 assertEquals(parentPath + "/", childPath);
189 assertEquals(parentPath + "/", childIter.getEntryPathString());
190 assertEquals(childPath + "/", e.getEntryPathString());
191 }
192
193 @Test
194 public void testSimpleIterate() throws Exception {
195 final FileTreeIterator top = new FileTreeIterator(trash, db.getFS(),
196 db.getConfig().get(WorkingTreeOptions.KEY));
197
198 assertTrue(top.first());
199 assertFalse(top.eof());
200 assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode);
201 assertEquals(paths[0], nameOf(top));
202 assertEquals(paths[0].length(), top.getEntryLength());
203 assertEquals(mtime[0], top.getEntryLastModifiedInstant());
204
205 top.next(1);
206 assertFalse(top.first());
207 assertFalse(top.eof());
208 assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode);
209 assertEquals(paths[1], nameOf(top));
210 assertEquals(paths[1].length(), top.getEntryLength());
211 assertEquals(mtime[1], top.getEntryLastModifiedInstant());
212
213 top.next(1);
214 assertFalse(top.first());
215 assertFalse(top.eof());
216 assertEquals(FileMode.TREE.getBits(), top.mode);
217
218 final ObjectReader reader = db.newObjectReader();
219 final AbstractTreeIterator sub = top.createSubtreeIterator(reader);
220 assertTrue(sub instanceof FileTreeIterator);
221 final FileTreeIterator subfti = (FileTreeIterator) sub;
222 assertTrue(sub.first());
223 assertFalse(sub.eof());
224 assertEquals(paths[2], nameOf(sub));
225 assertEquals(paths[2].length(), subfti.getEntryLength());
226 assertEquals(mtime[2], subfti.getEntryLastModifiedInstant());
227
228 sub.next(1);
229 assertTrue(sub.eof());
230
231 top.next(1);
232 assertFalse(top.first());
233 assertFalse(top.eof());
234 assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode);
235 assertEquals(paths[3], nameOf(top));
236 assertEquals(paths[3].length(), top.getEntryLength());
237 assertEquals(mtime[3], top.getEntryLastModifiedInstant());
238
239 top.next(1);
240 assertTrue(top.eof());
241 }
242
243 @Test
244 public void testComputeFileObjectId() throws Exception {
245 final FileTreeIterator top = new FileTreeIterator(trash, db.getFS(),
246 db.getConfig().get(WorkingTreeOptions.KEY));
247
248 final MessageDigest md = Constants.newMessageDigest();
249 md.update(Constants.encodeASCII(Constants.TYPE_BLOB));
250 md.update((byte) ' ');
251 md.update(Constants.encodeASCII(paths[0].length()));
252 md.update((byte) 0);
253 md.update(Constants.encode(paths[0]));
254 final ObjectId expect = ObjectId.fromRaw(md.digest());
255
256 assertEquals(expect, top.getEntryObjectId());
257
258
259
260 FileUtils.delete(new File(trash, paths[0]));
261 assertEquals(expect, top.getEntryObjectId());
262 }
263
264 @Test
265 public void testDirCacheMatchingId() throws Exception {
266 File f = writeTrashFile("file", "content");
267 try (Git git = new Git(db)) {
268 writeTrashFile("file", "content");
269 fsTick(f);
270 git.add().addFilepattern("file").call();
271 }
272 DirCacheEntry dce = db.readDirCache().getEntry("file");
273 TreeWalk tw = new TreeWalk(db);
274 FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db
275 .getConfig().get(WorkingTreeOptions.KEY));
276 tw.addTree(fti);
277 DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
278 tw.addTree(dci);
279 fti.setDirCacheIterator(tw, 1);
280 while (tw.next() && !tw.getPathString().equals("file")) {
281
282 }
283 assertEquals(MetadataDiff.EQUAL, fti.compareMetadata(dce));
284 ObjectId fromRaw = ObjectId.fromRaw(fti.idBuffer(), fti.idOffset());
285 assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
286 fromRaw.getName());
287 try (ObjectReader objectReader = db.newObjectReader()) {
288 assertFalse(fti.isModified(dce, false, objectReader));
289 }
290 }
291
292 @Test
293 public void testTreewalkEnterSubtree() throws Exception {
294 try (Git git = new Git(db); TreeWalk tw = new TreeWalk(db)) {
295 writeTrashFile("b/c", "b/c");
296 writeTrashFile("z/.git", "gitdir: /tmp/somewhere");
297 git.add().addFilepattern(".").call();
298 git.rm().addFilepattern("a,").addFilepattern("a,b")
299 .addFilepattern("a0b").call();
300 assertEquals("[a/b, mode:100644][b/c, mode:100644][z, mode:160000]",
301 indexState(0));
302 FileUtils.delete(new File(db.getWorkTree(), "b"),
303 FileUtils.RECURSIVE);
304
305 tw.addTree(new DirCacheIterator(db.readDirCache()));
306 tw.addTree(new FileTreeIterator(db));
307 assertTrue(tw.next());
308 assertEquals("a", tw.getPathString());
309 tw.enterSubtree();
310 tw.next();
311 assertEquals("a/b", tw.getPathString());
312 tw.next();
313 assertEquals("b", tw.getPathString());
314 tw.enterSubtree();
315 tw.next();
316 assertEquals("b/c", tw.getPathString());
317 assertNotNull(tw.getTree(0, AbstractTreeIterator.class));
318 assertNotNull(tw.getTree(EmptyTreeIterator.class));
319 }
320 }
321
322 @Test
323 public void testIsModifiedSymlinkAsFile() throws Exception {
324 writeTrashFile("symlink", "content");
325 try (Git git = new Git(db)) {
326 db.getConfig().setString(ConfigConstants.CONFIG_CORE_SECTION, null,
327 ConfigConstants.CONFIG_KEY_SYMLINKS, "false");
328 git.add().addFilepattern("symlink").call();
329 git.commit().setMessage("commit").call();
330 }
331
332
333 DirCacheEntry dce = db.readDirCache().getEntry("symlink");
334 dce.setFileMode(FileMode.SYMLINK);
335 try (ObjectReader objectReader = db.newObjectReader()) {
336 DirCacheCheckout.checkoutEntry(db, dce, objectReader, false, null);
337
338 FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(),
339 db.getConfig().get(WorkingTreeOptions.KEY));
340 while (!fti.getEntryPathString().equals("symlink"))
341 fti.next(1);
342 assertFalse(fti.isModified(dce, false, objectReader));
343 }
344 }
345
346 @Test
347 public void testIsModifiedFileSmudged() throws Exception {
348 File f = writeTrashFile("file", "content");
349 FS fs = db.getFS();
350 try (Git git = new Git(db)) {
351
352
353 fsTick(f);
354 writeTrashFile("file", "content");
355 Instant lastModified = fs.lastModifiedInstant(f);
356 git.add().addFilepattern("file").call();
357 writeTrashFile("file", "conten2");
358 fs.setLastModified(f.toPath(), lastModified);
359
360
361
362
363 fs.setLastModified(db.getIndexFile().toPath(), lastModified);
364 }
365 DirCacheEntry dce = db.readDirCache().getEntry("file");
366 FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db
367 .getConfig().get(WorkingTreeOptions.KEY));
368 while (!fti.getEntryPathString().equals("file"))
369 fti.next(1);
370
371
372 assertEquals(MetadataDiff.SMUDGED, fti.compareMetadata(dce));
373 try (ObjectReader objectReader = db.newObjectReader()) {
374 assertTrue(fti.isModified(dce, false, objectReader));
375 }
376 }
377
378 @Test
379 public void submoduleHeadMatchesIndex() throws Exception {
380 try (Git git = new Git(db);
381 TreeWalk walk = new TreeWalk(db)) {
382 writeTrashFile("file.txt", "content");
383 git.add().addFilepattern("file.txt").call();
384 final RevCommit id = git.commit().setMessage("create file").call();
385 final String path = "sub";
386 DirCache cache = db.lockDirCache();
387 DirCacheEditor editor = cache.editor();
388 editor.add(new PathEdit(path) {
389
390 @Override
391 public void apply(DirCacheEntry ent) {
392 ent.setFileMode(FileMode.GITLINK);
393 ent.setObjectId(id);
394 }
395 });
396 editor.commit();
397
398 Git.cloneRepository().setURI(db.getDirectory().toURI().toString())
399 .setDirectory(new File(db.getWorkTree(), path)).call()
400 .getRepository().close();
401
402 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
403 FileTreeIterator workTreeIter = new FileTreeIterator(db);
404 walk.addTree(indexIter);
405 walk.addTree(workTreeIter);
406 walk.setFilter(PathFilter.create(path));
407
408 assertTrue(walk.next());
409 assertTrue(indexIter.idEqual(workTreeIter));
410 }
411 }
412
413 @Test
414 public void submoduleWithNoGitDirectory() throws Exception {
415 try (Git git = new Git(db);
416 TreeWalk walk = new TreeWalk(db)) {
417 writeTrashFile("file.txt", "content");
418 git.add().addFilepattern("file.txt").call();
419 final RevCommit id = git.commit().setMessage("create file").call();
420 final String path = "sub";
421 DirCache cache = db.lockDirCache();
422 DirCacheEditor editor = cache.editor();
423 editor.add(new PathEdit(path) {
424
425 @Override
426 public void apply(DirCacheEntry ent) {
427 ent.setFileMode(FileMode.GITLINK);
428 ent.setObjectId(id);
429 }
430 });
431 editor.commit();
432
433 File submoduleRoot = new File(db.getWorkTree(), path);
434 assertTrue(submoduleRoot.mkdir());
435 assertTrue(new File(submoduleRoot, Constants.DOT_GIT).mkdir());
436
437 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
438 FileTreeIterator workTreeIter = new FileTreeIterator(db);
439 walk.addTree(indexIter);
440 walk.addTree(workTreeIter);
441 walk.setFilter(PathFilter.create(path));
442
443 assertTrue(walk.next());
444 assertFalse(indexIter.idEqual(workTreeIter));
445 assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId());
446 }
447 }
448
449 @Test
450 public void submoduleWithNoHead() throws Exception {
451 try (Git git = new Git(db);
452 TreeWalk walk = new TreeWalk(db)) {
453 writeTrashFile("file.txt", "content");
454 git.add().addFilepattern("file.txt").call();
455 final RevCommit id = git.commit().setMessage("create file").call();
456 final String path = "sub";
457 DirCache cache = db.lockDirCache();
458 DirCacheEditor editor = cache.editor();
459 editor.add(new PathEdit(path) {
460
461 @Override
462 public void apply(DirCacheEntry ent) {
463 ent.setFileMode(FileMode.GITLINK);
464 ent.setObjectId(id);
465 }
466 });
467 editor.commit();
468
469 assertNotNull(Git.init().setDirectory(new File(db.getWorkTree(), path))
470 .call().getRepository());
471
472 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
473 FileTreeIterator workTreeIter = new FileTreeIterator(db);
474 walk.addTree(indexIter);
475 walk.addTree(workTreeIter);
476 walk.setFilter(PathFilter.create(path));
477
478 assertTrue(walk.next());
479 assertFalse(indexIter.idEqual(workTreeIter));
480 assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId());
481 }
482 }
483
484 @Test
485 public void submoduleDirectoryIterator() throws Exception {
486 try (Git git = new Git(db);
487 TreeWalk walk = new TreeWalk(db)) {
488 writeTrashFile("file.txt", "content");
489 git.add().addFilepattern("file.txt").call();
490 final RevCommit id = git.commit().setMessage("create file").call();
491 final String path = "sub";
492 DirCache cache = db.lockDirCache();
493 DirCacheEditor editor = cache.editor();
494 editor.add(new PathEdit(path) {
495
496 @Override
497 public void apply(DirCacheEntry ent) {
498 ent.setFileMode(FileMode.GITLINK);
499 ent.setObjectId(id);
500 }
501 });
502 editor.commit();
503
504 Git.cloneRepository().setURI(db.getDirectory().toURI().toString())
505 .setDirectory(new File(db.getWorkTree(), path)).call()
506 .getRepository().close();
507
508 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
509 FileTreeIterator workTreeIter = new FileTreeIterator(db.getWorkTree(),
510 db.getFS(), db.getConfig().get(WorkingTreeOptions.KEY));
511 walk.addTree(indexIter);
512 walk.addTree(workTreeIter);
513 walk.setFilter(PathFilter.create(path));
514
515 assertTrue(walk.next());
516 assertTrue(indexIter.idEqual(workTreeIter));
517 }
518 }
519
520 @Test
521 public void submoduleNestedWithHeadMatchingIndex() throws Exception {
522 try (Git git = new Git(db);
523 TreeWalk walk = new TreeWalk(db)) {
524 writeTrashFile("file.txt", "content");
525 git.add().addFilepattern("file.txt").call();
526 final RevCommit id = git.commit().setMessage("create file").call();
527 final String path = "sub/dir1/dir2";
528 DirCache cache = db.lockDirCache();
529 DirCacheEditor editor = cache.editor();
530 editor.add(new PathEdit(path) {
531
532 @Override
533 public void apply(DirCacheEntry ent) {
534 ent.setFileMode(FileMode.GITLINK);
535 ent.setObjectId(id);
536 }
537 });
538 editor.commit();
539
540 Git.cloneRepository().setURI(db.getDirectory().toURI().toString())
541 .setDirectory(new File(db.getWorkTree(), path)).call()
542 .getRepository().close();
543
544 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
545 FileTreeIterator workTreeIter = new FileTreeIterator(db);
546 walk.addTree(indexIter);
547 walk.addTree(workTreeIter);
548 walk.setFilter(PathFilter.create(path));
549
550 assertTrue(walk.next());
551 assertTrue(indexIter.idEqual(workTreeIter));
552 }
553 }
554
555 @Test
556 public void idOffset() throws Exception {
557 try (Git git = new Git(db);
558 TreeWalk tw = new TreeWalk(db)) {
559 writeTrashFile("fileAinfsonly", "A");
560 File fileBinindex = writeTrashFile("fileBinindex", "B");
561 fsTick(fileBinindex);
562 git.add().addFilepattern("fileBinindex").call();
563 writeTrashFile("fileCinfsonly", "C");
564 DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache());
565 FileTreeIterator workTreeIter = new FileTreeIterator(db);
566 tw.addTree(indexIter);
567 tw.addTree(workTreeIter);
568 workTreeIter.setDirCacheIterator(tw, 0);
569 assertEntry("d46c305e85b630558ee19cc47e73d2e5c8c64cdc", "a,", tw);
570 assertEntry("58ee403f98538ec02409538b3f80adf610accdec", "a,b", tw);
571 assertEntry("0000000000000000000000000000000000000000", "a", tw);
572 assertEntry("b8d30ff397626f0f1d3538d66067edf865e201d6", "a0b", tw);
573
574
575 assertEntry("8c7e5a667f1b771847fe88c01c3de34413a1b220",
576 "fileAinfsonly", tw);
577 assertEntry("7371f47a6f8bd23a8fa1a8b2a9479cdd76380e54", "fileBinindex",
578 tw);
579 assertEntry("96d80cd6c4e7158dbebd0849f4fb7ce513e5828c",
580 "fileCinfsonly", tw);
581 assertFalse(tw.next());
582 }
583 }
584
585 private final FileTreeIterator.FileModeStrategy NO_GITLINKS_STRATEGY =
586 new FileTreeIterator.FileModeStrategy() {
587 @Override
588 public FileMode getMode(File f, FS.Attributes attributes) {
589 if (attributes.isSymbolicLink()) {
590 return FileMode.SYMLINK;
591 } else if (attributes.isDirectory()) {
592
593
594
595
596
597 return FileMode.TREE;
598 } else if (attributes.isExecutable()) {
599 return FileMode.EXECUTABLE_FILE;
600 } else {
601 return FileMode.REGULAR_FILE;
602 }
603 }
604 };
605
606 private Repository createNestedRepo() throws IOException {
607 File gitdir = createUniqueTestGitDir(false);
608 FileRepositoryBuilder builder = new FileRepositoryBuilder();
609 builder.setGitDir(gitdir);
610 Repository nestedRepo = builder.build();
611 nestedRepo.create();
612
613 JGitTestUtil.writeTrashFile(nestedRepo, "sub", "a.txt", "content");
614
615 File nestedRepoPath = new File(nestedRepo.getWorkTree(), "sub/nested");
616 FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder();
617 nestedBuilder.setWorkTree(nestedRepoPath);
618 nestedBuilder.build().create();
619
620 JGitTestUtil.writeTrashFile(nestedRepo, "sub/nested", "b.txt",
621 "content b");
622
623 return nestedRepo;
624 }
625
626 @Test
627 public void testCustomFileModeStrategy() throws Exception {
628 Repository nestedRepo = createNestedRepo();
629
630 try (Git git = new Git(nestedRepo)) {
631
632 WorkingTreeIterator customIterator = new FileTreeIterator(
633 nestedRepo, NO_GITLINKS_STRATEGY);
634 git.add().setWorkingTreeIterator(customIterator).addFilepattern(".")
635 .call();
636 assertEquals(
637 "[sub/a.txt, mode:100644, content:content]"
638 + "[sub/nested/b.txt, mode:100644, content:content b]",
639 indexState(nestedRepo, CONTENT));
640 }
641 }
642
643 @Test
644 public void testCustomFileModeStrategyFromParentIterator() throws Exception {
645 Repository nestedRepo = createNestedRepo();
646
647 try (Git git = new Git(nestedRepo)) {
648 FileTreeIterator customIterator = new FileTreeIterator(nestedRepo,
649 NO_GITLINKS_STRATEGY);
650 File r = new File(nestedRepo.getWorkTree(), "sub");
651
652
653
654
655
656 FileTreeIterator childIterator = new FileTreeIterator(
657 customIterator, r, nestedRepo.getFS());
658 git.add().setWorkingTreeIterator(childIterator).addFilepattern(".")
659 .call();
660 assertEquals(
661 "[sub/a.txt, mode:100644, content:content]"
662 + "[sub/nested/b.txt, mode:100644, content:content b]",
663 indexState(nestedRepo, CONTENT));
664 }
665 }
666
667 @Test
668 public void testFileModeSymLinkIsNotATree() throws IOException {
669 org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
670 FS fs = db.getFS();
671
672 writeTrashFile("mål/data", "targetdata");
673 fs.createSymLink(new File(trash, "länk"), "mål");
674 FileTreeIterator fti = new FileTreeIterator(db);
675 assertFalse(fti.eof());
676 while (!fti.getEntryPathString().equals("länk")) {
677 fti.next(1);
678 }
679 assertEquals("länk", fti.getEntryPathString());
680 assertEquals(FileMode.SYMLINK, fti.getEntryFileMode());
681 fti.next(1);
682 assertFalse(fti.eof());
683 assertEquals("mål", fti.getEntryPathString());
684 assertEquals(FileMode.TREE, fti.getEntryFileMode());
685 fti.next(1);
686 assertTrue(fti.eof());
687 }
688
689 @Test
690 public void testSymlinkNotModifiedThoughNormalized() throws Exception {
691 DirCache dc = db.lockDirCache();
692 DirCacheEditor dce = dc.editor();
693 final String UNNORMALIZED = "target/";
694 final byte[] UNNORMALIZED_BYTES = Constants.encode(UNNORMALIZED);
695 try (ObjectInserter oi = db.newObjectInserter()) {
696 final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
697 UNNORMALIZED_BYTES, 0, UNNORMALIZED_BYTES.length);
698 dce.add(new DirCacheEditor.PathEdit("link") {
699 @Override
700 public void apply(DirCacheEntry ent) {
701 ent.setFileMode(FileMode.SYMLINK);
702 ent.setObjectId(linkid);
703 ent.setLength(UNNORMALIZED_BYTES.length);
704 }
705 });
706 assertTrue(dce.commit());
707 }
708 try (Git git = new Git(db)) {
709 git.commit().setMessage("Adding link").call();
710 git.reset().setMode(ResetType.HARD).call();
711 DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
712 FileTreeIterator fti = new FileTreeIterator(db);
713
714
715 while (!fti.getEntryPathString().equals("link")) {
716 fti.next(1);
717 }
718 assertEquals("link", fti.getEntryPathString());
719 assertEquals("link", dci.getEntryPathString());
720
721
722 assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
723 db.newObjectReader()));
724 }
725 }
726
727
728
729
730
731
732
733 @Test
734 public void testSymlinkModifiedNotNormalized() throws Exception {
735 DirCache dc = db.lockDirCache();
736 DirCacheEditor dce = dc.editor();
737 final String NORMALIZED = "target";
738 final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
739 try (ObjectInserter oi = db.newObjectInserter()) {
740 final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
741 NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length);
742 dce.add(new DirCacheEditor.PathEdit("link") {
743 @Override
744 public void apply(DirCacheEntry ent) {
745 ent.setFileMode(FileMode.SYMLINK);
746 ent.setObjectId(linkid);
747 ent.setLength(NORMALIZED_BYTES.length);
748 }
749 });
750 assertTrue(dce.commit());
751 }
752 try (Git git = new Git(db)) {
753 git.commit().setMessage("Adding link").call();
754 git.reset().setMode(ResetType.HARD).call();
755 DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
756 FileTreeIterator fti = new FileTreeIterator(db);
757
758
759 while (!fti.getEntryPathString().equals("link")) {
760 fti.next(1);
761 }
762 assertEquals("link", fti.getEntryPathString());
763 assertEquals("link", dci.getEntryPathString());
764
765
766 assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
767 db.newObjectReader()));
768 }
769 }
770
771
772
773
774
775
776
777 @Test
778 public void testSymlinkActuallyModified() throws Exception {
779 org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
780 final String NORMALIZED = "target";
781 final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
782 try (ObjectInserter oi = db.newObjectInserter()) {
783 final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
784 NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length);
785 DirCache dc = db.lockDirCache();
786 DirCacheEditor dce = dc.editor();
787 dce.add(new DirCacheEditor.PathEdit("link") {
788 @Override
789 public void apply(DirCacheEntry ent) {
790 ent.setFileMode(FileMode.SYMLINK);
791 ent.setObjectId(linkid);
792 ent.setLength(NORMALIZED_BYTES.length);
793 }
794 });
795 assertTrue(dce.commit());
796 }
797 try (Git git = new Git(db)) {
798 git.commit().setMessage("Adding link").call();
799 git.reset().setMode(ResetType.HARD).call();
800
801 FileUtils.delete(new File(trash, "link"), FileUtils.NONE);
802 FS.DETECTED.createSymLink(new File(trash, "link"), "newtarget");
803 DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
804 FileTreeIterator fti = new FileTreeIterator(db);
805
806
807 while (!fti.getEntryPathString().equals("link")) {
808 fti.next(1);
809 }
810 assertEquals("link", fti.getEntryPathString());
811 assertEquals("link", dci.getEntryPathString());
812
813
814 assertTrue(fti.isModified(dci.getDirCacheEntry(), true,
815 db.newObjectReader()));
816 }
817 }
818
819 private static void assertEntry(String sha1string, String path, TreeWalk tw)
820 throws MissingObjectException, IncorrectObjectTypeException,
821 CorruptObjectException, IOException {
822 assertTrue(tw.next());
823 assertEquals(path, tw.getPathString());
824 assertEquals(sha1string, tw.getObjectId(1).getName() );
825 }
826
827 private static String nameOf(AbstractTreeIterator i) {
828 return RawParseUtils.decode(UTF_8, i.path, 0, i.pathLen);
829 }
830 }