First step toward making ArchiveCommand itself format-agnostic. Change-Id: I3cff5fce28fa7a19e34f8291cfb5b62f16429713tags/v3.1.0.201309270735-rc1
@@ -102,6 +102,13 @@ public class ArchiveTest extends CLIRepositoryTestCase { | |||
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!"); |
@@ -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} |
@@ -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 @@ class Archive extends TextBuiltin { | |||
cmd.setTree(tree) | |||
.setFormat(format) | |||
.setOutputStream(outs).call(); | |||
} catch (GitAPIException e) { | |||
throw die(e.getMessage()); | |||
} finally { | |||
cmd.release(); | |||
} |
@@ -44,15 +44,11 @@ package org.eclipse.jgit.pgm.archive; | |||
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 @@ import org.eclipse.jgit.treewalk.TreeWalk; | |||
* <pre> | |||
* try { | |||
* cmd.setTree(db.resolve("master")) | |||
* .setFormat(ArchiveCommand.Format.ZIP) | |||
* .setFormat("zip") | |||
* .setOutputStream(out).call(); | |||
* } finally { | |||
* cmd.release(); | |||
@@ -103,91 +99,69 @@ import org.eclipse.jgit.treewalk.TreeWalk; | |||
*/ | |||
public class ArchiveCommand extends GitCommand<OutputStream> { | |||
/** | |||
* 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<Format, Archiver> 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; | |||
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); | |||
} | |||
private final String format; | |||
public void putEntry(String path, FileMode mode, // | |||
ObjectLoader loader, ArchiveOutputStream out) // | |||
throws IOException { | |||
final ZipArchiveEntry entry = new ZipArchiveEntry(path); | |||
/** | |||
* @param format the problematic format name | |||
*/ | |||
public UnsupportedFormatException(String format) { | |||
super(MessageFormat.format(CLIText.get().unsupportedArchiveFormat, format)); | |||
this.format = format; | |||
} | |||
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); | |||
} | |||
/** | |||
* @return the problematic format name | |||
*/ | |||
public String getFormat() { | |||
return format; | |||
} | |||
} | |||
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; | |||
} | |||
private static final ConcurrentMap<String, Format> formats = | |||
new ConcurrentHashMap<String, Format>(); | |||
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; | |||
static { | |||
formats.put("zip", new ZipFormat()); | |||
formats.put("tar", new TarFormat()); | |||
} | |||
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 class ArchiveCommand extends GitCommand<OutputStream> { | |||
@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 class ArchiveCommand extends GitCommand<OutputStream> { | |||
/** | |||
* @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; | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -236,6 +236,7 @@ public class CLIText extends TranslationBundle { | |||
/***/ public char[] unknownIoErrorStdout; | |||
/***/ public String unknownMergeStrategy; | |||
/***/ public String unmergedPaths; | |||
/***/ public String unsupportedArchiveFormat; | |||
/***/ public String unsupportedOperation; | |||
/***/ public String untrackedFiles; | |||
/***/ public String updating; |
@@ -45,11 +45,19 @@ package org.eclipse.jgit.api.errors; | |||
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); | |||
} | |||
} |