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
This commit is contained in:
Robin Rosenberg 2012-05-21 10:48:40 -07:00 committed by Kevin Sawicki
parent 15147a273f
commit 1953ae6aee
4 changed files with 66 additions and 18 deletions

View File

@ -64,6 +64,7 @@
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 void testIsModifiedSymlink() throws Exception {
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);

View File

@ -60,8 +60,8 @@
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 @@ else if (ver != 2)
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 @@ else if (ver != 2)
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 @@ void writeTo(final OutputStream os) throws IOException {
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) {

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

View File

@ -503,6 +503,16 @@ public FileSnapshot getCommitSnapshot() {
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>