diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java index ca0f06fae..f0159f626 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java @@ -54,7 +54,6 @@ import org.eclipse.jgit.lib.ObjectDatabase; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdSubclassMap; -import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.storage.pack.ObjectToPack; import org.eclipse.jgit.storage.pack.PackWriter; @@ -113,7 +112,7 @@ public void close() { } @Override - public ObjectInserter newInserter() { + public ObjectDirectoryInserter newInserter() { return wrapped.newInserter(); } @@ -213,6 +212,11 @@ long getObjectSize2(WindowCursor curs, String objectName, AnyObjectId objectId) throw new UnsupportedOperationException(); } + @Override + boolean insertUnpackedObject(File tmp, ObjectId objectId, boolean force) { + return wrapped.insertUnpackedObject(tmp, objectId, force); + } + @Override void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, WindowCursor curs) throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java index da38887fc..29c7a2531 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java @@ -62,6 +62,9 @@ public ObjectReader newReader() { return new WindowCursor(this); } + @Override + public abstract ObjectDirectoryInserter newInserter(); + /** * Does the requested object exist in this database? *

@@ -246,6 +249,8 @@ abstract long getObjectSize1(WindowCursor curs, AnyObjectId objectId) abstract long getObjectSize2(WindowCursor curs, String objectName, AnyObjectId objectId) throws IOException; + abstract boolean insertUnpackedObject(File tmp, ObjectId id, boolean force); + abstract FileObjectDatabase newCachedFileObjectDatabase(); static class AlternateHandle { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java index 2b98f107f..8d15fcf79 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java @@ -44,9 +44,12 @@ package org.eclipse.jgit.storage.file; import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.DataFormatException; +import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; import org.eclipse.jgit.errors.IncorrectObjectTypeException; @@ -58,7 +61,6 @@ import org.eclipse.jgit.lib.ObjectStream; import org.eclipse.jgit.storage.pack.BinaryDelta; import org.eclipse.jgit.storage.pack.DeltaStream; -import org.eclipse.jgit.util.TemporaryBuffer; import org.eclipse.jgit.util.io.TeeInputStream; class LargePackedDeltaObject extends ObjectLoader { @@ -165,14 +167,39 @@ public byte[] getCachedBytes() throws LargeObjectException { @Override public ObjectStream openStream() throws MissingObjectException, IOException { + // If the object was recently unpacked, its available loose. + // The loose format is going to be faster to access than a + // delta applied on top of a base. Use that whenever we can. + // + final ObjectId myId = getObjectId(); final WindowCursor wc = new WindowCursor(db); + ObjectLoader ldr = db.openObject2(wc, myId.name(), myId); + if (ldr != null) + return ldr.openStream(); + InputStream in = open(wc); in = new BufferedInputStream(in, 8192); - return new ObjectStream.Filter(getType(), size, in) { + + // While we inflate the object, also deflate it back as a loose + // object. This will later be cleaned up by a gc pass, but until + // then we will reuse the loose form by the above code path. + // + int myType = getType(); + long mySize = getSize(); + final ObjectDirectoryInserter odi = db.newInserter(); + final File tmp = odi.newTempFile(); + DeflaterOutputStream dOut = odi.compress(new FileOutputStream(tmp)); + odi.writeHeader(dOut, myType, mySize); + + in = new TeeInputStream(in, dOut); + return new ObjectStream.Filter(myType, mySize, in) { @Override public void close() throws IOException { - wc.release(); super.close(); + + odi.release(); + wc.release(); + db.insertUnpackedObject(tmp, myId, true /* force creation */); } }; } @@ -195,13 +222,9 @@ private InputStream open(final WindowCursor wc) final ObjectLoader base = pack.load(wc, baseOffset); DeltaStream ds = new DeltaStream(delta) { private long baseSize = SIZE_UNKNOWN; - private TemporaryBuffer.LocalFile buffer; @Override protected InputStream openBase() throws IOException { - if (buffer != null) - return buffer.openInputStream(); - InputStream in; if (base instanceof LargePackedDeltaObject) in = ((LargePackedDeltaObject) base).open(wc); @@ -213,9 +236,7 @@ protected InputStream openBase() throws IOException { else if (in instanceof ObjectStream) baseSize = ((ObjectStream) in).getSize(); } - - buffer = new TemporaryBuffer.LocalFile(db.getDirectory()); - return new TeeInputStream(in, buffer); + return in; } @Override @@ -228,14 +249,11 @@ protected long getBaseSize() throws IOException { } return baseSize; } - - @Override - public void close() throws IOException { - super.close(); - if (buffer != null) - buffer.destroy(); - } }; + if (type == Constants.OBJ_BAD) { + if (!(base instanceof LargePackedDeltaObject)) + type = base.getType(); + } if (size == SIZE_UNKNOWN) size = ds.getSize(); return ds; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java index 29a89bc09..372a97813 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java @@ -69,7 +69,6 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectDatabase; import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.RepositoryCache; import org.eclipse.jgit.lib.RepositoryCache.FileKey; @@ -176,7 +175,7 @@ public void create() throws IOException { } @Override - public ObjectInserter newInserter() { + public ObjectDirectoryInserter newInserter() { return new ObjectDirectoryInserter(this, config); } @@ -455,8 +454,48 @@ ObjectLoader openObject2(final WindowCursor curs, } } - void addUnpackedObject(ObjectId id) { - unpackedObjectCache.add(id); + @Override + boolean insertUnpackedObject(File tmp, ObjectId id, boolean force) { + if (!force && has(id)) { + // Object is already in the repository, remove temporary file. + // + tmp.delete(); + return true; + } + tmp.setReadOnly(); + + final File dst = fileFor(id); + if (force && dst.exists()) { + tmp.delete(); + return true; + } + if (tmp.renameTo(dst)) { + unpackedObjectCache.add(id); + return true; + } + + // Maybe the directory doesn't exist yet as the object + // directories are always lazily created. Note that we + // try the rename first as the directory likely does exist. + // + dst.getParentFile().mkdir(); + if (tmp.renameTo(dst)) { + unpackedObjectCache.add(id); + return true; + } + + if (!force && has(id)) { + tmp.delete(); + return true; + } + + // The object failed to be renamed into its proper + // location and it doesn't exist in the repository + // either. We really don't know what went wrong, so + // fail. + // + tmp.delete(); + return false; } boolean tryAgain1() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java index a1f224c7b..d92285de8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java @@ -83,40 +83,10 @@ public ObjectId insert(final int type, long len, final InputStream is) final MessageDigest md = digest(); final File tmp = toTemp(md, type, len, is); final ObjectId id = ObjectId.fromRaw(md.digest()); - if (db.has(id)) { - // Object is already in the repository, remove temporary file. - // - tmp.delete(); + if (db.insertUnpackedObject(tmp, id, false /* no duplicate */)) return id; - } final File dst = db.fileFor(id); - if (tmp.renameTo(dst)) { - db.addUnpackedObject(id); - return id; - } - - // Maybe the directory doesn't exist yet as the object - // directories are always lazily created. Note that we - // try the rename first as the directory likely does exist. - // - dst.getParentFile().mkdir(); - if (tmp.renameTo(dst)) { - db.addUnpackedObject(id); - return id; - } - - if (db.has(id)) { - tmp.delete(); - return id; - } - - // The object failed to be renamed into its proper - // location and it doesn't exist in the repository - // either. We really don't know what went wrong, so - // fail. - // - tmp.delete(); throw new ObjectWritingException("Unable to create new object: " + dst); } @@ -140,15 +110,12 @@ private File toTemp(final MessageDigest md, final int type, long len, final InputStream is) throws IOException, FileNotFoundException, Error { boolean delete = true; - File tmp = File.createTempFile("noz", null, db.getDirectory()); + File tmp = newTempFile(); try { DigestOutputStream dOut = new DigestOutputStream( compress(new FileOutputStream(tmp)), md); try { - dOut.write(Constants.encodedTypeString(type)); - dOut.write((byte) ' '); - dOut.write(Constants.encodeASCII(len)); - dOut.write((byte) 0); + writeHeader(dOut, type, len); final byte[] buf = buffer(); while (len > 0) { @@ -162,7 +129,6 @@ private File toTemp(final MessageDigest md, final int type, long len, dOut.close(); } - tmp.setReadOnly(); delete = false; return tmp; } finally { @@ -171,7 +137,19 @@ private File toTemp(final MessageDigest md, final int type, long len, } } - private DeflaterOutputStream compress(final OutputStream out) { + void writeHeader(OutputStream out, final int type, long len) + throws IOException { + out.write(Constants.encodedTypeString(type)); + out.write((byte) ' '); + out.write(Constants.encodeASCII(len)); + out.write((byte) 0); + } + + File newTempFile() throws IOException { + return File.createTempFile("noz", null, db.getDirectory()); + } + + DeflaterOutputStream compress(final OutputStream out) { if (deflate == null) deflate = new Deflater(config.get(CoreConfig.KEY).getCompression()); else