Reuse ObjectReader for all objects touched during checkout

Bug: 349361
Change-Id: I61ffcb7694eb8b99ebaf4d0d0acd63e0ee91bcb3
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
This commit is contained in:
Matthias Sohn 2011-08-02 17:54:20 +02:00
parent 6d293c7b06
commit 1067f82f56
3 changed files with 122 additions and 68 deletions

View File

@ -66,6 +66,7 @@
import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AnyObjectId;
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.ObjectReader;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.RefUpdate.Result;
@ -266,9 +267,14 @@ public void apply(DirCacheEntry ent) {
} }
File workTree = repo.getWorkTree(); File workTree = repo.getWorkTree();
for (String file : files) ObjectReader r = repo.getObjectDatabase().newReader();
DirCacheCheckout.checkoutEntry(repo, new File(workTree, file), try {
dc.getEntry(file)); for (String file : files)
DirCacheCheckout.checkoutEntry(repo, new File(workTree,
file), dc.getEntry(file), r);
} finally {
r.release();
}
} finally { } finally {
dc.unlock(); dc.unlock();
revWalk.release(); revWalk.release();

View File

@ -60,6 +60,7 @@
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.ObjectLoader; import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.CanonicalTreeParser;
@ -386,64 +387,68 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
MissingObjectException, IncorrectObjectTypeException, MissingObjectException, IncorrectObjectTypeException,
CheckoutConflictException, IndexWriteException { CheckoutConflictException, IndexWriteException {
toBeDeleted.clear(); toBeDeleted.clear();
if (headCommitTree != null)
preScanTwoTrees();
else
prescanOneTree();
if (!conflicts.isEmpty()) { ObjectReader objectReader = repo.getObjectDatabase().newReader();
if (failOnConflict) { try {
dc.unlock(); if (headCommitTree != null)
throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()])); preScanTwoTrees();
} else else
cleanUpConflicts(); prescanOneTree();
}
// update our index if (!conflicts.isEmpty()) {
builder.finish(); if (failOnConflict) {
dc.unlock();
throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()]));
} else
cleanUpConflicts();
}
File file=null; // update our index
String last = ""; builder.finish();
// when deleting files process them in the opposite order as they have
// been reported. This ensures the files are deleted before we delete File file = null;
// their parent folders String last = "";
for (int i = removed.size() - 1; i >= 0; i--) { // when deleting files process them in the opposite order as they have
String r = removed.get(i); // been reported. This ensures the files are deleted before we delete
file = new File(repo.getWorkTree(), r); // their parent folders
if (!file.delete() && file.exists()) for (int i = removed.size() - 1; i >= 0; i--) {
String r = removed.get(i);
file = new File(repo.getWorkTree(), r);
if (!file.delete() && file.exists())
toBeDeleted.add(r); toBeDeleted.add(r);
else { else {
if (!isSamePrefix(r, last)) if (!isSamePrefix(r, last))
removeEmptyParents(new File(repo.getWorkTree(), last)); removeEmptyParents(new File(repo.getWorkTree(), last));
last = r; last = r;
}
} }
} if (file != null)
if (file != null) removeEmptyParents(file);
removeEmptyParents(file);
for (String path : updated.keySet()) { for (String path : updated.keySet()) {
// ... create/overwrite this file ... // ... create/overwrite this file ...
file = new File(repo.getWorkTree(), path); file = new File(repo.getWorkTree(), path);
if (!file.getParentFile().mkdirs()) { if (!file.getParentFile().mkdirs()) {
// ignore // ignore
}
DirCacheEntry entry = dc.getEntry(path);
// submodules are handled with separate operations
if (FileMode.GITLINK.equals(entry.getRawMode()))
continue;
checkoutEntry(repo, file, entry, objectReader);
} }
DirCacheEntry entry = dc.getEntry(path); // commit the index builder - a new index is persisted
if (!builder.commit()) {
// submodules are handled with separate operations dc.unlock();
if (FileMode.GITLINK.equals(entry.getRawMode())) throw new IndexWriteException();
continue; }
} finally {
checkoutEntry(repo, file, entry); objectReader.release();
} }
// commit the index builder - a new index is persisted
if (!builder.commit()) {
dc.unlock();
throw new IndexWriteException();
}
return toBeDeleted.size() == 0; return toBeDeleted.size() == 0;
} }
@ -848,12 +853,19 @@ private boolean isModified(String path) throws CorruptObjectException, IOExcepti
* Updates the file in the working tree with content and mode from an entry * Updates the file in the working tree with content and mode from an entry
* in the index. The new content is first written to a new temporary file in * in the index. The new content is first written to a new temporary file in
* the same directory as the real file. Then that new file is renamed to the * the same directory as the real file. Then that new file is renamed to the
* final filename. * final filename. Use this method only for checkout of a single entry.
* Otherwise use
* {@code checkoutEntry(Repository, File f, DirCacheEntry, ObjectReader)}
* instead which allows to reuse one {@code ObjectReader} for multiple
* entries.
* *
* <p>
* TODO: this method works directly on File IO, we may need another * TODO: this method works directly on File IO, we may need another
* abstraction (like WorkingTreeIterator). This way we could tell e.g. * abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed * Eclipse that Files in the workspace got changed
* @param repo * </p>
*
* @param repository
* @param f * @param f
* the file to be modified. The parent directory for this file * the file to be modified. The parent directory for this file
* has to exist already * has to exist already
@ -861,9 +873,41 @@ private boolean isModified(String path) throws CorruptObjectException, IOExcepti
* the entry containing new mode and content * the entry containing new mode and content
* @throws IOException * @throws IOException
*/ */
public static void checkoutEntry(final Repository repo, File f, public static void checkoutEntry(final Repository repository, File f,
DirCacheEntry entry) throws IOException { DirCacheEntry entry) throws IOException {
ObjectLoader ol = repo.open(entry.getObjectId()); ObjectReader or = repository.newObjectReader();
try {
checkoutEntry(repository, f, entry, repository.newObjectReader());
} finally {
or.release();
}
}
/**
* Updates the file in the working tree with content and mode from an entry
* in the index. The new content is first written to a new temporary file in
* the same directory as the real file. Then that new file is renamed to the
* final filename.
*
* <p>
* TODO: this method works directly on File IO, we may need another
* abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed
* </p>
*
* @param repo
* @param f
* the file to be modified. The parent directory for this file
* has to exist already
* @param entry
* the entry containing new mode and content
* @param or
* object reader to use for checkout
* @throws IOException
*/
public static void checkoutEntry(final Repository repo, File f,
DirCacheEntry entry, ObjectReader or) throws IOException {
ObjectLoader ol = or.open(entry.getObjectId());
File parentDir = f.getParentFile(); File parentDir = f.getParentFile();
File tmpFile = File.createTempFile("._" + f.getName(), null, parentDir); File tmpFile = File.createTempFile("._" + f.getName(), null, parentDir);
FileOutputStream channel = new FileOutputStream(tmpFile); FileOutputStream channel = new FileOutputStream(tmpFile);

View File

@ -238,19 +238,23 @@ protected boolean mergeImpl() throws IOException {
} }
private void checkout() throws NoWorkTreeException, IOException { private void checkout() throws NoWorkTreeException, IOException {
for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut.entrySet()) { ObjectReader r = db.getObjectDatabase().newReader();
File f = new File(db.getWorkTree(), entry.getKey()); try {
if (entry.getValue() != null) { for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
createDir(f.getParentFile()); .entrySet()) {
DirCacheCheckout.checkoutEntry(db, File f = new File(db.getWorkTree(), entry.getKey());
f, if (entry.getValue() != null) {
entry.getValue()); createDir(f.getParentFile());
} else { DirCacheCheckout.checkoutEntry(db, f, entry.getValue(), r);
if (!f.delete()) } else {
failingPaths.put(entry.getKey(), if (!f.delete())
MergeFailureReason.COULD_NOT_DELETE); failingPaths.put(entry.getKey(),
MergeFailureReason.COULD_NOT_DELETE);
}
modifiedFiles.add(entry.getKey());
} }
modifiedFiles.add(entry.getKey()); } finally {
r.release();
} }
} }