From 5ae21c9369422889a54506d621f3f6b1004182c4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 13 Oct 2008 09:52:26 -0700 Subject: [PATCH] Add bundle creation support BundleWriter offers a safe API to create new bundles. Signed-off-by: Shawn O. Pearce Signed-off-by: Robin Rosenberg --- .../org/spearce/jgit/transport/BundleWriter.java | 196 +++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/BundleWriter.java diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BundleWriter.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BundleWriter.java new file mode 100644 index 00000000..a22a31dd --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BundleWriter.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008, Google Inc. + * + * 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 Git Development Community 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.spearce.jgit.transport; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.spearce.jgit.lib.AnyObjectId; +import org.spearce.jgit.lib.Constants; +import org.spearce.jgit.lib.ObjectId; +import org.spearce.jgit.lib.PackWriter; +import org.spearce.jgit.lib.ProgressMonitor; +import org.spearce.jgit.lib.Ref; +import org.spearce.jgit.lib.Repository; +import org.spearce.jgit.revwalk.RevCommit; + +/** + * Creates a Git bundle file, for sneaker-net transport to another system. + *

+ * Bundles generated by this class can be later read in from a file URI using + * the bundle transport, or from an application controlled buffer by the more + * generic {@link TransportBundleStream}. + *

+ * Applications creating bundles need to call one or more include + * calls to reflect which objects should be available as refs in the bundle for + * the other side to fetch. At least one include is required to create a valid + * bundle file, and duplicate names are not permitted. + *

+ * Optional assume calls can be made to declare commits which the + * recipient must have in order to fetch from the bundle file. Objects reachable + * from these assumed commits can be used as delta bases in order to reduce the + * overall bundle size. + */ +public class BundleWriter { + private final PackWriter packWriter; + + private final Map include; + + private final Set assume; + + /** + * Create a writer for a bundle. + * + * @param repo + * repository where objects are stored. + * @param monitor + * operations progress monitor. + */ + public BundleWriter(final Repository repo, final ProgressMonitor monitor) { + packWriter = new PackWriter(repo, monitor); + include = new TreeMap(); + assume = new HashSet(); + } + + /** + * Include an object (and everything reachable from it) in the bundle. + * + * @param name + * name the recipient can discover this object as from the + * bundle's list of advertised refs . The name must be a valid + * ref format and must not have already been included in this + * bundle writer. + * @param id + * object to pack. Multiple refs may point to the same object. + */ + public void include(final String name, final AnyObjectId id) { + if (!Repository.isValidRefName(name)) + throw new IllegalArgumentException("Invalid ref name: " + name); + if (include.containsKey(name)) + throw new IllegalStateException("Duplicate ref: " + name); + include.put(name, id.toObjectId()); + } + + /** + * Include a single ref (a name/object pair) in the bundle. + *

+ * This is a utility function for: + * include(r.getName(), r.getObjectId()). + * + * @param r + * the ref to include. + */ + public void include(final Ref r) { + include(r.getName(), r.getObjectId()); + } + + /** + * Assume a commit is available on the recipient's side. + *

+ * In order to fetch from a bundle the recipient must have any assumed + * commit. Each assumed commit is explicitly recorded in the bundle header + * to permit the recipient to validate it has these objects. + * + * @param c + * the commit to assume being available. This commit should be + * parsed and not disposed in order to maximize the amount of + * debugging information available in the bundle stream. + */ + public void assume(final RevCommit c) { + if (c != null) + assume.add(c); + } + + /** + * Generate and write the bundle to the output stream. + *

+ * This method can only be called once per BundleWriter instance. + * + * @param os + * the stream the bundle is written to. If the stream is not + * buffered it will be buffered by the writer. Caller is + * responsible for closing the stream. + * @throws IOException + * an error occurred reading a local object's data to include in + * the bundle, or writing compressed object data to the output + * stream. + */ + public void writeBundle(OutputStream os) throws IOException { + if (!(os instanceof BufferedOutputStream)) + os = new BufferedOutputStream(os); + + final HashSet inc = new HashSet(); + final HashSet exc = new HashSet(); + inc.addAll(include.values()); + for (final RevCommit r : assume) + exc.add(r.getId()); + packWriter.preparePack(inc, exc, exc.size() > 0, true); + + final Writer w = new OutputStreamWriter(os, Constants.CHARSET); + w.write(TransportBundle.V2_BUNDLE_SIGNATURE); + w.write('\n'); + + final char[] tmp = new char[Constants.OBJECT_ID_LENGTH * 2]; + for (final RevCommit a : assume) { + w.write('-'); + a.copyTo(tmp, w); + if (a.getRawBuffer() != null) { + w.write(' '); + w.write(a.getShortMessage()); + } + w.write('\n'); + } + for (final Map.Entry e : include.entrySet()) { + e.getValue().copyTo(tmp, w); + w.write(' '); + w.write(e.getKey()); + w.write('\n'); + } + + w.write('\n'); + w.flush(); + packWriter.writePack(os); + } +} -- 2.11.4.GIT