diff options
Diffstat (limited to 'org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java')
-rw-r--r-- | org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java | 840 |
1 files changed, 557 insertions, 283 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java index 767e13d114..7b9e70d4da 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java @@ -1,60 +1,33 @@ /* - * Copyright (C) 2008, Google Inc. - * and other copyright owners as documented in the project's IP log. + * Copyright (C) 2008, 2017, Google Inc. and others * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.treewalk; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; import java.io.File; import java.io.IOException; +import java.nio.file.InvalidPathException; import java.security.MessageDigest; +import java.time.Instant; import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.ResetCommand.ResetType; +import org.eclipse.jgit.dircache.Checkout; import org.eclipse.jgit.dircache.DirCache; -import org.eclipse.jgit.dircache.DirCacheCheckout; +import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.dircache.DirCacheEntry; @@ -62,15 +35,21 @@ import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.treewalk.WorkingTreeIterator.MetadataDiff; import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.RawParseUtils; import org.junit.Before; @@ -79,8 +58,9 @@ import org.junit.Test; public class FileTreeIteratorTest extends RepositoryTestCase { private final String[] paths = { "a,", "a,b", "a/b", "a0b" }; - private long[] mtime; + private Instant[] mtime; + @Override @Before public void setUp() throws Exception { super.setUp(); @@ -91,11 +71,11 @@ public class FileTreeIteratorTest extends RepositoryTestCase { // This should stress the sorting code better than doing it in // the correct order. // - mtime = new long[paths.length]; + mtime = new Instant[paths.length]; for (int i = paths.length - 1; i >= 0; i--) { final String s = paths[i]; writeTrashFile(s, s); - mtime[i] = new File(trash, s).lastModified(); + mtime[i] = db.getFS().lastModifiedInstant(new File(trash, s)); } } @@ -191,7 +171,7 @@ public class FileTreeIteratorTest extends RepositoryTestCase { assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode); assertEquals(paths[0], nameOf(top)); assertEquals(paths[0].length(), top.getEntryLength()); - assertEquals(mtime[0], top.getEntryLastModified()); + assertEquals(mtime[0], top.getEntryLastModifiedInstant()); top.next(1); assertFalse(top.first()); @@ -199,36 +179,36 @@ public class FileTreeIteratorTest extends RepositoryTestCase { assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode); assertEquals(paths[1], nameOf(top)); assertEquals(paths[1].length(), top.getEntryLength()); - assertEquals(mtime[1], top.getEntryLastModified()); + assertEquals(mtime[1], top.getEntryLastModifiedInstant()); top.next(1); assertFalse(top.first()); assertFalse(top.eof()); assertEquals(FileMode.TREE.getBits(), top.mode); - final ObjectReader reader = db.newObjectReader(); - final AbstractTreeIterator sub = top.createSubtreeIterator(reader); - assertTrue(sub instanceof FileTreeIterator); - final FileTreeIterator subfti = (FileTreeIterator) sub; - assertTrue(sub.first()); - assertFalse(sub.eof()); - assertEquals(paths[2], nameOf(sub)); - assertEquals(paths[2].length(), subfti.getEntryLength()); - assertEquals(mtime[2], subfti.getEntryLastModified()); - - sub.next(1); - assertTrue(sub.eof()); - - top.next(1); - assertFalse(top.first()); - assertFalse(top.eof()); - assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode); - assertEquals(paths[3], nameOf(top)); - assertEquals(paths[3].length(), top.getEntryLength()); - assertEquals(mtime[3], top.getEntryLastModified()); - - top.next(1); - assertTrue(top.eof()); + try (ObjectReader reader = db.newObjectReader()) { + final AbstractTreeIterator sub = top.createSubtreeIterator(reader); + assertTrue(sub instanceof FileTreeIterator); + final FileTreeIterator subfti = (FileTreeIterator) sub; + assertTrue(sub.first()); + assertFalse(sub.eof()); + assertEquals(paths[2], nameOf(sub)); + assertEquals(paths[2].length(), subfti.getEntryLength()); + assertEquals(mtime[2], subfti.getEntryLastModifiedInstant()); + + sub.next(1); + assertTrue(sub.eof()); + top.next(1); + assertFalse(top.first()); + assertFalse(top.eof()); + assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode); + assertEquals(paths[3], nameOf(top)); + assertEquals(paths[3].length(), top.getEntryLength()); + assertEquals(mtime[3], top.getEntryLastModifiedInstant()); + + top.next(1); + assertTrue(top.eof()); + } } @Test @@ -255,49 +235,87 @@ public class FileTreeIteratorTest extends RepositoryTestCase { @Test public void testDirCacheMatchingId() throws Exception { File f = writeTrashFile("file", "content"); - Git git = new Git(db); - writeTrashFile("file", "content"); - fsTick(f); - git.add().addFilepattern("file").call(); + try (Git git = new Git(db)) { + writeTrashFile("file", "content"); + fsTick(f); + git.add().addFilepattern("file").call(); + } DirCacheEntry dce = db.readDirCache().getEntry("file"); - TreeWalk tw = new TreeWalk(db); - FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db - .getConfig().get(WorkingTreeOptions.KEY)); - tw.addTree(fti); - DirCacheIterator dci = new DirCacheIterator(db.readDirCache()); - tw.addTree(dci); - fti.setDirCacheIterator(tw, 1); - while (tw.next() && !tw.getPathString().equals("file")) { - // + try (TreeWalk tw = new TreeWalk(db)) { + FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), + db.getConfig().get(WorkingTreeOptions.KEY)); + tw.addTree(fti); + DirCacheIterator dci = new DirCacheIterator(db.readDirCache()); + tw.addTree(dci); + fti.setDirCacheIterator(tw, 1); + while (tw.next() && !tw.getPathString().equals("file")) { + // + } + assertEquals(MetadataDiff.EQUAL, fti.compareMetadata(dce)); + ObjectId fromRaw = ObjectId.fromRaw(fti.idBuffer(), fti.idOffset()); + assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea", + fromRaw.getName()); + try (ObjectReader objectReader = db.newObjectReader()) { + assertFalse(fti.isModified(dce, false, objectReader)); + } } - assertEquals(MetadataDiff.EQUAL, fti.compareMetadata(dce)); - ObjectId fromRaw = ObjectId.fromRaw(fti.idBuffer(), fti.idOffset()); - assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea", - fromRaw.getName()); - try (ObjectReader objectReader = db.newObjectReader()) { - assertFalse(fti.isModified(dce, false, objectReader)); + } + + @Test + public void testTreewalkEnterSubtree() throws Exception { + try (Git git = new Git(db); TreeWalk tw = new TreeWalk(db)) { + writeTrashFile("b/c", "b/c"); + writeTrashFile("z/.git", "gitdir: /tmp/somewhere"); + git.add().addFilepattern(".").call(); + git.rm().addFilepattern("a,").addFilepattern("a,b") + .addFilepattern("a0b").call(); + assertEquals("[a/b, mode:100644][b/c, mode:100644][z, mode:160000]", + indexState(0)); + FileUtils.delete(new File(db.getWorkTree(), "b"), + FileUtils.RECURSIVE); + + tw.addTree(new DirCacheIterator(db.readDirCache())); + tw.addTree(new FileTreeIterator(db)); + assertTrue(tw.next()); + assertEquals("a", tw.getPathString()); + tw.enterSubtree(); + tw.next(); + assertEquals("a/b", tw.getPathString()); + tw.next(); + assertEquals("b", tw.getPathString()); + tw.enterSubtree(); + tw.next(); + assertEquals("b/c", tw.getPathString()); + assertNotNull(tw.getTree(0, AbstractTreeIterator.class)); + assertNotNull(tw.getTree(EmptyTreeIterator.class)); } } @Test public void testIsModifiedSymlinkAsFile() throws Exception { writeTrashFile("symlink", "content"); - Git git = new Git(db); - db.getConfig().setString(ConfigConstants.CONFIG_CORE_SECTION, null, - ConfigConstants.CONFIG_KEY_SYMLINKS, "false"); - git.add().addFilepattern("symlink").call(); - git.commit().setMessage("commit").call(); + try (Git git = new Git(db)) { + db.getConfig().setString(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_SYMLINKS, "false"); + git.add().addFilepattern("symlink").call(); + git.commit().setMessage("commit").call(); + } // Modify previously committed DirCacheEntry and write it back to disk DirCacheEntry dce = db.readDirCache().getEntry("symlink"); dce.setFileMode(FileMode.SYMLINK); try (ObjectReader objectReader = db.newObjectReader()) { - DirCacheCheckout.checkoutEntry(db, dce, objectReader); - + Checkout checkout = new Checkout(db).setRecursiveDeletion(false); + checkout.checkout(dce, + new CheckoutMetadata(EolStreamType.DIRECT, null), + objectReader, null); + WorkingTreeOptions options = db.getConfig() + .get(WorkingTreeOptions.KEY); FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), - db.getConfig().get(WorkingTreeOptions.KEY)); - while (!fti.getEntryPathString().equals("symlink")) + options); + while (!fti.getEntryPathString().equals("symlink")) { fti.next(1); + } assertFalse(fti.isModified(dce, false, objectReader)); } } @@ -305,20 +323,22 @@ public class FileTreeIteratorTest extends RepositoryTestCase { @Test public void testIsModifiedFileSmudged() throws Exception { File f = writeTrashFile("file", "content"); - Git git = new Git(db); - // The idea of this test is to check the smudged handling - // Hopefully fsTick will make sure our entry gets smudged - fsTick(f); - writeTrashFile("file", "content"); - long lastModified = f.lastModified(); - git.add().addFilepattern("file").call(); - writeTrashFile("file", "conten2"); - f.setLastModified(lastModified); - // We cannot trust this to go fast enough on - // a system with less than one-second lastModified - // resolution, so we force the index to have the - // same timestamp as the file we look at. - db.getIndexFile().setLastModified(lastModified); + FS fs = db.getFS(); + try (Git git = new Git(db)) { + // The idea of this test is to check the smudged handling + // Hopefully fsTick will make sure our entry gets smudged + fsTick(f); + writeTrashFile("file", "content"); + Instant lastModified = fs.lastModifiedInstant(f); + git.add().addFilepattern("file").call(); + writeTrashFile("file", "conten2"); + fs.setLastModified(f.toPath(), lastModified); + // We cannot trust this to go fast enough on + // a system with less than one-second lastModified + // resolution, so we force the index to have the + // same timestamp as the file we look at. + fs.setLastModified(db.getIndexFile().toPath(), lastModified); + } DirCacheEntry dce = db.readDirCache().getEntry("file"); FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db .getConfig().get(WorkingTreeOptions.KEY)); @@ -334,198 +354,452 @@ public class FileTreeIteratorTest extends RepositoryTestCase { @Test public void submoduleHeadMatchesIndex() throws Exception { - Git git = new Git(db); - writeTrashFile("file.txt", "content"); - git.add().addFilepattern("file.txt").call(); - final RevCommit id = git.commit().setMessage("create file").call(); - final String path = "sub"; - DirCache cache = db.lockDirCache(); - DirCacheEditor editor = cache.editor(); - editor.add(new PathEdit(path) { - - public void apply(DirCacheEntry ent) { - ent.setFileMode(FileMode.GITLINK); - ent.setObjectId(id); - } - }); - editor.commit(); - - Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) - .setDirectory(new File(db.getWorkTree(), path)).call() - .getRepository().close(); - - TreeWalk walk = new TreeWalk(db); - DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); - FileTreeIterator workTreeIter = new FileTreeIterator(db); - walk.addTree(indexIter); - walk.addTree(workTreeIter); - walk.setFilter(PathFilter.create(path)); - - assertTrue(walk.next()); - assertTrue(indexIter.idEqual(workTreeIter)); + try (Git git = new Git(db); + TreeWalk walk = new TreeWalk(db)) { + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) + .setDirectory(new File(db.getWorkTree(), path)).call() + .getRepository().close(); + + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertTrue(indexIter.idEqual(workTreeIter)); + } } @Test public void submoduleWithNoGitDirectory() throws Exception { - Git git = new Git(db); - writeTrashFile("file.txt", "content"); - git.add().addFilepattern("file.txt").call(); - final RevCommit id = git.commit().setMessage("create file").call(); - final String path = "sub"; - DirCache cache = db.lockDirCache(); - DirCacheEditor editor = cache.editor(); - editor.add(new PathEdit(path) { - - public void apply(DirCacheEntry ent) { - ent.setFileMode(FileMode.GITLINK); - ent.setObjectId(id); - } - }); - editor.commit(); - - File submoduleRoot = new File(db.getWorkTree(), path); - assertTrue(submoduleRoot.mkdir()); - assertTrue(new File(submoduleRoot, Constants.DOT_GIT).mkdir()); - - TreeWalk walk = new TreeWalk(db); - DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); - FileTreeIterator workTreeIter = new FileTreeIterator(db); - walk.addTree(indexIter); - walk.addTree(workTreeIter); - walk.setFilter(PathFilter.create(path)); - - assertTrue(walk.next()); - assertFalse(indexIter.idEqual(workTreeIter)); - assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId()); + try (Git git = new Git(db); + TreeWalk walk = new TreeWalk(db)) { + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + File submoduleRoot = new File(db.getWorkTree(), path); + assertTrue(submoduleRoot.mkdir()); + assertTrue(new File(submoduleRoot, Constants.DOT_GIT).mkdir()); + + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertFalse(indexIter.idEqual(workTreeIter)); + assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId()); + } } @Test public void submoduleWithNoHead() throws Exception { - Git git = new Git(db); - writeTrashFile("file.txt", "content"); - git.add().addFilepattern("file.txt").call(); - final RevCommit id = git.commit().setMessage("create file").call(); - final String path = "sub"; - DirCache cache = db.lockDirCache(); - DirCacheEditor editor = cache.editor(); - editor.add(new PathEdit(path) { - - public void apply(DirCacheEntry ent) { - ent.setFileMode(FileMode.GITLINK); - ent.setObjectId(id); - } - }); - editor.commit(); - - assertNotNull(Git.init().setDirectory(new File(db.getWorkTree(), path)) - .call().getRepository()); - - TreeWalk walk = new TreeWalk(db); - DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); - FileTreeIterator workTreeIter = new FileTreeIterator(db); - walk.addTree(indexIter); - walk.addTree(workTreeIter); - walk.setFilter(PathFilter.create(path)); - - assertTrue(walk.next()); - assertFalse(indexIter.idEqual(workTreeIter)); - assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId()); + try (Git git = new Git(db); + TreeWalk walk = new TreeWalk(db)) { + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + assertNotNull(Git.init().setDirectory(new File(db.getWorkTree(), path)) + .call().getRepository()); + + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertFalse(indexIter.idEqual(workTreeIter)); + assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId()); + } } @Test public void submoduleDirectoryIterator() throws Exception { - Git git = new Git(db); - writeTrashFile("file.txt", "content"); - git.add().addFilepattern("file.txt").call(); - final RevCommit id = git.commit().setMessage("create file").call(); - final String path = "sub"; - DirCache cache = db.lockDirCache(); - DirCacheEditor editor = cache.editor(); - editor.add(new PathEdit(path) { - - public void apply(DirCacheEntry ent) { - ent.setFileMode(FileMode.GITLINK); - ent.setObjectId(id); - } - }); - editor.commit(); - - Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) - .setDirectory(new File(db.getWorkTree(), path)).call() - .getRepository().close(); - - TreeWalk walk = new TreeWalk(db); - DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); - FileTreeIterator workTreeIter = new FileTreeIterator(db.getWorkTree(), - db.getFS(), db.getConfig().get(WorkingTreeOptions.KEY)); - walk.addTree(indexIter); - walk.addTree(workTreeIter); - walk.setFilter(PathFilter.create(path)); - - assertTrue(walk.next()); - assertTrue(indexIter.idEqual(workTreeIter)); + try (Git git = new Git(db); + TreeWalk walk = new TreeWalk(db)) { + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) + .setDirectory(new File(db.getWorkTree(), path)).call() + .getRepository().close(); + + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db.getWorkTree(), + db.getFS(), db.getConfig().get(WorkingTreeOptions.KEY)); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertTrue(indexIter.idEqual(workTreeIter)); + } } @Test public void submoduleNestedWithHeadMatchingIndex() throws Exception { - Git git = new Git(db); - writeTrashFile("file.txt", "content"); - git.add().addFilepattern("file.txt").call(); - final RevCommit id = git.commit().setMessage("create file").call(); - final String path = "sub/dir1/dir2"; - DirCache cache = db.lockDirCache(); - DirCacheEditor editor = cache.editor(); - editor.add(new PathEdit(path) { - - public void apply(DirCacheEntry ent) { - ent.setFileMode(FileMode.GITLINK); - ent.setObjectId(id); - } - }); - editor.commit(); - - Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) - .setDirectory(new File(db.getWorkTree(), path)).call() - .getRepository().close(); - - TreeWalk walk = new TreeWalk(db); - DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); - FileTreeIterator workTreeIter = new FileTreeIterator(db); - walk.addTree(indexIter); - walk.addTree(workTreeIter); - walk.setFilter(PathFilter.create(path)); - - assertTrue(walk.next()); - assertTrue(indexIter.idEqual(workTreeIter)); + try (Git git = new Git(db); + TreeWalk walk = new TreeWalk(db)) { + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub/dir1/dir2"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) + .setDirectory(new File(db.getWorkTree(), path)).call() + .getRepository().close(); + + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertTrue(indexIter.idEqual(workTreeIter)); + } } @Test public void idOffset() throws Exception { - Git git = new Git(db); - writeTrashFile("fileAinfsonly", "A"); - File fileBinindex = writeTrashFile("fileBinindex", "B"); - fsTick(fileBinindex); - git.add().addFilepattern("fileBinindex").call(); - writeTrashFile("fileCinfsonly", "C"); - TreeWalk tw = new TreeWalk(db); - DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); - FileTreeIterator workTreeIter = new FileTreeIterator(db); - tw.addTree(indexIter); - tw.addTree(workTreeIter); - workTreeIter.setDirCacheIterator(tw, 0); - assertEntry("d46c305e85b630558ee19cc47e73d2e5c8c64cdc", "a,", tw); - assertEntry("58ee403f98538ec02409538b3f80adf610accdec", "a,b", tw); - assertEntry("0000000000000000000000000000000000000000", "a", tw); - assertEntry("b8d30ff397626f0f1d3538d66067edf865e201d6", "a0b", tw); - // The reason for adding this test. Check that the id is correct for - // mixed - assertEntry("8c7e5a667f1b771847fe88c01c3de34413a1b220", - "fileAinfsonly", tw); - assertEntry("7371f47a6f8bd23a8fa1a8b2a9479cdd76380e54", "fileBinindex", - tw); - assertEntry("96d80cd6c4e7158dbebd0849f4fb7ce513e5828c", - "fileCinfsonly", tw); - assertFalse(tw.next()); + try (Git git = new Git(db); + TreeWalk tw = new TreeWalk(db)) { + writeTrashFile("fileAinfsonly", "A"); + File fileBinindex = writeTrashFile("fileBinindex", "B"); + fsTick(fileBinindex); + git.add().addFilepattern("fileBinindex").call(); + writeTrashFile("fileCinfsonly", "C"); + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + tw.addTree(indexIter); + tw.addTree(workTreeIter); + workTreeIter.setDirCacheIterator(tw, 0); + assertEntry("d46c305e85b630558ee19cc47e73d2e5c8c64cdc", "a,", tw); + assertEntry("58ee403f98538ec02409538b3f80adf610accdec", "a,b", tw); + assertEntry("0000000000000000000000000000000000000000", "a", tw); + assertEntry("b8d30ff397626f0f1d3538d66067edf865e201d6", "a0b", tw); + // The reason for adding this test. Check that the id is correct for + // mixed + assertEntry("8c7e5a667f1b771847fe88c01c3de34413a1b220", + "fileAinfsonly", tw); + assertEntry("7371f47a6f8bd23a8fa1a8b2a9479cdd76380e54", "fileBinindex", + tw); + assertEntry("96d80cd6c4e7158dbebd0849f4fb7ce513e5828c", + "fileCinfsonly", tw); + assertFalse(tw.next()); + } + } + + private final FileTreeIterator.FileModeStrategy NO_GITLINKS_STRATEGY = ( + File f, FS.Attributes attributes) -> { + if (attributes.isSymbolicLink()) { + return FileMode.SYMLINK; + } else if (attributes.isDirectory()) { + // NOTE: in the production DefaultFileModeStrategy, there is + // a check here for a subdirectory called '.git', and if it + // exists, we create a GITLINK instead of recursing into the + // tree. In this custom strategy, we ignore nested git dirs + // and treat all directories the same. + return FileMode.TREE; + } else if (attributes.isExecutable()) { + return FileMode.EXECUTABLE_FILE; + } else { + return FileMode.REGULAR_FILE; + } + }; + + private Repository createNestedRepo() throws IOException { + File gitdir = createUniqueTestGitDir(false); + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + builder.setGitDir(gitdir); + Repository nestedRepo = builder.build(); + nestedRepo.create(); + + JGitTestUtil.writeTrashFile(nestedRepo, "sub", "a.txt", "content"); + + File nestedRepoPath = new File(nestedRepo.getWorkTree(), "sub/nested"); + FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder(); + nestedBuilder.setWorkTree(nestedRepoPath); + nestedBuilder.build().create(); + + JGitTestUtil.writeTrashFile(nestedRepo, "sub/nested", "b.txt", + "content b"); + + return nestedRepo; + } + + @Test + public void testCustomFileModeStrategy() throws Exception { + try (Repository nestedRepo = createNestedRepo(); + Git git = new Git(nestedRepo)) { + // validate that our custom strategy is honored + WorkingTreeIterator customIterator = new FileTreeIterator( + nestedRepo, NO_GITLINKS_STRATEGY); + git.add().setWorkingTreeIterator(customIterator).addFilepattern(".") + .call(); + assertEquals( + "[sub/a.txt, mode:100644, content:content]" + + "[sub/nested/b.txt, mode:100644, content:content b]", + indexState(nestedRepo, CONTENT)); + } + } + + @Test + public void testCustomFileModeStrategyFromParentIterator() throws Exception { + try (Repository nestedRepo = createNestedRepo(); + Git git = new Git(nestedRepo)) { + FileTreeIterator customIterator = new FileTreeIterator(nestedRepo, + NO_GITLINKS_STRATEGY); + File r = new File(nestedRepo.getWorkTree(), "sub"); + + // here we want to validate that if we create a new iterator using + // the constructor that accepts a parent iterator, that the child + // iterator correctly inherits the FileModeStrategy from the parent + // iterator. + FileTreeIterator childIterator = new FileTreeIterator( + customIterator, r, nestedRepo.getFS()); + git.add().setWorkingTreeIterator(childIterator).addFilepattern(".") + .call(); + assertEquals( + "[sub/a.txt, mode:100644, content:content]" + + "[sub/nested/b.txt, mode:100644, content:content b]", + indexState(nestedRepo, CONTENT)); + } + } + + @Test + public void testFileModeSymLinkIsNotATree() throws IOException { + org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); + FS fs = db.getFS(); + // mål = target in swedish, just to get some unicode in here + writeTrashFile("mål/data", "targetdata"); + File file = new File(trash, "länk"); + + try { + file.toPath(); + } catch (InvalidPathException e) { + // When executing a test with LANG environment variable set to non + // UTF-8 encoding, it seems that JRE cannot handle Unicode file + // paths. This happens when this test is executed in Bazel as it + // unsets LANG + // (https://docs.bazel.build/versions/master/test-encyclopedia.html#initial-conditions). + // Skip the test if the runtime cannot handle Unicode characters. + assumeNoException(e); + } + + fs.createSymLink(file, "mål"); + FileTreeIterator fti = new FileTreeIterator(db); + assertFalse(fti.eof()); + while (!fti.getEntryPathString().equals("länk")) { + fti.next(1); + } + assertEquals("länk", fti.getEntryPathString()); + assertEquals(FileMode.SYMLINK, fti.getEntryFileMode()); + fti.next(1); + assertFalse(fti.eof()); + assertEquals("mål", fti.getEntryPathString()); + assertEquals(FileMode.TREE, fti.getEntryFileMode()); + fti.next(1); + assertTrue(fti.eof()); + } + + @Test + public void testSymlinkNotModifiedThoughNormalized() throws Exception { + DirCache dc = db.lockDirCache(); + DirCacheEditor dce = dc.editor(); + final String UNNORMALIZED = "target/"; + final byte[] UNNORMALIZED_BYTES = Constants.encode(UNNORMALIZED); + try (ObjectInserter oi = db.newObjectInserter()) { + final ObjectId linkid = oi.insert(Constants.OBJ_BLOB, + UNNORMALIZED_BYTES, 0, UNNORMALIZED_BYTES.length); + dce.add(new DirCacheEditor.PathEdit("link") { + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.SYMLINK); + ent.setObjectId(linkid); + ent.setLength(UNNORMALIZED_BYTES.length); + } + }); + assertTrue(dce.commit()); + } + try (Git git = new Git(db)) { + git.commit().setMessage("Adding link").call(); + git.reset().setMode(ResetType.HARD).call(); + DirCacheIterator dci = new DirCacheIterator(db.readDirCache()); + FileTreeIterator fti = new FileTreeIterator(db); + + // self-check + while (!fti.getEntryPathString().equals("link")) { + fti.next(1); + } + assertEquals("link", fti.getEntryPathString()); + assertEquals("link", dci.getEntryPathString()); + + // test + assertFalse(fti.isModified(dci.getDirCacheEntry(), true, + db.newObjectReader())); + } + } + + /** + * Like #testSymlinkNotModifiedThoughNormalized but there is no + * normalization being done. + * + * @throws Exception + */ + @Test + public void testSymlinkModifiedNotNormalized() throws Exception { + DirCache dc = db.lockDirCache(); + DirCacheEditor dce = dc.editor(); + final String NORMALIZED = "target"; + final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED); + try (ObjectInserter oi = db.newObjectInserter()) { + final ObjectId linkid = oi.insert(Constants.OBJ_BLOB, + NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length); + dce.add(new DirCacheEditor.PathEdit("link") { + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.SYMLINK); + ent.setObjectId(linkid); + ent.setLength(NORMALIZED_BYTES.length); + } + }); + assertTrue(dce.commit()); + } + try (Git git = new Git(db)) { + git.commit().setMessage("Adding link").call(); + git.reset().setMode(ResetType.HARD).call(); + DirCacheIterator dci = new DirCacheIterator(db.readDirCache()); + FileTreeIterator fti = new FileTreeIterator(db); + + // self-check + while (!fti.getEntryPathString().equals("link")) { + fti.next(1); + } + assertEquals("link", fti.getEntryPathString()); + assertEquals("link", dci.getEntryPathString()); + + // test + assertFalse(fti.isModified(dci.getDirCacheEntry(), true, + db.newObjectReader())); + } + } + + /** + * Like #testSymlinkNotModifiedThoughNormalized but here the link is + * modified. + * + * @throws Exception + */ + @Test + public void testSymlinkActuallyModified() throws Exception { + org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); + final String NORMALIZED = "target"; + final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED); + try (ObjectInserter oi = db.newObjectInserter()) { + final ObjectId linkid = oi.insert(Constants.OBJ_BLOB, + NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length); + DirCache dc = db.lockDirCache(); + DirCacheEditor dce = dc.editor(); + dce.add(new DirCacheEditor.PathEdit("link") { + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.SYMLINK); + ent.setObjectId(linkid); + ent.setLength(NORMALIZED_BYTES.length); + } + }); + assertTrue(dce.commit()); + } + try (Git git = new Git(db)) { + git.commit().setMessage("Adding link").call(); + git.reset().setMode(ResetType.HARD).call(); + + FileUtils.delete(new File(trash, "link"), FileUtils.NONE); + FS.DETECTED.createSymLink(new File(trash, "link"), "newtarget"); + DirCacheIterator dci = new DirCacheIterator(db.readDirCache()); + FileTreeIterator fti = new FileTreeIterator(db); + + // self-check + while (!fti.getEntryPathString().equals("link")) { + fti.next(1); + } + assertEquals("link", fti.getEntryPathString()); + assertEquals("link", dci.getEntryPathString()); + + // test + assertTrue(fti.isModified(dci.getDirCacheEntry(), true, + db.newObjectReader())); + } } private static void assertEntry(String sha1string, String path, TreeWalk tw) @@ -536,7 +810,7 @@ public class FileTreeIteratorTest extends RepositoryTestCase { assertEquals(sha1string, tw.getObjectId(1).getName() /* 1=filetree here */); } - private static String nameOf(final AbstractTreeIterator i) { - return RawParseUtils.decode(Constants.CHARSET, i.path, 0, i.pathLen); + private static String nameOf(AbstractTreeIterator i) { + return RawParseUtils.decode(UTF_8, i.path, 0, i.pathLen); } } |