Support 'git-upload-archive'
This allows use the standard git archive command to create an archive
of the content of a repository:
$ git archive -f tar.bz2 --prefix=foo-1.0/ \
--remote=ssh://john@gerrit:29418/foo \
refs/changes/73/673/1 > foo-1.0.tar.bz2
Different compression levels can be configured for zip format:
$ git archive -f zip -9 \
--remote=ssh://john@gerrit:29418/foo \
refs/changes/73/673/1 > foo.zip
TEST PLAN:
buck test --include ssh
Bug: Issue 2061
Change-Id: Ifc1a92bacef3155cf474adee883cbe587dd8759f
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 61c764a..8c4c4a9 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1451,7 +1451,7 @@
[[download.archive]]download.archive::
+
Specifies which archive formats, if any, should be offered on the change
-screen:
+screen and supported for `git-upload-archive` operation:
+
----
[download]
@@ -1459,11 +1459,17 @@
archive = tbz2
archive = tgz
archive = txz
+ archive = zip
----
If `download.archive` is not specified defaults to all archive
commands. Set to `off` or empty string to disable.
+Zip is not supported because it may be interpreted by a Java plugin as a
+valid JAR file, whose code would have access to cookies on the domain.
+For this reason `zip` format is always excluded from formats offered
+through the `Download` drop down or accessible in the REST API.
+
[[gc]]
=== Section gc
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
index 07d0f50..b07ed30 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
@@ -15,11 +15,13 @@
package com.google.gerrit.acceptance;
import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.eclipse.jgit.lib.Config;
import java.util.ArrayList;
+import java.util.Arrays;
class ConfigAnnotationParser {
private static Splitter splitter = Splitter.on(".").trimResults();
@@ -45,9 +47,19 @@
private static void parseAnnotation(Config cfg, GerritConfig c) {
ArrayList<String> l = Lists.newArrayList(splitter.split(c.name()));
if (l.size() == 2) {
- cfg.setString(l.get(0), null, l.get(1), c.value());
+ if (!Strings.isNullOrEmpty(c.value())) {
+ cfg.setString(l.get(0), null, l.get(1), c.value());
+ } else {
+ String[] values = c.values();
+ cfg.setStringList(l.get(0), null, l.get(1), Arrays.asList(values));
+ }
} else if (l.size() == 3) {
- cfg.setString(l.get(0), l.get(1), l.get(2), c.value());
+ if (!Strings.isNullOrEmpty(c.value())) {
+ cfg.setString(l.get(0), l.get(1), l.get(2), c.value());
+ } else {
+ cfg.setStringList(l.get(0), l.get(1), l.get(2),
+ Arrays.asList(c.value()));
+ }
} else {
throw new IllegalArgumentException(
"GerritConfig.name must be of the format"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
index 5cb1229..4b956a2 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
@@ -24,5 +24,6 @@
@Retention(RUNTIME)
public @interface GerritConfig {
String name();
- String value();
+ String value() default "";
+ String[] values() default "";
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java
index 701b337..794f832 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java
@@ -42,11 +42,12 @@
}
@SuppressWarnings("resource")
- public String exec(String command) throws JSchException, IOException {
+ public String exec(String command, InputStream opt) throws JSchException,
+ IOException {
ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
try {
channel.setCommand(command);
- channel.setInputStream(null);
+ channel.setInputStream(opt);
InputStream in = channel.getInputStream();
channel.connect();
@@ -60,6 +61,20 @@
}
}
+ public InputStream exec2(String command, InputStream opt) throws JSchException,
+ IOException {
+ ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
+ channel.setCommand(command);
+ channel.setInputStream(opt);
+ InputStream in = channel.getInputStream();
+ channel.connect();
+ return in;
+ }
+
+ public String exec(String command) throws JSchException, IOException {
+ return exec(command, null);
+ }
+
public boolean hasError() {
return error != null;
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BUCK
index 2ea5dec..d067b34 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BUCK
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BUCK
@@ -2,5 +2,6 @@
acceptance_tests(
srcs = glob(['*IT.java']),
+ deps = ['//lib/commons:compress'],
labels = ['ssh'],
)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
new file mode 100644
index 0000000..88821ce
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
@@ -0,0 +1,127 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.ssh;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.PushOneCommit;
+
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
+import org.eclipse.jgit.transport.PacketLineIn;
+import org.eclipse.jgit.transport.PacketLineOut;
+import org.eclipse.jgit.util.IO;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
+import java.util.TreeSet;
+
+@NoHttpd
+public class UploadArchiveIT extends AbstractDaemonTest {
+
+ @Test
+ @GerritConfig(name = "download.archive", value = "off")
+ public void archiveFeatureOff() throws Exception {
+ archiveNotPermitted();
+ }
+
+ @Test
+ @GerritConfig(name = "download.archive", values = {"tar", "tbz2", "tgz", "txz"})
+ public void zipFormatDisabled() throws Exception {
+ archiveNotPermitted();
+ }
+
+ @Test
+ public void zipFormat() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String abbreviated = r.getCommitId().abbreviate(8).name();
+ String c = command(r, abbreviated);
+
+ InputStream out =
+ sshSession.exec2("git-upload-archive " + project.get(),
+ argumentsToInputStream(c));
+
+ // Wrap with PacketLineIn to read ACK bytes from output stream
+ PacketLineIn in = new PacketLineIn(out);
+ String tmp = in.readString();
+ assertThat(tmp).isEqualTo("ACK");
+ tmp = in.readString();
+
+ // Skip length (4 bytes) + 1 byte
+ // to position the output stream to the raw zip stream
+ byte[] buffer = new byte[5];
+ IO.readFully(out, buffer, 0, 5);
+ Set<String> entryNames = new TreeSet<>();
+ try (ZipArchiveInputStream zip = new ZipArchiveInputStream(out)) {
+ ZipArchiveEntry zipEntry = zip.getNextZipEntry();
+ while (zipEntry != null) {
+ String name = zipEntry.getName();
+ entryNames.add(name);
+ zipEntry = zip.getNextZipEntry();
+ }
+ }
+
+ assertThat(entryNames.size()).isEqualTo(1);
+ assertThat(Iterables.getOnlyElement(entryNames)).isEqualTo(
+ String.format("%s/%s", abbreviated, PushOneCommit.FILE_NAME));
+ }
+
+ private String command(PushOneCommit.Result r, String abbreviated) {
+ String c = "-f=zip "
+ + "-9 "
+ + "--prefix=" + abbreviated + "/ "
+ + r.getCommit().name() + " "
+ + PushOneCommit.FILE_NAME;
+ return c;
+ }
+
+ private void archiveNotPermitted() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String abbreviated = r.getCommitId().abbreviate(8).name();
+ String c = command(r, abbreviated);
+
+ InputStream out =
+ sshSession.exec2("git-upload-archive " + project.get(),
+ argumentsToInputStream(c));
+
+ // Wrap with PacketLineIn to read ACK bytes from output stream
+ PacketLineIn in = new PacketLineIn(out);
+ String tmp = in.readString();
+ assertThat(tmp).isEqualTo("ACK");
+ tmp = in.readString();
+ tmp = in.readString();
+ tmp = tmp.substring(1);
+ assertThat(tmp).isEqualTo("fatal: upload-archive not permitted");
+ }
+
+ private InputStream argumentsToInputStream(String c) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PacketLineOut pctOut = new PacketLineOut(out);
+ for (String arg : Splitter.on(' ').split(c)) {
+ pctOut.writeString("argument " + arg);
+ }
+ pctOut.end();
+ return new ByteArrayInputStream(out.toByteArray());
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
index 25c5321..1a0df84 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -16,6 +16,7 @@
import com.google.common.base.Function;
import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GerritConfig;
@@ -134,8 +135,18 @@
config.setChangeUpdateDelay((int) ConfigUtil.getTimeUnit(
cfg, "change", null, "updateDelay", 30, TimeUnit.SECONDS));
config.setLargeChangeSize(cfg.getInt("change", "largeChange", 500));
+
+ // Zip is not supported because it may be interpreted by a Java plugin as a
+ // valid JAR file, whose code would have access to cookies on the domain.
config.setArchiveFormats(Lists.newArrayList(Iterables.transform(
- archiveFormats.getAllowed(),
+ Iterables.filter(
+ archiveFormats.getAllowed(),
+ new Predicate<ArchiveFormat>() {
+ @Override
+ public boolean apply(ArchiveFormat format) {
+ return (format != ArchiveFormat.ZIP);
+ }
+ }),
new Function<ArchiveFormat, String>() {
@Override
public String apply(ArchiveFormat in) {
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index ec91d49..3a0b31f 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -48,6 +48,7 @@
'//lib/antlr:java_runtime',
'//lib/auto:auto-value',
'//lib/commons:codec',
+ '//lib/commons:compress',
'//lib/commons:dbcp',
'//lib/commons:lang',
'//lib/commons:net',
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java
index a5054f3..14fa7d6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java
@@ -19,14 +19,14 @@
import org.eclipse.jgit.archive.Tbz2Format;
import org.eclipse.jgit.archive.TgzFormat;
import org.eclipse.jgit.archive.TxzFormat;
+import org.eclipse.jgit.archive.ZipFormat;
public enum ArchiveFormat {
TGZ("application/x-gzip", new TgzFormat()),
TAR("application/x-tar", new TarFormat()),
TBZ2("application/x-bzip2", new Tbz2Format()),
- TXZ("application/x-xz", new TxzFormat());
- // Zip is not supported because it may be interpreted by a Java plugin as a
- // valid JAR file, whose code would have access to cookies on the domain.
+ TXZ("application/x-xz", new TxzFormat()),
+ ZIP("application/x-zip", new ZipFormat());
private final ArchiveCommand.Format<?> format;
private final String mimeType;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java
index 913f69e..bebaf52 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -78,6 +79,10 @@
public Set<ArchiveFormat> getAllowed() {
return allowed;
}
+
+ public ImmutableMap<String, ArchiveFormat> getExtensions() {
+ return extensions;
+ }
}
private final GitRepositoryManager repoManager;
@@ -93,8 +98,8 @@
}
@Override
- public BinaryResult apply(RevisionResource rsrc)
- throws BadRequestException, IOException {
+ public BinaryResult apply(RevisionResource rsrc) throws BadRequestException,
+ IOException, MethodNotAllowedException {
if (Strings.isNullOrEmpty(format)) {
throw new BadRequestException("format is not specified");
}
@@ -102,6 +107,9 @@
if (f == null) {
throw new BadRequestException("unknown archive format");
}
+ if (f == ArchiveFormat.ZIP) {
+ throw new MethodNotAllowedException("zip format is disabled");
+ }
boolean close = true;
final Repository repo = repoManager
.openRepository(rsrc.getControl().getProject().getNameKey());
diff --git a/gerrit-sshd/BUCK b/gerrit-sshd/BUCK
index 4774cb3..7bee47e 100644
--- a/gerrit-sshd/BUCK
+++ b/gerrit-sshd/BUCK
@@ -28,6 +28,7 @@
'//lib/mina:core',
'//lib/mina:sshd',
'//lib/jgit:jgit',
+ '//lib/jgit:jgit-archive',
],
provided_deps = [
'//lib/bouncycastle:bcprov',
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
index e4b21d1..2d071c7 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -70,6 +70,8 @@
// Honor the legacy hyphenated forms as aliases for the non-hyphenated forms
command("git-upload-pack").to(Commands.key(git, "upload-pack"));
command(git, "upload-pack").to(Upload.class);
+ command("git-upload-archive").to(Commands.key(git, "upload-archive"));
+ command(git, "upload-archive").to(UploadArchive.class);
command("suexec").to(SuExec.class);
listener().to(ShowCaches.StartupListener.class);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java
new file mode 100644
index 0000000..929f7ea
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java
@@ -0,0 +1,226 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.sshd.commands;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.ArchiveFormat;
+import com.google.gerrit.server.change.GetArchive;
+import com.google.gerrit.sshd.AbstractGitCommand;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.api.ArchiveCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.PacketLineIn;
+import org.eclipse.jgit.transport.PacketLineOut;
+import org.eclipse.jgit.transport.SideBandOutputStream;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.Option;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Allows getting archives for Git repositories over SSH using the Git
+ * upload-archive protocol.
+ */
+public class UploadArchive extends AbstractGitCommand {
+ /**
+ * Options for parsing Git commands.
+ * <p>
+ * These options are not passed on command line, but received through input
+ * stream in pkt-line format.
+ */
+ static class Options {
+ @Option(name = "-f", aliases = {"--format"}, usage = "Format of the"
+ + " resulting archive: tar or zip... If this option is not given, and"
+ + " the output file is specified, the format is inferred from the"
+ + " filename if possible (e.g. writing to \"foo.zip\" makes the output"
+ + " to be in the zip format). Otherwise the output format is tar.")
+ private String format = "tar";
+
+ @Option(name = "--prefix",
+ usage = "Prepend <prefix>/ to each filename in the archive.")
+ private String prefix;
+
+ @Option(name = "-0", usage = "Store the files instead of deflating them.")
+ private boolean level0;
+ @Option(name = "-1")
+ private boolean level1;
+ @Option(name = "-2")
+ private boolean level2;
+ @Option(name = "-3")
+ private boolean level3;
+ @Option(name = "-4")
+ private boolean level4;
+ @Option(name = "-5")
+ private boolean level5;
+ @Option(name = "-6")
+ private boolean level6;
+ @Option(name = "-7")
+ private boolean level7;
+ @Option(name = "-8")
+ private boolean level8;
+ @Option(name = "-9", usage = "Highest and slowest compression level. You "
+ + "can specify any number from 1 to 9 to adjust compression speed and "
+ + "ratio.")
+ private boolean level9;
+
+ @Argument(index = 0, required = true, usage = "The tree or commit to "
+ + "produce an archive for.")
+ private String treeIsh = "master";
+
+ @Argument(index = 1, multiValued = true, usage =
+ "Without an optional path parameter, all files and subdirectories of "
+ + "the current working directory are included in the archive. If one "
+ + "or more paths are specified, only these are included.")
+ private List<String> path;
+ }
+
+ @Inject
+ private GetArchive.AllowedFormats allowedFormats;
+ @Inject
+ private Provider<ReviewDb> db;
+ private Options options = new Options();
+
+ /**
+ * Read and parse arguments from input stream.
+ * This method gets the arguments from input stream, in Pkt-line format,
+ * then parses them to fill the options object.
+ */
+ protected void readArguments() throws IOException, Failure {
+ String argCmd = "argument ";
+ List<String> args = Lists.newArrayList();
+
+ // Read arguments in Pkt-Line format
+ PacketLineIn packetIn = new PacketLineIn(in);
+ for (;;) {
+ String s = packetIn.readString();
+ if (s == PacketLineIn.END) {
+ break;
+ }
+ if (!s.startsWith(argCmd)) {
+ throw new Failure(1, "fatal: 'argument' token or flush expected");
+ }
+ String[] parts = s.substring(argCmd.length()).split("=", 2);
+ for(String p : parts) {
+ args.add(p);
+ }
+ }
+
+ try {
+ // Parse them into the 'options' field
+ CmdLineParser parser = new CmdLineParser(options);
+ parser.parseArgument(args);
+ if (options.path == null || Arrays.asList(".").equals(options.path)) {
+ options.path = Collections.emptyList();
+ }
+ } catch (CmdLineException e) {
+ throw new Failure(2, "fatal: unable to parse arguments, " + e);
+ }
+ }
+
+ @Override
+ protected void runImpl() throws IOException, Failure {
+ PacketLineOut packetOut = new PacketLineOut(out);
+ packetOut.setFlushOnEnd(true);
+ packetOut.writeString("ACK");
+ packetOut.end();
+
+ try {
+ // Parse Git arguments
+ readArguments();
+
+ ArchiveFormat f = allowedFormats.getExtensions().get("." + options.format);
+ if (f == null) {
+ throw new Failure(3, "fatal: upload-archive not permitted");
+ }
+
+ // Find out the object to get from the specified reference and paths
+ ObjectId treeId = repo.resolve(options.treeIsh);
+ if (treeId.equals(ObjectId.zeroId())) {
+ throw new Failure(4, "fatal: reference not found");
+ }
+
+ // Verify the user has permissions to read the specified reference
+ if (!projectControl.allRefsAreVisible() && !canRead(treeId)) {
+ throw new Failure(5, "fatal: cannot perform upload-archive operation");
+ }
+
+ try {
+ // The archive is sent in DATA sideband channel
+ SideBandOutputStream sidebandOut =
+ new SideBandOutputStream(SideBandOutputStream.CH_DATA,
+ SideBandOutputStream.MAX_BUF, out);
+ new ArchiveCommand(repo)
+ .setFormat(f.name())
+ .setFormatOptions(getFormatOptions(f))
+ .setTree(treeId)
+ .setPaths(options.path.toArray(new String[0]))
+ .setPrefix(options.prefix)
+ .setOutputStream(sidebandOut)
+ .call();
+ sidebandOut.flush();
+ sidebandOut.close();
+ } catch (GitAPIException e) {
+ throw new Failure(7, "fatal: git api exception, " + e);
+ }
+ } catch (Failure f) {
+ // Report the error in ERROR sideband channel
+ SideBandOutputStream sidebandError =
+ new SideBandOutputStream(SideBandOutputStream.CH_ERROR,
+ SideBandOutputStream.MAX_BUF, out);
+ sidebandError.write(f.getMessage().getBytes(UTF_8));
+ sidebandError.flush();
+ sidebandError.close();
+ throw f;
+ } finally {
+ // In any case, cleanly close the packetOut channel
+ packetOut.end();
+ }
+ }
+
+ private Map<String, Object> getFormatOptions(ArchiveFormat f) {
+ if (f == ArchiveFormat.ZIP) {
+ int value = Arrays.asList(options.level0, options.level1, options.level2,
+ options.level3, options.level4, options.level5, options.level6,
+ options.level7, options.level8, options.level9).indexOf(true);
+ if (value >= 0) {
+ return ImmutableMap.<String, Object> of(
+ "level", Integer.valueOf(value));
+ }
+ }
+ return Collections.emptyMap();
+ }
+
+ private boolean canRead(ObjectId revId) throws IOException {
+ try (RevWalk rw = new RevWalk(repo)) {
+ RevCommit commit = rw.parseCommit(revId);
+ return projectControl.canReadCommit(db.get(), rw, commit);
+ }
+ }
+}
diff --git a/lib/commons/BUCK b/lib/commons/BUCK
index fe249fa..dfdd131 100644
--- a/lib/commons/BUCK
+++ b/lib/commons/BUCK
@@ -23,7 +23,6 @@
sha1 = 'ab365c96ee9bc88adcc6fa40d185c8e15a31410d',
license = 'Apache2.0',
exclude = ['META-INF/LICENSE.txt', 'META-INF/NOTICE.txt'],
- visibility = ['//lib/jgit:jgit-archive'],
)
maven_jar(