From f2abbd0ea99aed638ca098e336f60f52bc923237 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Dec 2013 11:22:46 -0800 Subject: [PATCH] archive: Prepend a specified prefix to all entry filenames Common practice when distributing tarballs is to prefix all entries with a single directory name so when the tarball is extracted it all falls neatly into a single directory. Add a setPrefix() method to ArchiveCommand to support this. Change-Id: I16b2832ef98c30977f6b77b646728b83d93c196f Signed-off-by: Jonathan Nieder --- .../tst/org/eclipse/jgit/pgm/ArchiveTest.java | 93 +++++++++++++++++++ .../jgit/pgm/internal/CLIText.properties | 2 + .../src/org/eclipse/jgit/pgm/Archive.java | 4 + .../eclipse/jgit/pgm/internal/CLIText.java | 1 + .../org/eclipse/jgit/api/ArchiveCommand.java | 16 +++- 5 files changed, 115 insertions(+), 1 deletion(-) 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 87137b475..816094aef 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 @@ -338,6 +338,99 @@ public void testTarWithSubdir() throws Exception { assertArrayEquals(expect, actual); } + private void commitBazAndFooSlashBar() throws Exception { + writeTrashFile("baz", "a file"); + writeTrashFile("foo/bar", "another file"); + git.add().addFilepattern("baz").call(); + git.add().addFilepattern("foo").call(); + git.commit().setMessage("sample commit").call(); + } + + @Test + public void testArchivePrefixOption() throws Exception { + commitBazAndFooSlashBar(); + byte[] result = CLIGitCommand.rawExecute( + "git archive --prefix=x/ --format=zip master", db); + String[] expect = { "x/baz", "x/foo/bar" }; + String[] actual = listZipEntries(result); + + Arrays.sort(expect); + Arrays.sort(actual); + assertArrayEquals(expect, actual); + } + + @Test + public void testTarPrefixOption() throws Exception { + commitBazAndFooSlashBar(); + byte[] result = CLIGitCommand.rawExecute( + "git archive --prefix=x/ --format=tar master", db); + String[] expect = { "x/baz", "x/foo/bar" }; + String[] actual = listTarEntries(result); + + Arrays.sort(expect); + Arrays.sort(actual); + assertArrayEquals(expect, actual); + } + + private void commitFoo() throws Exception { + writeTrashFile("foo", "a file"); + git.add().addFilepattern("foo").call(); + git.commit().setMessage("boring commit").call(); + } + + @Test + public void testPrefixDoesNotNormalizeDoubleSlash() throws Exception { + commitFoo(); + byte[] result = CLIGitCommand.rawExecute( + "git archive --prefix=x// --format=zip master", db); + String[] expect = { "x//foo" }; + assertArrayEquals(expect, listZipEntries(result)); + } + + @Test + public void testPrefixDoesNotNormalizeDoubleSlashInTar() throws Exception { + commitFoo(); + final byte[] result = CLIGitCommand.rawExecute( // + "git archive --prefix=x// --format=tar master", db); + String[] expect = { "x//foo" }; + assertArrayEquals(expect, listTarEntries(result)); + } + + /** + * The prefix passed to "git archive" need not end with '/'. + * In practice it is not very common to have a nonempty prefix + * that does not name a directory (and hence end with /), but + * since git has historically supported other prefixes, we do, + * too. + * + * @throws Exception + */ + @Test + public void testPrefixWithoutTrailingSlash() throws Exception { + commitBazAndFooSlashBar(); + byte[] result = CLIGitCommand.rawExecute( + "git archive --prefix=my- --format=zip master", db); + String[] expect = { "my-baz", "my-foo/bar" }; + String[] actual = listZipEntries(result); + + Arrays.sort(expect); + Arrays.sort(actual); + assertArrayEquals(expect, actual); + } + + @Test + public void testTarPrefixWithoutTrailingSlash() throws Exception { + commitBazAndFooSlashBar(); + final byte[] result = CLIGitCommand.rawExecute( // + "git archive --prefix=my- --format=tar master", db); + String[] expect = { "my-baz", "my-foo/bar" }; + 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"); 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 d23f37899..1d2a8971f 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 @@ -74,6 +74,7 @@ mergeWentWellStoppedBeforeCommitting=Automatic merge went well; stopped before c metaVar_DAG=DAG metaVar_KEY=KEY metaVar_archiveFormat=format +metaVar_archivePrefix=prefix/ metaVar_arg=ARG metaVar_author=AUTHOR metaVar_base=base @@ -229,6 +230,7 @@ usage_approveDestructionOfRepository=approve destruction of repository usage_archive=zip up files from the named tree usage_archiveFormat=archive format. Currently supported formats: 'tar', 'zip', 'tgz', 'tbz2', 'txz' usage_archiveOutput=output file to write the archive to +usage_archivePrefix=string to prepend to each pathname in the archive usage_blameLongRevision=show long revision usage_blameRange=annotate only the given range usage_blameRawTimestamp=show raw timestamp 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 7b88a9434..80bb9ec9d 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 @@ -69,6 +69,9 @@ class Archive extends TextBuiltin { @Option(name = "--format", metaVar = "metaVar_archiveFormat", usage = "usage_archiveFormat") private String format; + @Option(name = "--prefix", metaVar = "metaVar_archivePrefix", usage = "usage_archivePrefix") + private String prefix; + @Option(name = "--output", aliases = { "-o" }, metaVar = "metaVar_file", usage = "usage_archiveOutput") private String output; @@ -88,6 +91,7 @@ protected void run() throws Exception { ArchiveCommand cmd = new Git(db).archive() .setTree(tree) .setFormat(format) + .setPrefix(prefix) .setOutputStream(stream); if (output != null) cmd.setFilename(output); 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 a51313ae1..c42e5fb59 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 @@ -143,6 +143,7 @@ public static String formatLine(String line) { /***/ public String mergeWentWellStoppedBeforeCommitting; /***/ public String metaVar_KEY; /***/ public String metaVar_archiveFormat; + /***/ public String metaVar_archivePrefix; /***/ public String metaVar_arg; /***/ public String metaVar_author; /***/ public String metaVar_bucket; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java index 42d0f6512..c17330e6f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java @@ -250,6 +250,7 @@ private static Format lookupFormat(String formatName) throws UnsupportedForma private OutputStream out; private ObjectId tree; + private String prefix; private String format; /** Filename suffix, for automatically choosing a format. */ @@ -264,6 +265,7 @@ public ArchiveCommand(Repository repo) { } private OutputStream writeArchive(Format fmt) { + final String pfx = prefix == null ? "" : prefix; final TreeWalk walk = new TreeWalk(repo); try { final T outa = fmt.createArchiveOutputStream(out); @@ -275,7 +277,7 @@ private OutputStream writeArchive(Format fmt) { walk.reset(rw.parseTree(tree)); walk.setRecursive(true); while (walk.next()) { - final String name = walk.getPathString(); + final String name = pfx + walk.getPathString(); final FileMode mode = walk.getFileMode(0); if (mode == FileMode.TREE) @@ -329,6 +331,18 @@ public ArchiveCommand setTree(ObjectId tree) { return this; } + /** + * @param prefix + * string prefixed to filenames in archive (e.g., "master/"). + * null means to not use any leading prefix. + * @return this + * @since 3.3 + */ + public ArchiveCommand setPrefix(String prefix) { + this.prefix = prefix; + return this; + } + /** * Set the intended filename for the produced archive. Currently the only * effect is to determine the default archive format when none is specified