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 583beefed..cad379774 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 @@ -43,11 +43,16 @@ package org.eclipse.jgit.pgm; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNoException; import java.io.BufferedReader; import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.IOException; +import java.io.OutputStream; import java.lang.String; import java.util.ArrayList; @@ -57,7 +62,9 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.lib.CLIRepositoryTestCase; +import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.pgm.CLIGitCommand; import org.junit.Before; import org.junit.Ignore; @@ -125,6 +132,32 @@ public class ArchiveTest extends CLIRepositoryTestCase { assertArrayEquals(expect, actual); } + @Test + public void testArchivePreservesMode() 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[] zipData = CLIGitCommand.rawExecute( // + "git archive master", db); + writeRaw("zip-with-modes.zip", zipData); + assertContainsEntryWithMode("zip-with-modes.zip", "-rw-", "plain"); + assertContainsEntryWithMode("zip-with-modes.zip", "-rwx", "executable"); + assertContainsEntryWithMode("zip-with-modes.zip", "l", "symlink"); + } + @Test public void testArchivePreservesContent() throws Exception { final String payload = "“The quick brown fox jumps over the lazy dog!”"; @@ -138,6 +171,47 @@ public class ArchiveTest extends CLIRepositoryTestCase { zipEntryContent(result, "xyzzy")); } + private void assertContainsEntryWithMode(String zipFilename, String mode, String name) // + throws Exception { + final File cwd = db.getWorkTree(); + final ProcessBuilder procBuilder = new ProcessBuilder("zipinfo", zipFilename) // + .directory(cwd) // + .redirectErrorStream(true); + Process proc = null; + try { + proc = procBuilder.start(); + } catch (IOException e) { + // On machines without a "zipinfo" command, let the test pass. + assumeNoException(e); + } + + proc.getOutputStream().close(); + final BufferedReader reader = new BufferedReader( // + new InputStreamReader(proc.getInputStream(), "UTF-8")); + try { + String line; + while ((line = reader.readLine()) != null) + if (line.startsWith(mode) && line.endsWith(name)) + // found it! + return; + fail("expected entry " + name + " with mode " + mode + " but found none"); + } finally { + proc.getOutputStream().close(); + proc.destroy(); + } + } + + private void writeRaw(String filename, byte[] data) // + throws IOException { + final File path = new File(db.getWorkTree(), filename); + final OutputStream out = new FileOutputStream(path); + try { + out.write(data); + } finally { + out.close(); + } + } + private static String[] listZipEntries(byte[] zipData) throws IOException { final List l = new ArrayList(); final ZipInputStream in = new ZipInputStream( // 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 ec937a3cc..cc2f287b2 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 @@ -90,14 +90,20 @@ class Archive extends TextBuiltin { final ZipArchiveEntry entry = new ZipArchiveEntry(name); final ObjectLoader loader = reader.open(idBuf); entry.setSize(loader.getSize()); - out.putArchiveEntry(entry); - loader.copyTo(out); - out.closeArchiveEntry(); - if (mode != FileMode.REGULAR_FILE) + if (mode == FileMode.REGULAR_FILE) + ; // ok + else if (mode == FileMode.EXECUTABLE_FILE || + mode == FileMode.SYMLINK) + entry.setUnixMode(mode.getBits()); + else System.err.println(MessageFormat.format( // CLIText.get().archiveEntryModeIgnored, // name)); + + out.putArchiveEntry(entry); + loader.copyTo(out); + out.closeArchiveEntry(); } out.close();