Browse Source

Update smudged entries when writing index

Overload DirCache.lock to take a repository that is
used for updating smudged index entries with information
from the repository's working tree.

New unit tests are also added for updating smudged index
entries on reset, checkout, and commit.

Change-Id: I88689f26000e4e57e77931e5ace7c804d92af1b6
stable-2.0
Kevin Sawicki 13 years ago
parent
commit
dac66672df
  1. 39
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
  2. 107
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
  3. 42
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
  4. 101
      org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
  5. 4
      org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java

39
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java

@ -61,6 +61,8 @@ import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException; import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate;
@ -236,4 +238,41 @@ public class CheckoutCommandTest extends RepositoryTestCase {
assertFalse(head.isSymbolic()); assertFalse(head.isSymbolic());
assertSame(head, head.getTarget()); assertSame(head, head.getTarget());
} }
@Test
public void testUpdateSmudgedEntries() throws Exception {
git.branchCreate().setName("test2").call();
RefUpdate rup = db.updateRef(Constants.HEAD);
rup.link("refs/heads/test2");
File file = new File(db.getWorkTree(), "Test.txt");
long size = file.length();
long mTime = file.lastModified() - 5000L;
assertTrue(file.setLastModified(mTime));
DirCache cache = DirCache.lock(db.getIndexFile(), db.getFS());
DirCacheEntry entry = cache.getEntry("Test.txt");
assertNotNull(entry);
entry.setLength(0);
entry.setLastModified(0);
cache.write();
assertTrue(cache.commit());
cache = DirCache.read(db.getIndexFile(), db.getFS());
entry = cache.getEntry("Test.txt");
assertNotNull(entry);
assertEquals(0, entry.getLength());
assertEquals(0, entry.getLastModified());
db.getIndexFile().setLastModified(
db.getIndexFile().lastModified() - 5000);
assertNotNull(git.checkout().setName("test").call());
cache = DirCache.read(db.getIndexFile(), db.getFS());
entry = cache.getEntry("Test.txt");
assertNotNull(entry);
assertEquals(size, entry.getLength());
assertEquals(mTime, entry.getLastModified());
}
} }

107
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java

@ -50,6 +50,7 @@ import java.io.File;
import java.util.List; import java.util.List;
import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.FileMode;
@ -258,4 +259,110 @@ public class CommitCommandTest extends RepositoryTestCase {
assertEquals(path, subDiff.getNewPath()); assertEquals(path, subDiff.getNewPath());
assertEquals(path, subDiff.getOldPath()); assertEquals(path, subDiff.getOldPath());
} }
@Test
public void commitUpdatesSmudgedEntries() throws Exception {
Git git = new Git(db);
File file1 = writeTrashFile("file1.txt", "content1");
assertTrue(file1.setLastModified(file1.lastModified() - 5000));
File file2 = writeTrashFile("file2.txt", "content2");
assertTrue(file2.setLastModified(file2.lastModified() - 5000));
File file3 = writeTrashFile("file3.txt", "content3");
assertTrue(file3.setLastModified(file3.lastModified() - 5000));
assertNotNull(git.add().addFilepattern("file1.txt")
.addFilepattern("file2.txt").addFilepattern("file3.txt").call());
RevCommit commit = git.commit().setMessage("add files").call();
assertNotNull(commit);
DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
int file1Size = cache.getEntry("file1.txt").getLength();
int file2Size = cache.getEntry("file2.txt").getLength();
int file3Size = cache.getEntry("file3.txt").getLength();
ObjectId file2Id = cache.getEntry("file2.txt").getObjectId();
ObjectId file3Id = cache.getEntry("file3.txt").getObjectId();
assertTrue(file1Size > 0);
assertTrue(file2Size > 0);
assertTrue(file3Size > 0);
// Smudge entries
cache = DirCache.lock(db.getIndexFile(), db.getFS());
cache.getEntry("file1.txt").setLength(0);
cache.getEntry("file2.txt").setLength(0);
cache.getEntry("file3.txt").setLength(0);
cache.write();
assertTrue(cache.commit());
// Verify entries smudged
cache = DirCache.read(db.getIndexFile(), db.getFS());
assertEquals(0, cache.getEntry("file1.txt").getLength());
assertEquals(0, cache.getEntry("file2.txt").getLength());
assertEquals(0, cache.getEntry("file3.txt").getLength());
long indexTime = db.getIndexFile().lastModified();
db.getIndexFile().setLastModified(indexTime - 5000);
write(file1, "content4");
assertTrue(file1.setLastModified(file1.lastModified() + 1000));
assertNotNull(git.commit().setMessage("edit file").setOnly("file1.txt")
.call());
cache = db.readDirCache();
assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
assertEquals(file2Size, cache.getEntry("file2.txt").getLength());
assertEquals(file3Size, cache.getEntry("file3.txt").getLength());
assertEquals(file2Id, cache.getEntry("file2.txt").getObjectId());
assertEquals(file3Id, cache.getEntry("file3.txt").getObjectId());
}
@Test
public void commitIgnoresSmudgedEntryWithDifferentId() throws Exception {
Git git = new Git(db);
File file1 = writeTrashFile("file1.txt", "content1");
assertTrue(file1.setLastModified(file1.lastModified() - 5000));
File file2 = writeTrashFile("file2.txt", "content2");
assertTrue(file2.setLastModified(file2.lastModified() - 5000));
assertNotNull(git.add().addFilepattern("file1.txt")
.addFilepattern("file2.txt").call());
RevCommit commit = git.commit().setMessage("add files").call();
assertNotNull(commit);
DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
int file1Size = cache.getEntry("file1.txt").getLength();
int file2Size = cache.getEntry("file2.txt").getLength();
assertTrue(file1Size > 0);
assertTrue(file2Size > 0);
writeTrashFile("file2.txt", "content3");
assertNotNull(git.add().addFilepattern("file2.txt").call());
writeTrashFile("file2.txt", "content4");
// Smudge entries
cache = DirCache.lock(db.getIndexFile(), db.getFS());
cache.getEntry("file1.txt").setLength(0);
cache.getEntry("file2.txt").setLength(0);
cache.write();
assertTrue(cache.commit());
// Verify entries smudged
cache = db.readDirCache();
assertEquals(0, cache.getEntry("file1.txt").getLength());
assertEquals(0, cache.getEntry("file2.txt").getLength());
long indexTime = db.getIndexFile().lastModified();
db.getIndexFile().setLastModified(indexTime - 5000);
write(file1, "content5");
assertTrue(file1.setLastModified(file1.lastModified() + 1000));
assertNotNull(git.commit().setMessage("edit file").setOnly("file1.txt")
.call());
cache = db.readDirCache();
assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
assertEquals(0, cache.getEntry("file2.txt").getLength());
}
} }

42
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java

@ -219,6 +219,48 @@ public class ResetCommandTest extends RepositoryTestCase {
assertReflog(prevHead, head); assertReflog(prevHead, head);
} }
@Test
public void testMixedResetRetainsSizeAndModifiedTime() throws Exception {
git = new Git(db);
writeTrashFile("a.txt", "a").setLastModified(
System.currentTimeMillis() - 60 * 1000);
assertNotNull(git.add().addFilepattern("a.txt").call());
assertNotNull(git.commit().setMessage("a commit").call());
writeTrashFile("b.txt", "b").setLastModified(
System.currentTimeMillis() - 60 * 1000);
assertNotNull(git.add().addFilepattern("b.txt").call());
RevCommit commit2 = git.commit().setMessage("b commit").call();
assertNotNull(commit2);
DirCache cache = db.readDirCache();
DirCacheEntry aEntry = cache.getEntry("a.txt");
assertNotNull(aEntry);
assertTrue(aEntry.getLength() > 0);
assertTrue(aEntry.getLastModified() > 0);
DirCacheEntry bEntry = cache.getEntry("b.txt");
assertNotNull(bEntry);
assertTrue(bEntry.getLength() > 0);
assertTrue(bEntry.getLastModified() > 0);
git.reset().setMode(ResetType.MIXED).setRef(commit2.getName()).call();
cache = db.readDirCache();
DirCacheEntry mixedAEntry = cache.getEntry("a.txt");
assertNotNull(mixedAEntry);
assertEquals(aEntry.getLastModified(), mixedAEntry.getLastModified());
assertEquals(aEntry.getLastModified(), mixedAEntry.getLastModified());
DirCacheEntry mixedBEntry = cache.getEntry("b.txt");
assertNotNull(mixedBEntry);
assertEquals(bEntry.getLastModified(), mixedBEntry.getLastModified());
assertEquals(bEntry.getLastModified(), mixedBEntry.getLastModified());
}
@Test @Test
public void testPathsReset() throws Exception { public void testPathsReset() throws Exception {
setupRepository(); setupRepository();

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

@ -69,8 +69,11 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileSnapshot; import org.eclipse.jgit.storage.file.FileSnapshot;
import org.eclipse.jgit.storage.file.LockFile; import org.eclipse.jgit.storage.file.LockFile;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger; import org.eclipse.jgit.util.MutableInteger;
@ -138,6 +141,30 @@ public class DirCache {
return new DirCache(null, null); return new DirCache(null, null);
} }
/**
* Create a new in-core index representation and read an index from disk.
* <p>
* The new index will be read before it is returned to the caller. Read
* failures are reported as exceptions and therefore prevent the method from
* returning a partially populated index.
*
* @param repository
* repository containing the index to read
* @return a cache representing the contents of the specified index file (if
* it exists) or an empty cache if the file does not exist.
* @throws IOException
* the index file is present but could not be read.
* @throws CorruptObjectException
* the index file is using a format or extension that this
* library does not support.
*/
public static DirCache read(final Repository repository)
throws CorruptObjectException, IOException {
final DirCache c = read(repository.getIndexFile(), repository.getFS());
c.repository = repository;
return c;
}
/** /**
* Create a new in-core index representation and read an index from disk. * Create a new in-core index representation and read an index from disk.
* <p> * <p>
@ -209,6 +236,37 @@ public class DirCache {
return c; return c;
} }
/**
* Create a new in-core index representation, lock it, and read from disk.
* <p>
* The new index will be locked and then read before it is returned to the
* caller. Read failures are reported as exceptions and therefore prevent
* the method from returning a partially populated index. On read failure,
* the lock is released.
*
* @param repository
* repository containing the index to lock and read
* @param indexChangedListener
* listener to be informed when DirCache is committed
* @return a cache representing the contents of the specified index file (if
* it exists) or an empty cache if the file does not exist.
* @throws IOException
* the index file is present but could not be read, or the lock
* could not be obtained.
* @throws CorruptObjectException
* the index file is using a format or extension that this
* library does not support.
* @since 2.0
*/
public static DirCache lock(final Repository repository,
final IndexChangedListener indexChangedListener)
throws CorruptObjectException, IOException {
DirCache c = lock(repository.getIndexFile(), repository.getFS(),
indexChangedListener);
c.repository = repository;
return c;
}
/** /**
* Create a new in-core index representation, lock it, and read from disk. * Create a new in-core index representation, lock it, and read from disk.
* <p> * <p>
@ -272,6 +330,9 @@ public class DirCache {
/** listener to be informed on commit */ /** listener to be informed on commit */
private IndexChangedListener indexChangedListener; private IndexChangedListener indexChangedListener;
/** Repository containing this index */
private Repository repository;
/** /**
* Create a new in-core index representation. * Create a new in-core index representation.
* <p> * <p>
@ -591,6 +652,13 @@ public class DirCache {
smudge_s = 0; smudge_s = 0;
} }
// Check if tree is non-null here since calling updateSmudgedEntries
// will automatically build it via creating a DirCacheIterator
final boolean writeTree = tree != null;
if (repository != null && entryCnt > 0)
updateSmudgedEntries();
for (int i = 0; i < entryCnt; i++) { for (int i = 0; i < entryCnt; i++) {
final DirCacheEntry e = sortedEntries[i]; final DirCacheEntry e = sortedEntries[i];
if (e.mightBeRacilyClean(smudge_s, smudge_ns)) if (e.mightBeRacilyClean(smudge_s, smudge_ns))
@ -598,7 +666,7 @@ public class DirCache {
e.write(dos); e.write(dos);
} }
if (tree != null) { if (writeTree) {
final TemporaryBuffer bb = new TemporaryBuffer.LocalFile(); final TemporaryBuffer bb = new TemporaryBuffer.LocalFile();
tree.write(tmp, bb); tree.write(tmp, bb);
bb.close(); bb.close();
@ -865,4 +933,35 @@ public class DirCache {
private void registerIndexChangedListener(IndexChangedListener listener) { private void registerIndexChangedListener(IndexChangedListener listener) {
this.indexChangedListener = listener; this.indexChangedListener = listener;
} }
/**
* Update any smudged entries with information from the working tree.
*
* @throws IOException
*/
private void updateSmudgedEntries() throws IOException {
TreeWalk walk = new TreeWalk(repository);
try {
DirCacheIterator iIter = new DirCacheIterator(this);
FileTreeIterator fIter = new FileTreeIterator(repository);
walk.addTree(iIter);
walk.addTree(fIter);
walk.setRecursive(true);
while (walk.next()) {
iIter = walk.getTree(0, DirCacheIterator.class);
if (iIter == null)
continue;
fIter = walk.getTree(1, FileTreeIterator.class);
if (fIter == null)
continue;
DirCacheEntry entry = iIter.getDirCacheEntry();
if (entry.isSmudged() && iIter.idEqual(fIter)) {
entry.setLength(fIter.getEntryLength());
entry.setLastModified(fIter.getEntryLastModified());
}
}
} finally {
walk.release();
}
}
} }

4
org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java

@ -871,7 +871,7 @@ public abstract class Repository {
*/ */
public DirCache readDirCache() throws NoWorkTreeException, public DirCache readDirCache() throws NoWorkTreeException,
CorruptObjectException, IOException { CorruptObjectException, IOException {
return DirCache.read(getIndexFile(), getFS()); return DirCache.read(this);
} }
/** /**
@ -903,7 +903,7 @@ public abstract class Repository {
notifyIndexChanged(); notifyIndexChanged();
} }
}; };
return DirCache.lock(getIndexFile(), getFS(), l); return DirCache.lock(this, l);
} }
static byte[] gitInternalSlash(byte[] bytes) { static byte[] gitInternalSlash(byte[] bytes) {

Loading…
Cancel
Save