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 22eeb8e3d..67fae270e 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 @@ -46,6 +46,9 @@ import java.io.File; import java.security.MessageDigest; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.dircache.DirCacheCheckout; +import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; @@ -175,6 +178,24 @@ public void testComputeFileObjectId() throws Exception { assertEquals(expect, top.getEntryObjectId()); } + public void testIsModifiedSymlink() throws Exception { + File f = writeTrashFile("symlink", "content"); + Git git = new Git(db); + 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); + DirCacheCheckout.checkoutEntry(db, f, dce); + + FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db + .getConfig().get(WorkingTreeOptions.KEY)); + while (!fti.getEntryPathString().equals("symlink")) + fti.next(1); + assertFalse(fti.isModified(dce, false)); + } + private static String nameOf(final AbstractTreeIterator i) { return RawParseUtils.decode(Constants.CHARSET, i.path, 0, i.pathLen); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 6365c119c..69d9b22d1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -556,15 +556,19 @@ public boolean isModified(DirCacheEntry entry, boolean forceContentCheck) { // bitwise presentation of modeDiff we'll have a '1' when the two modes // differ at this position. int modeDiff = getEntryRawMode() ^ entry.getRawMode(); - // Ignore the executable file bits if checkFilemode tells me to do so. - // Ignoring is done by setting the bits representing a EXECUTABLE_FILE - // to '0' in modeDiff - if (!state.options.isFileMode()) - modeDiff &= ~FileMode.EXECUTABLE_FILE.getBits(); - if (modeDiff != 0) - // Report a modification if the modes still (after potentially - // ignoring EXECUTABLE_FILE bits) differ - return true; + + // Do not rely on filemode differences in case of symbolic links + if (modeDiff != 0 && !FileMode.SYMLINK.equals(entry.getRawMode())) { + // Ignore the executable file bits if WorkingTreeOptions tell me to + // do so. Ignoring is done by setting the bits representing a + // EXECUTABLE_FILE to '0' in modeDiff + if (!state.options.isFileMode()) + modeDiff &= ~FileMode.EXECUTABLE_FILE.getBits(); + if (modeDiff != 0) + // Report a modification if the modes still (after potentially + // ignoring EXECUTABLE_FILE bits) differ + return true; + } // Git under windows only stores seconds so we round the timestamp // Java gives us if it looks like the timestamp in index is seconds