]> source.dussan.org Git - jgit.git/commitdiff
Add internal porcelain-style API for ArchiveCommand 56/11556/10
authorJonathan Nieder <jrn@google.com>
Fri, 26 Apr 2013 20:43:23 +0000 (13:43 -0700)
committerJonathan Nieder <jrn@google.com>
Fri, 26 Apr 2013 21:12:42 +0000 (14:12 -0700)
One step closer to exposing archive creation functionality in a
org.eclipse.jgit.archive bundle.

Change-Id: If0ebb2417a941d9d3fc0d3f444316d0d1c494ff3

org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java [new file with mode: 0644]

index 39c12683960a5b0e61542e9929742d88089bc10f..4808df995e8b5e2f3f52d2f8cf9810dcea48f5b2 100644 (file)
@@ -48,6 +48,7 @@ deletedRemoteBranch=Deleted remote branch {0}
 doesNotExist={0} does not exist
 dontOverwriteLocalChanges=error: Your local changes to the following file would be overwritten by merge:
 everythingUpToDate=Everything up-to-date
+exceptionCaughtDuringExecutionOfArchiveCommand=Exception caught during execution of archive command
 expectedNumberOfbytes=Expected {0} bytes.
 exporting=Exporting {0}
 failedToCommitIndex=failed to commit index
index 9075cf068b18f9b90bd12173234c13800141e9ed..5685aa426703a00ccd6abaabcb61ac0ba02baef5 100644 (file)
 
 package org.eclipse.jgit.pgm;
 
-import java.lang.String;
-import java.lang.System;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.EnumMap;
-import java.util.Map;
-import java.text.MessageFormat;
-
-import org.apache.commons.compress.archivers.ArchiveOutputStream;
-import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
-import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
-import org.apache.commons.compress.archivers.tar.TarConstants;
-import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
-import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.MutableObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
-import org.eclipse.jgit.treewalk.AbstractTreeIterator;
-import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.pgm.archive.ArchiveCommand;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 
 @Command(common = true, usage = "usage_archive")
 class Archive extends TextBuiltin {
        @Argument(index = 0, metaVar = "metaVar_treeish")
-       private AbstractTreeIterator tree;
+       private ObjectId tree;
 
        @Option(name = "--format", metaVar = "metaVar_archiveFormat", usage = "usage_archiveFormat")
-       private Format format = Format.ZIP;
+       private ArchiveCommand.Format format = ArchiveCommand.Format.ZIP;
 
        @Override
        protected void run() throws Exception {
-               final MutableObjectId idBuf = new MutableObjectId();
-               final Archiver fmt = formats.get(format);
-
                if (tree == null)
                        throw die(CLIText.get().treeIsRequired);
 
-               final ArchiveOutputStream outa = fmt.createArchiveOutputStream(outs);
-               final TreeWalk walk = new TreeWalk(db);
-               final ObjectReader reader = walk.getObjectReader();
-
+               final ArchiveCommand cmd = new ArchiveCommand(db);
                try {
-                       walk.reset();
-                       walk.addTree(tree);
-                       walk.setRecursive(true);
-                       while (walk.next()) {
-                               final String name = walk.getPathString();
-                               final FileMode mode = walk.getFileMode(0);
-
-                               if (mode == FileMode.TREE)
-                                       // ZIP entries for directories are optional.
-                                       // Leave them out, mimicking "git archive".
-                                       continue;
-
-                               walk.getObjectId(idBuf, 0);
-                               fmt.putEntry(name, mode, reader.open(idBuf), outa);
-                       }
+                       cmd.setTree(tree)
+                                       .setFormat(format)
+                                       .setOutputStream(outs).call();
                } finally {
-                       reader.release();
-                       outa.close();
+                       cmd.release();
                }
        }
-
-       static private void warnArchiveEntryModeIgnored(String name) {
-               System.err.println(MessageFormat.format( //
-                               CLIText.get().archiveEntryModeIgnored, //
-                               name));
-       }
-
-       public enum Format {
-               ZIP,
-               TAR
-       }
-
-       private static interface Archiver {
-               ArchiveOutputStream createArchiveOutputStream(OutputStream s);
-               void putEntry(String path, FileMode mode, //
-                               ObjectLoader loader, ArchiveOutputStream out) //
-                               throws IOException;
-       }
-
-       private static final Map<Format, Archiver> formats;
-       static {
-               Map<Format, Archiver> fmts = new EnumMap<Format, Archiver>(Format.class);
-               fmts.put(Format.ZIP, new Archiver() {
-                       public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
-                               return new ZipArchiveOutputStream(s);
-                       }
-
-                       public void putEntry(String path, FileMode mode, //
-                                       ObjectLoader loader, ArchiveOutputStream out) //
-                                       throws IOException {
-                               final ZipArchiveEntry entry = new ZipArchiveEntry(path);
-
-                               if (mode == FileMode.REGULAR_FILE) {
-                                       // ok
-                               } else if (mode == FileMode.EXECUTABLE_FILE
-                                               || mode == FileMode.SYMLINK) {
-                                       entry.setUnixMode(mode.getBits());
-                               } else {
-                                       warnArchiveEntryModeIgnored(path);
-                               }
-                               entry.setSize(loader.getSize());
-                               out.putArchiveEntry(entry);
-                               loader.copyTo(out);
-                               out.closeArchiveEntry();
-                       }
-               });
-               fmts.put(Format.TAR, new Archiver() {
-                       public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
-                               return new TarArchiveOutputStream(s);
-                       }
-
-                       public void putEntry(String path, FileMode mode, //
-                                       ObjectLoader loader, ArchiveOutputStream out) //
-                                       throws IOException {
-                               if (mode == FileMode.SYMLINK) {
-                                       final TarArchiveEntry entry = new TarArchiveEntry( //
-                                                       path, TarConstants.LF_SYMLINK);
-                                       entry.setLinkName(new String( //
-                                                       loader.getCachedBytes(100), "UTF-8")); //$NON-NLS-1$
-                                       out.putArchiveEntry(entry);
-                                       out.closeArchiveEntry();
-                                       return;
-                               }
-
-                               final TarArchiveEntry entry = new TarArchiveEntry(path);
-                               if (mode == FileMode.REGULAR_FILE ||
-                                   mode == FileMode.EXECUTABLE_FILE)
-                                       entry.setMode(mode.getBits());
-                               else
-                                       warnArchiveEntryModeIgnored(path);
-                               entry.setSize(loader.getSize());
-                               out.putArchiveEntry(entry);
-                               loader.copyTo(out);
-                               out.closeArchiveEntry();
-                       }
-               });
-               formats = fmts;
-       }
 }
index fe1c87b8a453c5a51e0a1c4160af3aff2a6718ca..727317b839c38454694ff26e0cff7a3292244875 100644 (file)
@@ -117,6 +117,7 @@ public class CLIText extends TranslationBundle {
        /***/ public String doesNotExist;
        /***/ public String dontOverwriteLocalChanges;
        /***/ public String everythingUpToDate;
+       /***/ public String exceptionCaughtDuringExecutionOfArchiveCommand;
        /***/ public String expectedNumberOfbytes;
        /***/ public String exporting;
        /***/ public String failedToCommitIndex;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java
new file mode 100644 (file)
index 0000000..eaa80ae
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2012 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.archive;
+
+import java.lang.String;
+import java.lang.System;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.EnumMap;
+import java.util.Map;
+import java.text.MessageFormat;
+
+import org.apache.commons.compress.archivers.ArchiveOutputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.compress.archivers.tar.TarConstants;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
+import org.eclipse.jgit.api.GitCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.MutableObjectId;
+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.pgm.CLIText;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+
+/**
+ * Create an archive of files from a named tree.
+ * <p>
+ * Examples (<code>git</code> is a {@link Git} instance):
+ * <p>
+ * Create a tarball from HEAD:
+ *
+ * <pre>
+ * cmd = new ArchiveCommand(git.getRepository());
+ * try {
+ *     cmd.setTree(db.resolve(&quot;HEAD&quot;))
+ *             .setOutputStream(out).call();
+ * } finally {
+ *     cmd.release();
+ * }
+ * </pre>
+ * <p>
+ * Create a ZIP file from master:
+ *
+ * <pre>
+ * try {
+ *     cmd.setTree(db.resolve(&quot;master&quot;))
+ *             .setFormat(ArchiveCommand.Format.ZIP)
+ *             .setOutputStream(out).call();
+ * } finally {
+ *     cmd.release();
+ * }
+ * </pre>
+ *
+ * @see <a href="http://git-htmldocs.googlecode.com/git/git-archive.html"
+ *      >Git documentation about archive</a>
+ */
+public class ArchiveCommand extends GitCommand<OutputStream> {
+       /**
+        * Available archival formats (corresponding to values for
+        * the --format= option)
+        */
+       public static enum Format {
+               ZIP,
+               TAR
+       }
+
+       private static interface Archiver {
+               ArchiveOutputStream createArchiveOutputStream(OutputStream s);
+               void putEntry(String path, FileMode mode, //
+                               ObjectLoader loader, ArchiveOutputStream out) //
+                               throws IOException;
+       }
+
+       private static void warnArchiveEntryModeIgnored(String name) {
+               System.err.println(MessageFormat.format( //
+                               CLIText.get().archiveEntryModeIgnored, //
+                               name));
+       }
+
+       private static final Map<Format, Archiver> formats;
+
+       static {
+               Map<Format, Archiver> fmts = new EnumMap<Format, Archiver>(Format.class);
+               fmts.put(Format.ZIP, new Archiver() {
+                       public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
+                               return new ZipArchiveOutputStream(s);
+                       }
+
+                       public void putEntry(String path, FileMode mode, //
+                                       ObjectLoader loader, ArchiveOutputStream out) //
+                                       throws IOException {
+                               final ZipArchiveEntry entry = new ZipArchiveEntry(path);
+
+                               if (mode == FileMode.REGULAR_FILE) {
+                                       // ok
+                               } else if (mode == FileMode.EXECUTABLE_FILE
+                                               || mode == FileMode.SYMLINK) {
+                                       entry.setUnixMode(mode.getBits());
+                               } else {
+                                       warnArchiveEntryModeIgnored(path);
+                               }
+                               entry.setSize(loader.getSize());
+                               out.putArchiveEntry(entry);
+                               loader.copyTo(out);
+                               out.closeArchiveEntry();
+                       }
+               });
+               fmts.put(Format.TAR, new Archiver() {
+                       public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
+                               return new TarArchiveOutputStream(s);
+                       }
+
+                       public void putEntry(String path, FileMode mode, //
+                                       ObjectLoader loader, ArchiveOutputStream out) //
+                                       throws IOException {
+                               if (mode == FileMode.SYMLINK) {
+                                       final TarArchiveEntry entry = new TarArchiveEntry( //
+                                                       path, TarConstants.LF_SYMLINK);
+                                       entry.setLinkName(new String( //
+                                                       loader.getCachedBytes(100), "UTF-8")); //$NON-NLS-1$
+                                       out.putArchiveEntry(entry);
+                                       out.closeArchiveEntry();
+                                       return;
+                               }
+
+                               final TarArchiveEntry entry = new TarArchiveEntry(path);
+                               if (mode == FileMode.REGULAR_FILE ||
+                                   mode == FileMode.EXECUTABLE_FILE)
+                                       entry.setMode(mode.getBits());
+                               else
+                                       warnArchiveEntryModeIgnored(path);
+                               entry.setSize(loader.getSize());
+                               out.putArchiveEntry(entry);
+                               loader.copyTo(out);
+                               out.closeArchiveEntry();
+                       }
+               });
+               formats = fmts;
+       }
+
+       private OutputStream out;
+       private TreeWalk walk;
+       private Format format = Format.TAR;
+
+       /**
+        * @param repo
+        */
+       public ArchiveCommand(Repository repo) {
+               super(repo);
+               walk = new TreeWalk(repo);
+       }
+
+       /**
+        * Release any resources used by the internal ObjectReader.
+        * <p>
+        * This does not close the output stream set with setOutputStream, which
+        * belongs to the caller.
+        */
+       public void release() {
+               walk.release();
+       }
+
+       /**
+        * @return the stream to which the archive has been written
+        */
+       @Override
+       public OutputStream call() throws GitAPIException {
+               final MutableObjectId idBuf = new MutableObjectId();
+               final Archiver fmt = formats.get(format);
+               final ArchiveOutputStream outa = fmt.createArchiveOutputStream(out);
+               final ObjectReader reader = walk.getObjectReader();
+
+               try {
+                       try {
+                               walk.setRecursive(true);
+                               while (walk.next()) {
+                                       final String name = walk.getPathString();
+                                       final FileMode mode = walk.getFileMode(0);
+
+                                       if (mode == FileMode.TREE)
+                                               // ZIP entries for directories are optional.
+                                               // Leave them out, mimicking "git archive".
+                                               continue;
+
+                                       walk.getObjectId(idBuf, 0);
+                                       fmt.putEntry(name, mode, reader.open(idBuf), outa);
+                               }
+                       } finally {
+                               outa.close();
+                       }
+               } catch (IOException e) {
+                       // TODO(jrn): Throw finer-grained errors.
+                       throw new JGitInternalException(
+                                       CLIText.get().exceptionCaughtDuringExecutionOfArchiveCommand, e);
+               }
+
+               return out;
+       }
+
+       /**
+        * @param tree
+        *            the tag, commit, or tree object to produce an archive for
+        * @return this
+        */
+       public ArchiveCommand setTree(ObjectId tree) throws IOException {
+               final RevWalk rw = new RevWalk(walk.getObjectReader());
+               walk.reset(rw.parseTree(tree));
+               return this;
+       }
+
+       /**
+        * @param out
+        *            the stream to which to write the archive
+        * @return this
+        */
+       public ArchiveCommand setOutputStream(OutputStream out) {
+               this.out = out;
+               return this;
+       }
+
+       /**
+        * @param fmt
+        *            archive format (e.g., Format.TAR)
+        * @return this
+        */
+       public ArchiveCommand setFormat(Format fmt) {
+               this.format = fmt;
+               return this;
+       }
+}