diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index 0a4376921..d6eac232e 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -18,6 +18,7 @@ Import-Package: org.eclipse.jgit.api;version="[0.9.0,0.10.0)", org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)", org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)", org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)", + org.eclipse.jgit.storage.pack;version="[0.9.0,0.10.0)", org.eclipse.jgit.transport;version="[0.9.0,0.10.0)", org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)", org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)", diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin index 0d4a140e6..075cadef7 100644 --- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin +++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin @@ -30,6 +30,7 @@ org.eclipse.jgit.pgm.debug.RebuildCommitGraph org.eclipse.jgit.pgm.debug.ShowCacheTree org.eclipse.jgit.pgm.debug.ShowCommands org.eclipse.jgit.pgm.debug.ShowDirCache +org.eclipse.jgit.pgm.debug.ShowPackDelta org.eclipse.jgit.pgm.debug.WriteDirCache org.eclipse.jgit.pgm.eclipse.Iplog diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java new file mode 100644 index 000000000..1718ef30f --- /dev/null +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.pgm.debug; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.InflaterInputStream; + +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.pgm.TextBuiltin; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.pack.BinaryDelta; +import org.eclipse.jgit.storage.pack.ObjectReuseAsIs; +import org.eclipse.jgit.storage.pack.ObjectToPack; +import org.eclipse.jgit.storage.pack.PackOutputStream; +import org.eclipse.jgit.storage.pack.PackWriter; +import org.eclipse.jgit.storage.pack.StoredObjectRepresentation; +import org.eclipse.jgit.util.TemporaryBuffer; +import org.kohsuke.args4j.Argument; + +class ShowPackDelta extends TextBuiltin { + @Argument(index = 0) + private ObjectId objectId; + + @Override + protected void run() throws Exception { + ObjectReader reader = db.newObjectReader(); + RevObject obj = new RevWalk(reader).parseAny(objectId); + byte[] delta = getDelta(reader, obj); + + // We're crossing our fingers that this will be a delta. Double + // check the size field in the header, it should match. + // + long size = reader.getObjectSize(obj, obj.getType()); + try { + if (BinaryDelta.getResultSize(delta) != size) + throw die("Object " + obj.name() + " is not a delta"); + } catch (ArrayIndexOutOfBoundsException bad) { + throw die("Object " + obj.name() + " is not a delta"); + } + + out.println(BinaryDelta.format(delta)); + } + + private byte[] getDelta(ObjectReader reader, RevObject obj) + throws IOException, MissingObjectException, + StoredObjectRepresentationNotAvailableException { + ObjectReuseAsIs asis = (ObjectReuseAsIs) reader; + ObjectToPack target = asis.newObjectToPack(obj); + + PackWriter pw = new PackWriter(reader) { + @Override + public void select(ObjectToPack otp, StoredObjectRepresentation next) { + otp.select(next); + } + }; + + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + asis.selectObjectRepresentation(pw, target); + asis.copyObjectAsIs(new PackOutputStream(NullProgressMonitor.INSTANCE, + buf, pw), target); + + // At this point the object header has no delta information, + // because it was output as though it were a whole object. + // Skip over the header and inflate. + // + byte[] bufArray = buf.toByteArray(); + int ptr = 0; + while ((bufArray[ptr] & 0x80) != 0) + ptr++; + ptr++; + + TemporaryBuffer.Heap raw = new TemporaryBuffer.Heap(bufArray.length); + InflaterInputStream inf = new InflaterInputStream( + new ByteArrayInputStream(bufArray, ptr, bufArray.length)); + raw.copy(inf); + inf.close(); + return raw.toByteArray(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java index a93ac0516..92e1a197c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java @@ -71,11 +71,25 @@ public final class PackOutputStream extends OutputStream { private byte[] copyBuffer; - PackOutputStream(final ProgressMonitor writeMonitor, - final OutputStream out, final boolean ofsDelta) { + /** + * Initialize a pack output stream. + *

+ * This constructor is exposed to support debugging the JGit library only. + * Application or storage level code should not create a PackOutputStream, + * instead use {@link PackWriter}, and let the writer create the stream. + * + * @param writeMonitor + * monitor to update on object output progress. + * @param out + * target stream to receive all object contents. + * @param pw + * packer that is going to perform the output. + */ + public PackOutputStream(final ProgressMonitor writeMonitor, + final OutputStream out, final PackWriter pw) { this.writeMonitor = writeMonitor; this.out = out; - this.ofsDelta = ofsDelta; + this.ofsDelta = pw.isDeltaBaseAsOffset(); } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java index cae552831..d96d5ddfd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java @@ -718,7 +718,7 @@ public void writePack(ProgressMonitor compressMonitor, searchForReuse(); final PackOutputStream out = new PackOutputStream(writeMonitor, - packStream, isDeltaBaseAsOffset()); + packStream, this); writeMonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber()); out.writeFileHeader(PACK_VERSION_GENERATED, getObjectsNumber());