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