Browse Source

Smudge index entries on first write (too), as well when reading

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
stable-2.0
Robin Rosenberg 13 years ago committed by Kevin Sawicki
parent
commit
1953ae6aee
  1. 22
      org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
  2. 45
      org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
  3. 7
      org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
  4. 10
      org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java

22
org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java

@ -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);

45
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java vendored

@ -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) {

7
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java vendored

@ -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();
}
/**

10
org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java

@ -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>

Loading…
Cancel
Save