From 1067f82f562231cca1a7d75c6f0c2d2ce04c7381 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Tue, 2 Aug 2011 17:54:20 +0200 Subject: [PATCH] Reuse ObjectReader for all objects touched during checkout Bug: 349361 Change-Id: I61ffcb7694eb8b99ebaf4d0d0acd63e0ee91bcb3 Signed-off-by: Matthias Sohn Signed-off-by: Christian Halstrick --- .../org/eclipse/jgit/api/CheckoutCommand.java | 12 +- .../jgit/dircache/DirCacheCheckout.java | 144 ++++++++++++------ .../org/eclipse/jgit/merge/ResolveMerger.java | 28 ++-- 3 files changed, 119 insertions(+), 65 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java index f3a2e5360..8caff0629 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java @@ -66,6 +66,7 @@ import org.eclipse.jgit.errors.CheckoutConflictException; 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 class CheckoutCommand extends GitCommand { } 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(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index 79f2297b2..855948329 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -60,6 +60,7 @@ import org.eclipse.jgit.errors.MissingObjectException; 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 @@ public class DirCacheCheckout { 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(); - - 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; + if (!conflicts.isEmpty()) { + if (failOnConflict) { + dc.unlock(); + throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()])); + } else + cleanUpConflicts(); } - } - 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 + // 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; + } } + 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 + } - DirCacheEntry entry = dc.getEntry(path); - - // submodules are handled with separate operations - if (FileMode.GITLINK.equals(entry.getRawMode())) - continue; + DirCacheEntry entry = dc.getEntry(path); - checkoutEntry(repo, file, entry); - } + // submodules are handled with separate operations + if (FileMode.GITLINK.equals(entry.getRawMode())) + continue; + checkoutEntry(repo, file, entry, objectReader); + } - // commit the index builder - a new index is persisted - if (!builder.commit()) { - dc.unlock(); - throw new IndexWriteException(); + // commit the index builder - a new index is persisted + if (!builder.commit()) { + dc.unlock(); + throw new IndexWriteException(); + } + } finally { + objectReader.release(); } - return toBeDeleted.size() == 0; } @@ -844,26 +849,65 @@ public class DirCacheCheckout { return false; } + /** + * 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. 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. + * + *

+ * 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 repository + * @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 + * @throws IOException + */ + public static void checkoutEntry(final Repository repository, File f, + DirCacheEntry entry) throws IOException { + 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. * + *

* 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 * @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) throws IOException { - ObjectLoader ol = repo.open(entry.getObjectId()); + 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); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index d347b59a0..b76356895 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -238,19 +238,23 @@ public class ResolveMerger extends ThreeWayMerger { } private void checkout() throws NoWorkTreeException, IOException { - for (Map.Entry 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 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(); } }