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