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

View File

@ -60,6 +60,7 @@
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
@ -386,64 +387,68 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
MissingObjectException, IncorrectObjectTypeException,
CheckoutConflictException, IndexWriteException {
toBeDeleted.clear();
if (headCommitTree != null)
preScanTwoTrees();
else
prescanOneTree();
if (!conflicts.isEmpty()) {
if (failOnConflict) {
dc.unlock();
throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()]));
} else
cleanUpConflicts();
}
ObjectReader objectReader = repo.getObjectDatabase().newReader();
try {
if (headCommitTree != null)
preScanTwoTrees();
else
prescanOneTree();
// update our index
builder.finish();
if (!conflicts.isEmpty()) {
if (failOnConflict) {
dc.unlock();
throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()]));
} else
cleanUpConflicts();
}
File file=null;
String last = "";
// when deleting files process them in the opposite order as they have
// been reported. This ensures the files are deleted before we delete
// their parent folders
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())
// update our index
builder.finish();
File file = null;
String last = "";
// when deleting files process them in the opposite order as they have
// been reported. This ensures the files are deleted before we delete
// their parent folders
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);
else {
if (!isSamePrefix(r, last))
removeEmptyParents(new File(repo.getWorkTree(), last));
last = r;
else {
if (!isSamePrefix(r, last))
removeEmptyParents(new File(repo.getWorkTree(), last));
last = r;
}
}
}
if (file != null)
removeEmptyParents(file);
if (file != null)
removeEmptyParents(file);
for (String path : updated.keySet()) {
// ... create/overwrite this file ...
file = new File(repo.getWorkTree(), path);
if (!file.getParentFile().mkdirs()) {
// ignore
for (String path : updated.keySet()) {
// ... create/overwrite this file ...
file = new File(repo.getWorkTree(), path);
if (!file.getParentFile().mkdirs()) {
// 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);
// submodules are handled with separate operations
if (FileMode.GITLINK.equals(entry.getRawMode()))
continue;
checkoutEntry(repo, file, entry);
// commit the index builder - a new index is persisted
if (!builder.commit()) {
dc.unlock();
throw new IndexWriteException();
}
} finally {
objectReader.release();
}
// commit the index builder - a new index is persisted
if (!builder.commit()) {
dc.unlock();
throw new IndexWriteException();
}
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
* 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.
* 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
* abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed
* @param repo
* </p>
*
* @param repository
* @param f
* the file to be modified. The parent directory for this file
* has to exist already
@ -861,9 +873,41 @@ private boolean isModified(String path) throws CorruptObjectException, IOExcepti
* the entry containing new mode and content
* @throws IOException
*/
public static void checkoutEntry(final Repository repo, File f,
public static void checkoutEntry(final Repository repository, File f,
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 tmpFile = File.createTempFile("._" + f.getName(), null, parentDir);
FileOutputStream channel = new FileOutputStream(tmpFile);

View File

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