diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java index bcf272852..cb2a7258a 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java @@ -102,6 +102,13 @@ public void testEmptyTar() throws Exception { assertArrayEquals(new String[0], listTarEntries(result)); } + @Test + public void testUnrecognizedFormat() throws Exception { + final String[] expect = new String[] { "fatal: Unknown archive format 'nonsense'" }; + final String[] actual = execute("git archive --format=nonsense " + emptyTree); + assertArrayEquals(expect, actual); + } + @Test public void testArchiveWithFiles() throws Exception { writeTrashFile("a", "a file with content!"); diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index cdf712395..3fa167e95 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties @@ -173,6 +173,7 @@ tooManyRefsGiven=Too many refs given unknownIoErrorStdout=An unknown I/O error occurred on standard output unknownMergeStrategy=unknown merge strategy {0} specified unmergedPaths=Unmerged paths: +unsupportedArchiveFormat=Unknown archive format ''{0}'' unsupportedOperation=Unsupported operation: {0} untrackedFiles=Untracked files: updating=Updating {0}..{1} diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java index 815c96bfe..6d4f5aa51 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.pgm; +import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.pgm.TextBuiltin; import org.eclipse.jgit.pgm.archive.ArchiveCommand; @@ -56,7 +57,7 @@ class Archive extends TextBuiltin { private ObjectId tree; @Option(name = "--format", metaVar = "metaVar_archiveFormat", usage = "usage_archiveFormat") - private ArchiveCommand.Format format = ArchiveCommand.Format.ZIP; + private String format = "zip"; @Override protected void run() throws Exception { @@ -68,6 +69,8 @@ protected void run() throws Exception { cmd.setTree(tree) .setFormat(format) .setOutputStream(outs).call(); + } catch (GitAPIException e) { + throw die(e.getMessage()); } finally { cmd.release(); } 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 index 1235d0ae0..ff5b0d0cd 100644 --- 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 @@ -44,15 +44,11 @@ import java.io.IOException; import java.io.OutputStream; -import java.util.EnumMap; -import java.util.Map; +import java.text.MessageFormat; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; 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.Git; import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.errors.GitAPIException; @@ -89,7 +85,7 @@ *
  * try {
  *	cmd.setTree(db.resolve("master"))
- *		.setFormat(ArchiveCommand.Format.ZIP)
+ *		.setFormat("zip")
  *		.setOutputStream(out).call();
  * } finally {
  *	cmd.release();
@@ -103,91 +99,69 @@
  */
 public class ArchiveCommand extends GitCommand {
 	/**
-	 * Available archival formats (corresponding to values for
-	 * the --format= option)
+	 * Archival format.
+	 *
+	 * Usage:
+	 *	Repository repo = git.getRepository();
+	 *	ArchiveOutputStream out = format.createArchiveOutputStream(System.out);
+	 *	try {
+	 *		for (...) {
+	 *			format.putEntry(path, mode, repo.open(objectId), out);
+	 *		}
+	 *	} finally {
+	 *		out.close();
+	 *	}
 	 */
-	public static enum Format {
-		/** Zip format */
-		ZIP,
-
-		/** Posix TAR-format */
-		TAR
-	}
-
-	private static interface Archiver {
+	public static interface Format {
 		ArchiveOutputStream createArchiveOutputStream(OutputStream s);
 		void putEntry(String path, FileMode mode, //
 				ObjectLoader loader, ArchiveOutputStream out) //
 				throws IOException;
 	}
 
-	private static final Map formats;
+	/**
+	 * Signals an attempt to use an archival format that ArchiveCommand
+	 * doesn't know about (for example due to a typo).
+	 */
+	public static class UnsupportedFormatException extends GitAPIException {
+		private static final long serialVersionUID = 1L;
+
+		private final String format;
+
+		/**
+		 * @param format the problematic format name
+		 */
+		public UnsupportedFormatException(String format) {
+			super(MessageFormat.format(CLIText.get().unsupportedArchiveFormat, format));
+			this.format = format;
+		}
+
+		/**
+		 * @return the problematic format name
+		 */
+		public String getFormat() {
+			return format;
+		}
+	}
+
+	private static final ConcurrentMap formats =
+			new ConcurrentHashMap();
 
 	static {
-		Map fmts = new EnumMap(Format.class);
-		fmts.put(Format.ZIP, new Archiver() {
-			public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
-				return new ZipArchiveOutputStream(s);
-			}
+		formats.put("zip", new ZipFormat());
+		formats.put("tar", new TarFormat());
+	}
 
-			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 {
-					// TODO(jrn): Let the caller know the tree contained
-					// an entry with unsupported mode (e.g., a submodule).
-				}
-				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 {
-					// TODO(jrn): Let the caller know the tree contained
-					// an entry with unsupported mode (e.g., a submodule).
-				}
-				entry.setSize(loader.getSize());
-				out.putArchiveEntry(entry);
-				loader.copyTo(out);
-				out.closeArchiveEntry();
-			}
-		});
-		formats = fmts;
+	private static Format lookupFormat(String formatName) throws UnsupportedFormatException {
+		Format fmt = formats.get(formatName);
+		if (fmt == null)
+			throw new UnsupportedFormatException(formatName);
+		return fmt;
 	}
 
 	private OutputStream out;
 	private TreeWalk walk;
-	private Format format = Format.TAR;
+	private String format = "tar";
 
 	/**
 	 * @param repo
@@ -213,7 +187,7 @@ public void release() {
 	@Override
 	public OutputStream call() throws GitAPIException {
 		final MutableObjectId idBuf = new MutableObjectId();
-		final Archiver fmt = formats.get(format);
+		final Format fmt = lookupFormat(format);
 		final ArchiveOutputStream outa = fmt.createArchiveOutputStream(out);
 		final ObjectReader reader = walk.getObjectReader();
 
@@ -268,10 +242,10 @@ public ArchiveCommand setOutputStream(OutputStream out) {
 
 	/**
 	 * @param fmt
-	 *	      archive format (e.g., Format.TAR)
+	 *	      archive format (e.g., "tar" or "zip")
 	 * @return this
 	 */
-	public ArchiveCommand setFormat(Format fmt) {
+	public ArchiveCommand setFormat(String fmt) {
 		this.format = fmt;
 		return this;
 	}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/TarFormat.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/TarFormat.java
new file mode 100644
index 000000000..c27fb350b
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/TarFormat.java
@@ -0,0 +1,85 @@
+/*
+ * 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.io.IOException;
+import java.io.OutputStream;
+
+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.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectLoader;
+
+class TarFormat implements ArchiveCommand.Format {
+	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 {
+			// TODO(jrn): Let the caller know the tree contained
+			// an entry with unsupported mode (e.g., a submodule).
+		}
+		entry.setSize(loader.getSize());
+		out.putArchiveEntry(entry);
+		loader.copyTo(out);
+		out.closeArchiveEntry();
+	}
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ZipFormat.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ZipFormat.java
new file mode 100644
index 000000000..d08428f54
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ZipFormat.java
@@ -0,0 +1,77 @@
+/*
+ * 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.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.commons.compress.archivers.ArchiveOutputStream;
+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.ObjectLoader;
+
+class ZipFormat implements ArchiveCommand.Format {
+	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 {
+			// TODO(jrn): Let the caller know the tree contained
+			// an entry with unsupported mode (e.g., a submodule).
+		}
+		entry.setSize(loader.getSize());
+		out.putArchiveEntry(entry);
+		loader.copyTo(out);
+		out.closeArchiveEntry();
+	}
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index 62865d51c..937707bd3 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -236,6 +236,7 @@ public static String formatLine(String line) {
 	/***/ public char[] unknownIoErrorStdout;
 	/***/ public String unknownMergeStrategy;
 	/***/ public String unmergedPaths;
+	/***/ public String unsupportedArchiveFormat;
 	/***/ public String unsupportedOperation;
 	/***/ public String untrackedFiles;
 	/***/ public String updating;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/GitAPIException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/GitAPIException.java
index ba38529ab..92599ca7d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/GitAPIException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/GitAPIException.java
@@ -45,11 +45,19 @@
 public abstract class GitAPIException extends Exception {
 	private static final long serialVersionUID = 1L;
 
-	GitAPIException(String message, Throwable cause) {
+	/**
+	 * Constructs a new exception with the specified detail
+	 * message and cause.
+	 */
+	protected GitAPIException(String message, Throwable cause) {
 		super(message, cause);
 	}
 
-	GitAPIException(String message) {
+	/**
+	 * Constructs a new exception with the specified detail
+	 * message and no cause.
+	 */
+	protected GitAPIException(String message) {
 		super(message);
 	}
 }