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 <jrn@google.com>tags/v3.3.0.201402191814-rc1
@@ -338,6 +338,99 @@ public class ArchiveTest extends CLIRepositoryTestCase { | |||
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"); |
@@ -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 |
@@ -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 @@ class Archive extends TextBuiltin { | |||
ArchiveCommand cmd = new Git(db).archive() | |||
.setTree(tree) | |||
.setFormat(format) | |||
.setPrefix(prefix) | |||
.setOutputStream(stream); | |||
if (output != null) | |||
cmd.setFilename(output); |
@@ -143,6 +143,7 @@ public class CLIText extends TranslationBundle { | |||
/***/ 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; |
@@ -250,6 +250,7 @@ public class ArchiveCommand extends GitCommand<OutputStream> { | |||
private OutputStream out; | |||
private ObjectId tree; | |||
private String prefix; | |||
private String format; | |||
/** Filename suffix, for automatically choosing a format. */ | |||
@@ -264,6 +265,7 @@ public class ArchiveCommand extends GitCommand<OutputStream> { | |||
} | |||
private <T extends Closeable> OutputStream writeArchive(Format<T> fmt) { | |||
final String pfx = prefix == null ? "" : prefix; | |||
final TreeWalk walk = new TreeWalk(repo); | |||
try { | |||
final T outa = fmt.createArchiveOutputStream(out); | |||
@@ -275,7 +277,7 @@ public class ArchiveCommand extends GitCommand<OutputStream> { | |||
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 class ArchiveCommand extends GitCommand<OutputStream> { | |||
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 |