@@ -61,6 +61,8 @@ import org.eclipse.jgit.api.errors.InvalidRefNameException; | |||
import org.eclipse.jgit.api.errors.JGitInternalException; | |||
import org.eclipse.jgit.api.errors.RefAlreadyExistsException; | |||
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.Ref; | |||
import org.eclipse.jgit.lib.RefUpdate; | |||
@@ -236,4 +238,41 @@ public class CheckoutCommandTest extends RepositoryTestCase { | |||
assertFalse(head.isSymbolic()); | |||
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()); | |||
} | |||
} |
@@ -50,6 +50,7 @@ import java.io.File; | |||
import java.util.List; | |||
import org.eclipse.jgit.diff.DiffEntry; | |||
import org.eclipse.jgit.dircache.DirCache; | |||
import org.eclipse.jgit.lib.ConfigConstants; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.FileMode; | |||
@@ -258,4 +259,110 @@ public class CommitCommandTest extends RepositoryTestCase { | |||
assertEquals(path, subDiff.getNewPath()); | |||
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()); | |||
} | |||
} |
@@ -219,6 +219,48 @@ public class ResetCommandTest extends RepositoryTestCase { | |||
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 | |||
public void testPathsReset() throws Exception { | |||
setupRepository(); |
@@ -69,8 +69,11 @@ import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.ObjectId; | |||
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.LockFile; | |||
import org.eclipse.jgit.treewalk.FileTreeIterator; | |||
import org.eclipse.jgit.treewalk.TreeWalk; | |||
import org.eclipse.jgit.util.FS; | |||
import org.eclipse.jgit.util.IO; | |||
import org.eclipse.jgit.util.MutableInteger; | |||
@@ -138,6 +141,30 @@ public class DirCache { | |||
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. | |||
* <p> | |||
@@ -209,6 +236,37 @@ public class DirCache { | |||
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. | |||
* <p> | |||
@@ -272,6 +330,9 @@ public class DirCache { | |||
/** listener to be informed on commit */ | |||
private IndexChangedListener indexChangedListener; | |||
/** Repository containing this index */ | |||
private Repository repository; | |||
/** | |||
* Create a new in-core index representation. | |||
* <p> | |||
@@ -591,6 +652,13 @@ public class DirCache { | |||
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++) { | |||
final DirCacheEntry e = sortedEntries[i]; | |||
if (e.mightBeRacilyClean(smudge_s, smudge_ns)) | |||
@@ -598,7 +666,7 @@ public class DirCache { | |||
e.write(dos); | |||
} | |||
if (tree != null) { | |||
if (writeTree) { | |||
final TemporaryBuffer bb = new TemporaryBuffer.LocalFile(); | |||
tree.write(tmp, bb); | |||
bb.close(); | |||
@@ -865,4 +933,35 @@ public class DirCache { | |||
private void registerIndexChangedListener(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(); | |||
} | |||
} | |||
} |
@@ -871,7 +871,7 @@ public abstract class Repository { | |||
*/ | |||
public DirCache readDirCache() throws NoWorkTreeException, | |||
CorruptObjectException, IOException { | |||
return DirCache.read(getIndexFile(), getFS()); | |||
return DirCache.read(this); | |||
} | |||
/** | |||
@@ -903,7 +903,7 @@ public abstract class Repository { | |||
notifyIndexChanged(); | |||
} | |||
}; | |||
return DirCache.lock(getIndexFile(), getFS(), l); | |||
return DirCache.lock(this, l); | |||
} | |||
static byte[] gitInternalSlash(byte[] bytes) { |