Download commands: Add JGit archive
Since JGit 3.1 archive command was implemented. Add it to download
drop down as new line.
The following libraries are introduced in this change:
* jgit-archive (Apache 2)
* commons-compress (Apache 2)
* tukaani-xz (Public domain)
Change-Id: I5f61aac8c434414c73585a9320e84f4430dd111d
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
index 29be66b..6fc678b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
@@ -40,9 +40,13 @@
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.clippy.client.CopyableLabel;
+import java.util.ArrayList;
import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.List;
class DownloadBox extends VerticalPanel {
+ private final static String ARCHIVE[] = {"tar", "tbz2", "tgz", "txz"};
private final ChangeInfo change;
private final String revision;
private final PatchSet.Id psId;
@@ -109,6 +113,7 @@
}
}
insertPatch();
+ insertArchive();
insertCommand(null, scheme);
}
@@ -141,6 +146,34 @@
insertCommand("Patch-File", p);
}
+ private void insertArchive() {
+ List<Anchor> formats = new ArrayList<>(ARCHIVE.length);
+ for (String f : ARCHIVE) {
+ Anchor archive = new Anchor(f);
+ archive.setHref(new RestApi("/changes/")
+ .id(psId.getParentKey().get())
+ .view("revisions")
+ .id(revision)
+ .view("archive")
+ .addParameter("format", f)
+ .url());
+ formats.add(archive);
+ }
+
+ HorizontalPanel p = new HorizontalPanel();
+ Iterator<Anchor> it = formats.iterator();
+ while (it.hasNext()) {
+ Anchor a = it.next();
+ p.add(a);
+ if (it.hasNext()) {
+ InlineLabel spacer = new InlineLabel("|");
+ spacer.setStyleName(Gerrit.RESOURCES.css().downloadBoxSpacer());
+ p.add(spacer);
+ }
+ }
+ insertCommand("Archive", p);
+ }
+
private void insertCommand(String commandName, Widget w) {
int row = commandTable.getRowCount();
commandTable.insertRow(row);
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index 6ca5493..12dd162 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -57,6 +57,7 @@
'//lib/guice:guice-assistedinject',
'//lib/guice:guice-servlet',
'//lib/jgit:jgit',
+ '//lib/jgit:jgit-archive',
'//lib/joda:joda-time',
'//lib/log:api',
'//lib/prolog:prolog-cafe',
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
new file mode 100644
index 0000000..e7d05df
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java
@@ -0,0 +1,84 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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.server.change;
+
+import com.google.common.collect.Maps;
+
+import org.eclipse.jgit.api.ArchiveCommand;
+import org.eclipse.jgit.archive.TarFormat;
+import org.eclipse.jgit.archive.Tbz2Format;
+import org.eclipse.jgit.archive.TgzFormat;
+import org.eclipse.jgit.archive.TxzFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Map;
+
+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.
+
+ static final Logger log = LoggerFactory.getLogger(ArchiveFormat.class);
+
+ private final ArchiveCommand.Format<?> format;
+ private final String mimeType;
+
+ private ArchiveFormat(String mimeType, ArchiveCommand.Format<?> format) {
+ this.format = format;
+ this.mimeType = mimeType;
+ ArchiveCommand.registerFormat(name(), format);
+ }
+
+ String getShortName() {
+ return name().toLowerCase();
+ }
+
+ String getMimeType() {
+ return mimeType;
+ }
+
+ String getDefaultSuffix() {
+ return getSuffixes().iterator().next();
+ }
+
+ Iterable<String> getSuffixes() {
+ return format.suffixes();
+ }
+
+ static Map<String, ArchiveFormat> init() {
+ String[] formats = new String[values().length];
+ for (int i = 0; i < values().length; i++) {
+ formats[i] = values()[i].name();
+ }
+
+ Map<String, ArchiveFormat> exts = Maps.newLinkedHashMap();
+ for (String name : formats) {
+ try {
+ ArchiveFormat format = valueOf(name.toUpperCase());
+ for (String ext : format.getSuffixes()) {
+ exts.put(ext, format);
+ }
+ } catch (IllegalArgumentException e) {
+ log.warn("Invalid archive.format {}", name);
+ }
+ }
+ return Collections.unmodifiableMap(exts);
+ }
+}
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
new file mode 100644
index 0000000..9a4ab21
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java
@@ -0,0 +1,111 @@
+// 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.server.change;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.BinaryResult;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.api.ArchiveCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.kohsuke.args4j.Option;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+
+class GetArchive implements RestReadView<RevisionResource> {
+ private static final Map<String, ArchiveFormat> formats = ArchiveFormat.init();
+ private final GitRepositoryManager repoManager;
+
+ @Option(name = "--format")
+ private String format;
+
+ @Inject
+ GetArchive(GitRepositoryManager repoManager) {
+ this.repoManager = repoManager;
+ }
+
+ @Override
+ public BinaryResult apply(RevisionResource rsrc)
+ throws BadRequestException, IOException {
+ if (Strings.isNullOrEmpty(format)) {
+ throw new BadRequestException("format is not specified");
+ }
+ final ArchiveFormat f = formats.get("." + format);
+ if (f == null) {
+ throw new BadRequestException("unknown archive format");
+ }
+ boolean close = true;
+ final Repository repo = repoManager
+ .openRepository(rsrc.getControl().getProject().getNameKey());
+ try {
+ final RevWalk rw = new RevWalk(repo);
+ try {
+ final RevCommit commit =
+ rw.parseCommit(ObjectId.fromString(rsrc.getPatchSet()
+ .getRevision().get()));
+ BinaryResult bin = new BinaryResult() {
+ @Override
+ public void writeTo(OutputStream out) throws IOException {
+ try {
+ new ArchiveCommand(repo)
+ .setFormat(f.name())
+ .setTree(commit.getTree())
+ .setOutputStream(out).call();
+ } catch (GitAPIException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ rw.release();
+ repo.close();
+ }
+ };
+
+ bin.disableGzip()
+ .setContentType(f.getMimeType())
+ .setAttachmentName(name(f, rw, commit));
+
+ close = false;
+ return bin;
+ } finally {
+ if (close) {
+ rw.release();
+ }
+ }
+ } finally {
+ if (close) {
+ repo.close();
+ }
+ }
+ }
+
+ private static String name(ArchiveFormat format, RevWalk rw, RevCommit commit)
+ throws IOException {
+ return String.format("%s%s",
+ rw.getObjectReader().abbreviate(commit,7).name(),
+ format.getDefaultSuffix());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index 9ed2bee..6880ca2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -82,6 +82,7 @@
get(REVISION_KIND, "submit_type").to(TestSubmitType.Get.class);
post(REVISION_KIND, "test.submit_rule").to(TestSubmitRule.class);
post(REVISION_KIND, "test.submit_type").to(TestSubmitType.class);
+ get(REVISION_KIND, "archive").to(GetArchive.class);
child(REVISION_KIND, "drafts").to(Drafts.class);
put(REVISION_KIND, "drafts").to(CreateDraft.class);
diff --git a/lib/BUCK b/lib/BUCK
index b7ba064..290ed92 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -20,6 +20,7 @@
define_license(name = 'prologcafe')
define_license(name = 'protobuf')
define_license(name = 'slf4j')
+define_license(name = 'xz')
define_license(name = 'DO_NOT_DISTRIBUTE')
maven_jar(
@@ -248,3 +249,12 @@
visibility = ['//lib:easymock'],
attach_source = False,
)
+
+maven_jar(
+ name = 'tukaani-xz',
+ id = 'org.tukaani:xz:1.4',
+ sha1 = '18a9a2ce6abf32ea1b5fd31dae5210ad93f4e5e3',
+ license = 'xz',
+ attach_source = False,
+ visibility = ['//lib/jgit:jgit-archive'],
+)
diff --git a/lib/LICENSE-xz b/lib/LICENSE-xz
new file mode 100644
index 0000000..420556e
--- /dev/null
+++ b/lib/LICENSE-xz
@@ -0,0 +1,4 @@
+All the files in this package have been written by Lasse Collin
+and/or Igor Pavlov. All these files have been put into the
+public domain. You can do whatever you want with these files.
+This software is provided "as is", without any warranty.
diff --git a/lib/commons/BUCK b/lib/commons/BUCK
index 6f412e4..aed2c68 100644
--- a/lib/commons/BUCK
+++ b/lib/commons/BUCK
@@ -22,6 +22,15 @@
)
maven_jar(
+ name = 'compress',
+ id = 'org.apache.commons:commons-compress:1.7',
+ sha1 = 'ab365c96ee9bc88adcc6fa40d185c8e15a31410d',
+ license = 'Apache2.0',
+ exclude = ['META-INF/LICENSE.txt', 'META-INF/NOTICE.txt'],
+ visibility = ['//lib/jgit:jgit-archive'],
+)
+
+maven_jar(
name = 'dbcp',
id = 'commons-dbcp:commons-dbcp:1.4',
sha1 = '30be73c965cc990b153a100aaaaafcf239f82d39',
diff --git a/lib/jgit/BUCK b/lib/jgit/BUCK
index ee5c4d0..026fa40 100644
--- a/lib/jgit/BUCK
+++ b/lib/jgit/BUCK
@@ -34,6 +34,23 @@
)
maven_jar(
+ name = 'jgit-archive',
+ id = 'org.eclipse.jgit:org.eclipse.jgit.archive:' + VERS,
+ sha1 = 'c645b284344ec9791404f6fd0e04f6dbedb58b7d',
+ license = 'jgit',
+ repository = REPO,
+ deps = [':jgit',
+ '//lib/commons:compress',
+ '//lib:tukaani-xz',
+ ],
+ unsign = True,
+ exclude = [
+ 'about.html',
+ 'plugin.properties',
+ ],
+)
+
+maven_jar(
name = 'junit',
id = 'org.eclipse.jgit:org.eclipse.jgit.junit:' + VERS,
sha1 = '6ba0c7bf8f9577067c4338976385828e3ad774eb',