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