]> source.dussan.org Git - jgit.git/commitdiff
Smudge index entries on first write (too), as well when reading 13/5413/9
authorRobin Rosenberg <robin.rosenberg@dewire.com>
Mon, 21 May 2012 17:48:40 +0000 (10:48 -0700)
committerKevin Sawicki <kevin@github.com>
Mon, 21 May 2012 17:48:40 +0000 (10:48 -0700)
That happens when the index and a new file is created within the same
second and becomes a problem if we then modify the newly created file
within the same second after adding it to the index. Without smudging
JGit will, on later reads, think the file is unchanged.

The accompanying test passed with the smuding on read.

Change-Id: I4dfecf5c93993ef690e7f0dddb3f3e6125daae15

org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java

index 064100839eca1005c74f6ee9d9394fdabb757b04..b335f2d43a2b2b20a3f35c6c438b8ba380267c40 100644 (file)
@@ -64,6 +64,7 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.RepositoryTestCase;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.WorkingTreeIterator.MetadataDiff;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.RawParseUtils;
@@ -215,6 +216,27 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
                assertFalse(fti.isModified(dce, false));
        }
 
+       @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");
+               git.add().addFilepattern("file").call();
+               writeTrashFile("file", "conten2");
+               DirCacheEntry dce = db.readDirCache().getEntry("file");
+               FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db
+                               .getConfig().get(WorkingTreeOptions.KEY));
+               while (!fti.getEntryPathString().equals("file"))
+                       fti.next(1);
+               // If the fsTick trick does not work we could skip the compareMetaData
+               // test and hope that we are usually testing the intended code path.
+               assertEquals(MetadataDiff.SMUDGED, fti.compareMetadata(dce));
+               assertTrue(fti.isModified(dce, false));
+       }
+
        @Test
        public void submoduleHeadMatchesIndex() throws Exception {
                Git git = new Git(db);
index f139b0f7f868a419e4d89fdfd9aa086eb7f636bd..0d5952df2441964bb622f6157b441bff1f994304 100644 (file)
@@ -60,8 +60,8 @@ import java.text.MessageFormat;
 import java.util.Arrays;
 import java.util.Comparator;
 
-import org.eclipse.jgit.errors.LockFailedException;
 import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.LockFailedException;
 import org.eclipse.jgit.errors.UnmergedPathException;
 import org.eclipse.jgit.events.IndexChangedEvent;
 import org.eclipse.jgit.events.IndexChangedListener;
@@ -404,6 +404,10 @@ public class DirCache {
                if (entryCnt < 0)
                        throw new CorruptObjectException(JGitText.get().DIRCHasTooManyEntries);
 
+               snapshot = FileSnapshot.save(liveFile);
+               int smudge_s = (int) (snapshot.lastModified() / 1000);
+               int smudge_ns = ((int) (snapshot.lastModified() % 1000)) * 1000000;
+
                // Load the individual file entries.
                //
                final int infoLength = DirCacheEntry.getMaximumInfoLength(extended);
@@ -412,8 +416,7 @@ public class DirCache {
 
                final MutableInteger infoAt = new MutableInteger();
                for (int i = 0; i < entryCnt; i++)
-                       sortedEntries[i] = new DirCacheEntry(infos, infoAt, in, md);
-               snapshot = FileSnapshot.save(liveFile);
+                       sortedEntries[i] = new DirCacheEntry(infos, infoAt, in, md, smudge_s, smudge_ns);
 
                // After the file entries are index extensions, and then a footer.
                //
@@ -570,21 +573,29 @@ public class DirCache {
                dos.write(tmp, 0, 12);
 
                // Write the individual file entries.
-               //
-               if (snapshot == null) {
-                       // Write a new index, as no entries require smudging.
-                       //
-                       for (int i = 0; i < entryCnt; i++)
-                               sortedEntries[i].write(dos);
+
+               final int smudge_s;
+               final int smudge_ns;
+               if (myLock != null) {
+                       // For new files we need to smudge the index entry
+                       // if they have been modified "now". Ideally we'd
+                       // want the timestamp when we're done writing the index,
+                       // so we use the current timestamp as a approximation.
+                       myLock.createCommitSnapshot();
+                       snapshot = myLock.getCommitSnapshot();
+                       smudge_s = (int) (snapshot.lastModified() / 1000);
+                       smudge_ns = ((int) (snapshot.lastModified() % 1000)) * 1000000;
                } else {
-                       final int smudge_s = (int) (snapshot.lastModified() / 1000);
-                       final int smudge_ns = ((int) (snapshot.lastModified() % 1000)) * 1000000;
-                       for (int i = 0; i < entryCnt; i++) {
-                               final DirCacheEntry e = sortedEntries[i];
-                               if (e.mightBeRacilyClean(smudge_s, smudge_ns))
-                                       e.smudgeRacilyClean();
-                               e.write(dos);
-                       }
+                       // Used in unit tests only
+                       smudge_ns = 0;
+                       smudge_s = 0;
+               }
+
+               for (int i = 0; i < entryCnt; i++) {
+                       final DirCacheEntry e = sortedEntries[i];
+                       if (e.mightBeRacilyClean(smudge_s, smudge_ns))
+                               e.smudgeRacilyClean();
+                       e.write(dos);
                }
 
                if (tree != null) {
index 9f5aa8cfa1ddda8e366840b3f253d223de13f80a..aec12e316929264669c99674916bc9e08b0008ef 100644 (file)
@@ -141,7 +141,8 @@ public class DirCacheEntry {
        private byte inCoreFlags;
 
        DirCacheEntry(final byte[] sharedInfo, final MutableInteger infoAt,
-                       final InputStream in, final MessageDigest md) throws IOException {
+                       final InputStream in, final MessageDigest md, final int smudge_s,
+                       final int smudge_ns) throws IOException {
                info = sharedInfo;
                infoOffset = infoAt.value;
 
@@ -199,6 +200,10 @@ public class DirCacheEntry {
                        IO.skipFully(in, padLen);
                        md.update(nullpad, 0, padLen);
                }
+
+               if (mightBeRacilyClean(smudge_s, smudge_ns))
+                       smudgeRacilyClean();
+
        }
 
        /**
index f3b533c133663f781c4aa129433838d508f76cca..921812191767aab1591c9e652128afeb719d7242 100644 (file)
@@ -503,6 +503,16 @@ public class LockFile {
                return commitSnapshot;
        }
 
+       /**
+        * Update the commit snapshot {@link #getCommitSnapshot()} before commit.
+        * <p>
+        * This may be necessary if you need time stamp before commit occurs, e.g
+        * while writing the index.
+        */
+       public void createCommitSnapshot() {
+               saveStatInformation();
+       }
+
        /**
         * Unlock this file and abort this change.
         * <p>