import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
-
+import java.lang.Object;
import java.lang.String;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
assertArrayEquals(new String[0], listZipEntries(result));
}
+ @Test
+ public void testEmptyTar() throws Exception {
+ final byte[] result = CLIGitCommand.rawExecute( //
+ "git archive --format=tar " + emptyTree, db);
+ assertArrayEquals(new String[0], listTarEntries(result));
+ }
+
@Test
public void testArchiveWithFiles() throws Exception {
writeTrashFile("a", "a file with content!");
assertArrayEquals(expect, actual);
}
+ @Test
+ public void testTarWithSubdir() throws Exception {
+ writeTrashFile("a", "a file with content!");
+ writeTrashFile("b.c", "before subdir in git sort order");
+ writeTrashFile("b0c", "after subdir in git sort order");
+ writeTrashFile("c", "");
+ git.add().addFilepattern("a").call();
+ git.add().addFilepattern("b.c").call();
+ git.add().addFilepattern("b0c").call();
+ git.add().addFilepattern("c").call();
+ git.commit().setMessage("populate toplevel").call();
+ writeTrashFile("b/b", "file in subdirectory");
+ writeTrashFile("b/a", "another file in subdirectory");
+ git.add().addFilepattern("b").call();
+ git.commit().setMessage("add subdir").call();
+
+ final byte[] result = CLIGitCommand.rawExecute( //
+ "git archive --format=tar master", db);
+ String[] expect = { "a", "b.c", "b0c", "b/a", "b/b", "c" };
+ String[] actual = listTarEntries(result);
+
+ Arrays.sort(expect);
+ Arrays.sort(actual);
+ assertArrayEquals(expect, actual);
+ }
+
@Test
public void testArchivePreservesMode() throws Exception {
writeTrashFile("plain", "a file with content");
assertContainsEntryWithMode("zip-with-modes.zip", "l", "symlink");
}
+ @Test
+ public void testTarPreservesMode() throws Exception {
+ writeTrashFile("plain", "a file with content");
+ writeTrashFile("executable", "an executable file");
+ writeTrashFile("symlink", "plain");
+ git.add().addFilepattern("plain").call();
+ git.add().addFilepattern("executable").call();
+ git.add().addFilepattern("symlink").call();
+
+ DirCache cache = db.lockDirCache();
+ cache.getEntry("executable").setFileMode(FileMode.EXECUTABLE_FILE);
+ cache.getEntry("symlink").setFileMode(FileMode.SYMLINK);
+ cache.write();
+ cache.commit();
+ cache.unlock();
+
+ git.commit().setMessage("three files with different modes").call();
+
+ final byte[] archive = CLIGitCommand.rawExecute( //
+ "git archive --format=tar master", db);
+ writeRaw("with-modes.tar", archive);
+ assertTarContainsEntry("with-modes.tar", "-rw-r--r--", "plain");
+ assertTarContainsEntry("with-modes.tar", "-rwxr-xr-x", "executable");
+ assertTarContainsEntry("with-modes.tar", "l", "symlink -> plain");
+ }
+
@Test
public void testArchivePreservesContent() throws Exception {
final String payload = "“The quick brown fox jumps over the lazy dog!”";
zipEntryContent(result, "xyzzy"));
}
- private void assertContainsEntryWithMode(String zipFilename, String mode, String name) //
- throws Exception {
+ @Test
+ public void testTarPreservesContent() throws Exception {
+ final String payload = "“The quick brown fox jumps over the lazy dog!”";
+ writeTrashFile("xyzzy", payload);
+ git.add().addFilepattern("xyzzy").call();
+ git.commit().setMessage("add file with content").call();
+
+ final byte[] result = CLIGitCommand.rawExecute( //
+ "git archive --format=tar HEAD", db);
+ assertArrayEquals(new String[] { payload }, //
+ tarEntryContent(result, "xyzzy"));
+ }
+
+ private Process spawnAssumingCommandPresent(String... cmdline) {
final File cwd = db.getWorkTree();
- final ProcessBuilder procBuilder = new ProcessBuilder("zipinfo", zipFilename) //
+ final ProcessBuilder procBuilder = new ProcessBuilder(cmdline) //
.directory(cwd) //
.redirectErrorStream(true);
Process proc = null;
try {
proc = procBuilder.start();
} catch (IOException e) {
- // On machines without a "zipinfo" command, let the test pass.
+ // On machines without `cmdline[0]`, let the test pass.
assumeNoException(e);
}
- proc.getOutputStream().close();
- final BufferedReader reader = new BufferedReader( //
+ return proc;
+ }
+
+ private BufferedReader readFromProcess(Process proc) throws Exception {
+ return new BufferedReader( //
new InputStreamReader(proc.getInputStream(), "UTF-8"));
+ }
+
+ private void grepForEntry(String name, String mode, String... cmdline) //
+ throws Exception {
+ final Process proc = spawnAssumingCommandPresent(cmdline);
+ proc.getOutputStream().close();
+ final BufferedReader reader = readFromProcess(proc);
try {
String line;
while ((line = reader.readLine()) != null)
}
}
+ private void assertContainsEntryWithMode(String zipFilename, String mode, String name) //
+ throws Exception {
+ grepForEntry(name, mode, "zipinfo", zipFilename);
+ }
+
+ private void assertTarContainsEntry(String tarfile, String mode, String name) //
+ throws Exception {
+ grepForEntry(name, mode, "tar", "tvf", tarfile);
+ }
+
private void writeRaw(String filename, byte[] data) //
throws IOException {
final File path = new File(db.getWorkTree(), filename);
return l.toArray(new String[l.size()]);
}
+ private static Future<Object> writeAsync(final OutputStream stream, final byte[] data) {
+ final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ return executor.submit(new Callable<Object>() { //
+ public Object call() throws IOException {
+ try {
+ stream.write(data);
+ return null;
+ } finally {
+ stream.close();
+ }
+ }
+ });
+ }
+
+ private String[] listTarEntries(byte[] tarData) throws Exception {
+ final List<String> l = new ArrayList<String>();
+ final Process proc = spawnAssumingCommandPresent("tar", "tf", "-");
+ final BufferedReader reader = readFromProcess(proc);
+ final OutputStream out = proc.getOutputStream();
+
+ // Dump tarball to tar stdin in background
+ final Future<?> writing = writeAsync(out, tarData);
+
+ try {
+ String line;
+ while ((line = reader.readLine()) != null)
+ l.add(line);
+
+ return l.toArray(new String[l.size()]);
+ } finally {
+ writing.get();
+ reader.close();
+ proc.destroy();
+ }
+ }
+
private static String[] zipEntryContent(byte[] zipData, String path) //
throws IOException {
final ZipInputStream in = new ZipInputStream( //
// not found
return null;
}
+
+ private String[] tarEntryContent(byte[] tarData, String path) //
+ throws Exception {
+ final List<String> l = new ArrayList<String>();
+ final Process proc = spawnAssumingCommandPresent("tar", "Oxf", "-", path);
+ final BufferedReader reader = readFromProcess(proc);
+ final OutputStream out = proc.getOutputStream();
+ final Future<?> writing = writeAsync(out, tarData);
+
+ try {
+ String line;
+ while ((line = reader.readLine()) != null)
+ l.add(line);
+
+ return l.toArray(new String[l.size()]);
+ } finally {
+ writing.get();
+ reader.close();
+ proc.destroy();
+ }
+ }
}
import org.apache.commons.compress.archivers.ArchiveEntry;
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;
}
public enum Format {
- ZIP
+ ZIP,
+ TAR
};
private static interface Archiver {
out.closeArchiveEntry();
}
});
+ fmts.put(Format.TAR, new Archiver() {
+ @Override
+ public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
+ return new TarArchiveOutputStream(s);
+ }
+
+ @Override
+ 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"));
+ 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;
}
}