Since we replaced GitIndex by DirCache JGit didn't fire IndexChangedEvents anymore. For EGit this still worked with a high latency since its RepositoryChangeScanner which is scheduled to run each 10 seconds fires the event in case the index changes. This scanner is meant to detect index changes induced by a different process e.g. by calling "git add" from native git. When the index is changed from within the same process we should fire the event synchronously. Compare the index checksum on write to index checksum when index was read earlier to determine if index really changed. Use IndexChangedListener interface to keep DirCache decoupled from Repository. Change-Id: Id4311f7a7859ffe8738863b3d86c83c8b5f513af Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>tags/v1.2.0.201112221803-r
refdb.clearCache(); | refdb.clearCache(); | ||||
} | } | ||||
@Override | |||||
public void notifyIndexChanged() { | |||||
// we do not support non-bare repositories yet | |||||
} | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "DhtRepostitory[" + key + " / " + name + "]"; | return "DhtRepostitory[" + key + " / " + name + "]"; |
/* | /* | ||||
* Copyright (C) 2008-2009, Google Inc. | * Copyright (C) 2008-2009, Google Inc. | ||||
* Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com> | |||||
* and other copyright owners as documented in the project's IP log. | * and other copyright owners as documented in the project's IP log. | ||||
* | * | ||||
* This program and the accompanying materials are made available | * This program and the accompanying materials are made available | ||||
import static org.junit.Assert.assertNotSame; | import static org.junit.Assert.assertNotSame; | ||||
import static org.junit.Assert.assertSame; | import static org.junit.Assert.assertSame; | ||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assert.fail; | |||||
import java.io.File; | import java.io.File; | ||||
import org.eclipse.jgit.events.IndexChangedEvent; | |||||
import org.eclipse.jgit.events.IndexChangedListener; | |||||
import org.eclipse.jgit.events.ListenerList; | |||||
import org.eclipse.jgit.lib.FileMode; | import org.eclipse.jgit.lib.FileMode; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.RepositoryTestCase; | import org.eclipse.jgit.lib.RepositoryTestCase; | ||||
} | } | ||||
} | } | ||||
@Test | |||||
public void testBuildOneFile_Commit_IndexChangedEvent() | |||||
throws Exception { | |||||
final class ReceivedEventMarkerException extends RuntimeException { | |||||
private static final long serialVersionUID = 1L; | |||||
// empty | |||||
} | |||||
final String path = "a-file-path"; | |||||
final FileMode mode = FileMode.REGULAR_FILE; | |||||
// "old" date in 2008 | |||||
final long lastModified = 1218123387057L; | |||||
final int length = 1342; | |||||
DirCacheEntry entOrig; | |||||
boolean receivedEvent = false; | |||||
DirCache dc = db.lockDirCache(); | |||||
IndexChangedListener listener = new IndexChangedListener() { | |||||
public void onIndexChanged(IndexChangedEvent event) { | |||||
throw new ReceivedEventMarkerException(); | |||||
} | |||||
}; | |||||
ListenerList l = db.getListenerList(); | |||||
l.addIndexChangedListener(listener); | |||||
DirCacheBuilder b = dc.builder(); | |||||
entOrig = new DirCacheEntry(path); | |||||
entOrig.setFileMode(mode); | |||||
entOrig.setLastModified(lastModified); | |||||
entOrig.setLength(length); | |||||
b.add(entOrig); | |||||
try { | |||||
b.commit(); | |||||
} catch (ReceivedEventMarkerException e) { | |||||
receivedEvent = true; | |||||
} | |||||
if (!receivedEvent) | |||||
fail("did not receive IndexChangedEvent"); | |||||
// do the same again, as this doesn't change index compared to first | |||||
// round we should get no event this time | |||||
dc = db.lockDirCache(); | |||||
listener = new IndexChangedListener() { | |||||
public void onIndexChanged(IndexChangedEvent event) { | |||||
throw new ReceivedEventMarkerException(); | |||||
} | |||||
}; | |||||
l = db.getListenerList(); | |||||
l.addIndexChangedListener(listener); | |||||
b = dc.builder(); | |||||
entOrig = new DirCacheEntry(path); | |||||
entOrig.setFileMode(mode); | |||||
entOrig.setLastModified(lastModified); | |||||
entOrig.setLength(length); | |||||
b.add(entOrig); | |||||
try { | |||||
b.commit(); | |||||
} catch (ReceivedEventMarkerException e) { | |||||
fail("unexpected IndexChangedEvent"); | |||||
} | |||||
} | |||||
@Test | @Test | ||||
public void testFindSingleFile() throws Exception { | public void testFindSingleFile() throws Exception { | ||||
final String path = "a-file-path"; | final String path = "a-file-path"; |
/* | /* | ||||
* Copyright (C) 2008-2010, Google Inc. | * Copyright (C) 2008-2010, Google Inc. | ||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> | * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> | ||||
* Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com> | |||||
* and other copyright owners as documented in the project's IP log. | * and other copyright owners as documented in the project's IP log. | ||||
* | * | ||||
* This program and the accompanying materials are made available | * This program and the accompanying materials are made available | ||||
import org.eclipse.jgit.JGitText; | import org.eclipse.jgit.JGitText; | ||||
import org.eclipse.jgit.errors.CorruptObjectException; | import org.eclipse.jgit.errors.CorruptObjectException; | ||||
import org.eclipse.jgit.errors.UnmergedPathException; | import org.eclipse.jgit.errors.UnmergedPathException; | ||||
import org.eclipse.jgit.events.IndexChangedEvent; | |||||
import org.eclipse.jgit.events.IndexChangedListener; | |||||
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; | ||||
private static final DirCacheEntry[] NO_ENTRIES = {}; | private static final DirCacheEntry[] NO_ENTRIES = {}; | ||||
private static final byte[] NO_CHECKSUM = {}; | |||||
static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() { | static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() { | ||||
public int compare(final DirCacheEntry o1, final DirCacheEntry o2) { | public int compare(final DirCacheEntry o1, final DirCacheEntry o2) { | ||||
final int cr = cmp(o1, o2); | final int cr = cmp(o1, o2); | ||||
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 indexLocation | |||||
* location of the index file on disk. | |||||
* @param fs | |||||
* the file system abstraction which will be necessary to perform | |||||
* certain file system operations. | |||||
* @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. | |||||
*/ | |||||
public static DirCache lock(final File indexLocation, final FS fs, | |||||
IndexChangedListener indexChangedListener) | |||||
throws CorruptObjectException, | |||||
IOException { | |||||
DirCache c = lock(indexLocation, fs); | |||||
c.registerIndexChangedListener(indexChangedListener); | |||||
return c; | |||||
} | |||||
/** Location of the current version of the index file. */ | /** Location of the current version of the index file. */ | ||||
private final File liveFile; | private final File liveFile; | ||||
/** Keep track of whether the index has changed or not */ | /** Keep track of whether the index has changed or not */ | ||||
private FileSnapshot snapshot; | private FileSnapshot snapshot; | ||||
/** index checksum when index was read from disk */ | |||||
private byte[] readIndexChecksum; | |||||
/** index checksum when index was written to disk */ | |||||
private byte[] writeIndexChecksum; | |||||
/** listener to be informed on commit */ | |||||
private IndexChangedListener indexChangedListener; | |||||
/** | /** | ||||
* Create a new in-core index representation. | * Create a new in-core index representation. | ||||
* <p> | * <p> | ||||
sortedEntries = NO_ENTRIES; | sortedEntries = NO_ENTRIES; | ||||
entryCnt = 0; | entryCnt = 0; | ||||
tree = null; | tree = null; | ||||
readIndexChecksum = NO_CHECKSUM; | |||||
} | } | ||||
private void readFrom(final InputStream inStream) throws IOException, | private void readFrom(final InputStream inStream) throws IOException, | ||||
} | } | ||||
} | } | ||||
final byte[] exp = md.digest(); | |||||
if (!Arrays.equals(exp, hdr)) { | |||||
readIndexChecksum = md.digest(); | |||||
if (!Arrays.equals(readIndexChecksum, hdr)) { | |||||
throw new CorruptObjectException(JGitText.get().DIRCChecksumMismatch); | throw new CorruptObjectException(JGitText.get().DIRCChecksumMismatch); | ||||
} | } | ||||
} | } | ||||
dos.write(tmp, 0, 8); | dos.write(tmp, 0, 8); | ||||
bb.writeTo(dos, null); | bb.writeTo(dos, null); | ||||
} | } | ||||
os.write(foot.digest()); | |||||
writeIndexChecksum = foot.digest(); | |||||
os.write(writeIndexChecksum); | |||||
os.close(); | os.close(); | ||||
} | } | ||||
if (!tmp.commit()) | if (!tmp.commit()) | ||||
return false; | return false; | ||||
snapshot = tmp.getCommitSnapshot(); | snapshot = tmp.getCommitSnapshot(); | ||||
if (indexChangedListener != null | |||||
&& !Arrays.equals(readIndexChecksum, writeIndexChecksum)) | |||||
indexChangedListener.onIndexChanged(new IndexChangedEvent()); | |||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
private void registerIndexChangedListener(IndexChangedListener listener) { | |||||
this.indexChangedListener = listener; | |||||
} | |||||
} | } |
import org.eclipse.jgit.errors.MissingObjectException; | import org.eclipse.jgit.errors.MissingObjectException; | ||||
import org.eclipse.jgit.errors.NoWorkTreeException; | import org.eclipse.jgit.errors.NoWorkTreeException; | ||||
import org.eclipse.jgit.errors.RevisionSyntaxException; | import org.eclipse.jgit.errors.RevisionSyntaxException; | ||||
import org.eclipse.jgit.events.IndexChangedEvent; | |||||
import org.eclipse.jgit.events.IndexChangedListener; | |||||
import org.eclipse.jgit.events.ListenerList; | import org.eclipse.jgit.events.ListenerList; | ||||
import org.eclipse.jgit.events.RepositoryEvent; | import org.eclipse.jgit.events.RepositoryEvent; | ||||
import org.eclipse.jgit.revwalk.RevBlob; | import org.eclipse.jgit.revwalk.RevBlob; | ||||
*/ | */ | ||||
public DirCache lockDirCache() throws NoWorkTreeException, | public DirCache lockDirCache() throws NoWorkTreeException, | ||||
CorruptObjectException, IOException { | CorruptObjectException, IOException { | ||||
return DirCache.lock(getIndexFile(), getFS()); | |||||
// we want DirCache to inform us so that we can inform registered | |||||
// listeners about index changes | |||||
IndexChangedListener l = new IndexChangedListener() { | |||||
public void onIndexChanged(IndexChangedEvent event) { | |||||
notifyIndexChanged(); | |||||
} | |||||
}; | |||||
return DirCache.lock(getIndexFile(), getFS(), l); | |||||
} | } | ||||
static byte[] gitInternalSlash(byte[] bytes) { | static byte[] gitInternalSlash(byte[] bytes) { | ||||
*/ | */ | ||||
public abstract void scanForRepoChanges() throws IOException; | public abstract void scanForRepoChanges() throws IOException; | ||||
/** | |||||
* Notify that the index changed | |||||
*/ | |||||
public abstract void notifyIndexChanged(); | |||||
/** | /** | ||||
* @param refName | * @param refName | ||||
* | * |
File indexFile = getIndexFile(); | File indexFile = getIndexFile(); | ||||
if (snapshot == null) | if (snapshot == null) | ||||
snapshot = FileSnapshot.save(indexFile); | snapshot = FileSnapshot.save(indexFile); | ||||
else if (snapshot.isModified(indexFile)) { | |||||
snapshot = FileSnapshot.save(indexFile); | |||||
fireEvent(new IndexChangedEvent()); | |||||
} | |||||
else if (snapshot.isModified(indexFile)) | |||||
notifyIndexChanged(); | |||||
} | |||||
@Override | |||||
public void notifyIndexChanged() { | |||||
snapshot = FileSnapshot.save(getIndexFile()); | |||||
fireEvent(new IndexChangedEvent()); | |||||
} | } | ||||
/** | /** |