Merge "Add extension point to register JGit PostReceiveHooks"
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 7593578..2e9c83c 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -1519,7 +1519,7 @@
----
If a GWT plugin wants to invoke the Gerrit REST API it can use
-`com.google.gerrit.plugin.client.rpc.RestApi` to contruct the URL
+`com.google.gerrit.plugin.client.rpc.RestApi` to construct the URL
path and to trigger the REST calls.
Example for invoking a Gerrit core REST endpoint:
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 95d7f26..1409e96 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1299,16 +1299,18 @@
|`warning` |optional|
Warning message for the configuration parameter.
|`type` ||
-The type of the configuration parameter, can be `STRING`, `INT`,
-`LONG`, `BOOLEAN` or `LIST`.
+The type of the configuration parameter. Can be `STRING`, `INT`,
+`LONG`, `BOOLEAN`, `LIST` or `ARRAY`.
|`value` |optional|
The value of the configuration parameter as string. If the parameter
is inheritable this is the effective value which is deduced from
`configured_value` and `inherited_value`.
+|`values` |optional|
+The list of values. Only set if the `type` is `ARRAY`.
`editable` |`false` if not set|
Whether the value is editable.
|`permitted_values`|optional|
-The list of permitted values, only set if the `type` is `LIST`.
+The list of permitted values. Only set if the `type` is `LIST`.
|`inheritable` |`false` if not set|
Whether the configuration parameter can be inherited.
|`configured_value`|optional|
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index b28cbdb..99d910e 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -67,16 +67,16 @@
[[before_until]]
before:'TIME'/until:'TIME'
+
-Changes modified before the given 'TIME', inclusive. With no time,
-assumes 00:00:00 in the local time zone. Supports many formats supported
-by `git log`.
+Changes modified before the given 'TIME', inclusive. Must be in the
+format `2006-01-02[ 15:04:05[.890][ -0700]]`; omitting the time defaults
+to 00:00:00 and omitting the timezone defaults to UTC.
[[after_since]]
after:'TIME'/since:'TIME'
+
-Changes modified before the given 'TIME', inclusive. With no time,
-assumes 00:00:00 in the local time zone. Supports many formats supported
-by `git log`.
+Changes modified after the given 'TIME', inclusive. Must be in the
+format `2006-01-02[ 15:04:05[.890][ -0700]]`; omitting the time defaults
+to 00:00:00 and omitting the timezone defaults to UTC.
[[change]]
change:'ID'::
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 6e6ef37..5c5746b 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -22,6 +22,7 @@
import com.google.common.base.Joiner;
import com.google.common.primitives.Chars;
import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ListChangesOption;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -196,4 +197,10 @@
protected static Gson newGson() {
return OutputFormat.JSON_COMPACT.newGson();
}
+
+ protected RevisionApi revision(PushOneCommit.Result r) throws Exception {
+ return gApi.changes()
+ .id(r.getChangeId())
+ .current();
+ }
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/CustomLabelIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
new file mode 100644
index 0000000..83c9f38
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
@@ -0,0 +1,150 @@
+// 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.acceptance.server.project;
+
+import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
+import static com.google.gerrit.server.project.Util.category;
+import static com.google.gerrit.server.project.Util.grant;
+import static com.google.gerrit.server.project.Util.value;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.LabelInfo;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.inject.Inject;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@NoHttpd
+public class CustomLabelIT extends AbstractDaemonTest {
+
+ @Inject
+ private ProjectCache projectCache;
+
+ @Inject
+ private AllProjectsName allProjects;
+
+ @Inject
+ private MetaDataUpdate.Server metaDataUpdateFactory;
+
+ private final LabelType Q = category("CustomLabel",
+ value(1, "Positive"),
+ value(0, "No score"),
+ value(-1, "Negative"));
+
+ @Before
+ public void setUp() throws Exception {
+ ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
+ AccountGroup.UUID anonymousUsers =
+ SystemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID();
+ grant(cfg, Permission.forLabel(Q.getName()), -1, 1, anonymousUsers,
+ "refs/heads/*");
+ saveProjectConfig(cfg);
+ }
+
+ @Test
+ public void customLabelNoOp_NegativeVoteNotBlock() throws Exception {
+ Q.setFunctionName("NoOp");
+ saveLabelConfig();
+ PushOneCommit.Result r = createChange();
+ revision(r).review(new ReviewInput().label(Q.getName(), -1));
+ ChangeInfo c = get(r.getChangeId());
+ LabelInfo q = c.labels.get(Q.getName());
+ assertEquals(1, q.all.size());
+ assertNull(q.rejected);
+ assertNotNull(q.disliked);
+ }
+
+ @Test
+ public void customLabelNoBlock_NegativeVoteNotBlock() throws Exception {
+ Q.setFunctionName("NoBlock");
+ saveLabelConfig();
+ PushOneCommit.Result r = createChange();
+ revision(r).review(new ReviewInput().label(Q.getName(), -1));
+ ChangeInfo c = get(r.getChangeId());
+ LabelInfo q = c.labels.get(Q.getName());
+ assertEquals(1, q.all.size());
+ assertNull(q.rejected);
+ assertNotNull(q.disliked);
+ }
+
+ @Test
+ public void customLabelMaxNoBlock_NegativeVoteNotBlock() throws Exception {
+ Q.setFunctionName("MaxNoBlock");
+ saveLabelConfig();
+ PushOneCommit.Result r = createChange();
+ revision(r).review(new ReviewInput().label(Q.getName(), -1));
+ ChangeInfo c = get(r.getChangeId());
+ LabelInfo q = c.labels.get(Q.getName());
+ assertEquals(1, q.all.size());
+ assertNull(q.rejected);
+ assertNotNull(q.disliked);
+ }
+
+ @Test
+ public void customLabelAnyWithBlock_NegativeVoteBlock() throws Exception {
+ Q.setFunctionName("AnyWithBlock");
+ saveLabelConfig();
+ PushOneCommit.Result r = createChange();
+ revision(r).review(new ReviewInput().label(Q.getName(), -1));
+ ChangeInfo c = get(r.getChangeId());
+ LabelInfo q = c.labels.get(Q.getName());
+ assertEquals(1, q.all.size());
+ assertNull(q.disliked);
+ assertNotNull(q.rejected);
+ }
+
+ @Test
+ public void customLabelMaxWithBlock_NegativeVoteBlock() throws Exception {
+ saveLabelConfig();
+ PushOneCommit.Result r = createChange();
+ revision(r).review(new ReviewInput().label(Q.getName(), -1));
+ ChangeInfo c = get(r.getChangeId());
+ LabelInfo q = c.labels.get(Q.getName());
+ assertEquals(1, q.all.size());
+ assertNull(q.disliked);
+ assertNotNull(q.rejected);
+ }
+
+ private void saveLabelConfig() throws Exception {
+ ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
+ cfg.getLabelSections().put(Q.getName(), Q);
+ saveProjectConfig(cfg);
+ }
+
+ private void saveProjectConfig(ProjectConfig cfg) throws Exception {
+ MetaDataUpdate md = metaDataUpdateFactory.create(allProjects);
+ try {
+ cfg.commit(md);
+ } finally {
+ md.close();
+ }
+ projectCache.evict(allProjects);
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java
index e94d8f3..a2dd8ec 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java
@@ -15,7 +15,6 @@
package com.google.gerrit.acceptance.server.project;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.gerrit.extensions.common.ListChangesOption.DETAILED_LABELS;
import static org.junit.Assert.assertEquals;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -23,7 +22,6 @@
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.extensions.api.changes.ReviewInput;
-import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.server.config.AllProjectsName;
@@ -235,12 +233,6 @@
}
}
- private RevisionApi revision(PushOneCommit.Result r) throws Exception {
- return gApi.changes()
- .id(r.getChangeId())
- .current();
- }
-
private void merge(PushOneCommit.Result r) throws Exception {
revision(r).review(ReviewInput.approve());
revision(r).submit();
@@ -261,7 +253,7 @@
throws Exception {
// Don't use asserts from PushOneCommit so we can test the round-trip
// through JSON instead of querying the DB directly.
- ChangeInfo c = get(r.getChangeId(), DETAILED_LABELS);
+ ChangeInfo c = get(r.getChangeId());
LabelInfo cr = c.labels.get("Code-Review");
assertEquals(1, cr.all.size());
assertEquals("Administrator", cr.all.get(0).name);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
index 94f8322..b3f0f65 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
@@ -23,6 +23,7 @@
import com.google.gerrit.client.download.DownloadPanel;
import com.google.gerrit.client.projects.ConfigInfo;
import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterInfo;
+import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterValue;
import com.google.gerrit.client.projects.ConfigInfo.InheritedBooleanInfo;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.rpc.CallbackGroup;
@@ -366,17 +367,23 @@
pluginConfig.copyKeysIntoChildren("name");
for (ConfigParameterInfo param : Natives.asList(pluginConfig.values())) {
FocusWidget w;
- if ("STRING".equals(param.type())) {
- w = renderTextBox(g, param, false);
- } else if ("INT".equals(param.type()) || "LONG".equals(param.type())) {
- w = renderTextBox(g, param, true);
- } else if ("BOOLEAN".equals(param.type())) {
- w = renderCheckBox(g, param);
- } else if ("LIST".equals(param.type())
- && param.permittedValues() != null) {
- w = renderListBox(g, param);
- } else {
- continue;
+ switch (param.type()) {
+ case "STRING":
+ case "INT":
+ case "LONG":
+ w = renderTextBox(g, param);
+ break;
+ case "BOOLEAN":
+ w = renderCheckBox(g, param);
+ break;
+ case "LIST":
+ w = renderListBox(g, param);
+ break;
+ case "ARRAY":
+ w = renderTextArea(g, param);
+ break;
+ default:
+ throw new UnsupportedOperationException("unsupported widget type");
}
if (param.editable()) {
widgetMap.put(param.name(), w);
@@ -390,8 +397,10 @@
}
private TextBox renderTextBox(LabeledWidgetsGrid g,
- ConfigParameterInfo param, boolean numbersOnly) {
- NpTextBox textBox = numbersOnly ? new NpIntTextBox() : new NpTextBox();
+ ConfigParameterInfo param) {
+ NpTextBox textBox = param.type().equals("STRING")
+ ? new NpTextBox()
+ : new NpIntTextBox();
if (param.inheritable()) {
textBox.setValue(param.configuredValue());
Label inheritedLabel =
@@ -434,6 +443,9 @@
private ListBox renderListBox(LabeledWidgetsGrid g,
ConfigParameterInfo param) {
+ if (param.permittedValues() == null) {
+ return null;
+ }
ListBox listBox = new ListBox();
if (param.inheritable()) {
listBox.addItem(
@@ -485,6 +497,26 @@
return listBox;
}
+ private NpTextArea renderTextArea(LabeledWidgetsGrid g,
+ ConfigParameterInfo param) {
+ NpTextArea txtArea = new NpTextArea();
+ txtArea.setVisibleLines(4);
+ txtArea.setCharacterWidth(40);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < param.values().length(); i++) {
+ String v = param.values().get(i);
+ sb.append(v).append("\n");
+ }
+ txtArea.setText(sb.toString());
+ if (param.editable()) {
+ saveEnabler.listenTo(txtArea);
+ } else {
+ txtArea.setEnabled(false);
+ }
+ addWidget(g, txtArea, param);
+ return txtArea;
+ }
+
private void addWidget(LabeledWidgetsGrid g, Widget w, ConfigParameterInfo param) {
if (param.description() != null || param.warning() != null) {
HorizontalPanel p = new HorizontalPanel();
@@ -554,26 +586,34 @@
});
}
- private Map<String, Map<String, String>> getPluginConfigValues() {
- Map<String, Map<String, String>> pluginConfigValues =
+ private Map<String, Map<String, ConfigParameterValue>> getPluginConfigValues() {
+ Map<String, Map<String, ConfigParameterValue>> pluginConfigValues =
new HashMap<>(pluginConfigWidgets.size());
for (Entry<String, Map<String, FocusWidget>> e : pluginConfigWidgets.entrySet()) {
- Map<String, String> values =
- new HashMap<String, String>(e.getValue().size());
+ Map<String, ConfigParameterValue> values = new HashMap<>(e.getValue().size());
pluginConfigValues.put(e.getKey(), values);
for (Entry<String, FocusWidget> e2 : e.getValue().entrySet()) {
FocusWidget widget = e2.getValue();
if (widget instanceof TextBox) {
- values.put(e2.getKey(), ((TextBox) widget).getValue().trim());
+ values.put(e2.getKey(), ConfigParameterValue.create()
+ .value(((TextBox) widget).getValue().trim()));
} else if (widget instanceof CheckBox) {
- values.put(e2.getKey(), Boolean.toString(((CheckBox) widget).getValue()));
+ values.put(e2.getKey(), ConfigParameterValue.create()
+ .value(Boolean.toString(((CheckBox) widget).getValue())));
} else if (widget instanceof ListBox) {
ListBox listBox = (ListBox) widget;
// the inherited value is at index 0,
// if it is selected no value should be set on this project
String value = listBox.getSelectedIndex() > 0
? listBox.getValue(listBox.getSelectedIndex()) : null;
- values.put(e2.getKey(), value);
+ values.put(e2.getKey(), ConfigParameterValue.create()
+ .value(value));
+ } else if (widget instanceof NpTextArea) {
+ String text = ((NpTextArea) widget).getText().trim();
+ values.put(e2.getKey(), ConfigParameterValue.create()
+ .values(text.split("\n")));
+ } else {
+ throw new UnsupportedOperationException("unsupported widget type");
}
}
}
@@ -591,11 +631,21 @@
if (allowedCommands.contains(DownloadCommand.CHECKOUT)
|| allowedCommands.contains(DownloadCommand.DEFAULT_DOWNLOADS)) {
commands.add(cmdLinkfactory.new CloneCommandLink());
+ if (Gerrit.getConfig().getSshdAddress() != null && hasUserName()) {
+ commands.add(
+ cmdLinkfactory.new CloneWithCommitMsgHookCommandLink(getProjectKey()));
+ }
}
}
}
}
+ private static boolean hasUserName() {
+ return Gerrit.isSignedIn()
+ && Gerrit.getUserAccount().getUserName() != null
+ && Gerrit.getUserAccount().getUserName().length() > 0;
+ }
+
private class LabeledWidgetsGrid extends FlexTable {
private String labelSuffix;
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-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
index a9a8a93..2fa721e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
@@ -205,8 +205,8 @@
.append(FormatUtil.shortFormatDayTime(c.committer().date()))
.closeTd();
- String an = c.author() != null ? c.author().name() : null;
- String cn = c.committer() != null ? c.committer().name() : null;
+ String an = c.author() != null ? c.author().name() : "";
+ String cn = c.committer() != null ? c.committer().name() : "";
sb.openTd();
sb.append(an);
if (!"".equals(an) && !"".equals(cn) && !an.equals(cn)) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
index 7a477fa..0c0f529 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
@@ -17,9 +17,11 @@
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.clippy.client.CopyableLabel;
@@ -115,6 +117,55 @@
}
}
+ public class CloneWithCommitMsgHookCommandLink extends DownloadCommandLink {
+ private final Project.NameKey project;
+
+ public CloneWithCommitMsgHookCommandLink(Project.NameKey project) {
+ super(DownloadCommand.CHECKOUT, "clone with commmit-msg hook");
+ this.project = project;
+ }
+
+ @Override
+ protected void setCurrentUrl(DownloadUrlLink link) {
+ widget.setVisible(true);
+
+ String sshPort = "29418";
+ String sshAddr = Gerrit.getConfig().getSshdAddress();
+ int p = sshAddr.lastIndexOf(':');
+ if (p != -1 && !sshAddr.endsWith(":")) {
+ sshPort = sshAddr.substring(p + 1);
+ }
+
+ StringBuilder cmd = new StringBuilder();
+ cmd.append("git clone ");
+ cmd.append(link.getUrlData());
+ cmd.append(" && scp -p -P ");
+ cmd.append(sshPort);
+ cmd.append(" ");
+ cmd.append(Gerrit.getUserAccount().getUserName());
+ cmd.append("@");
+
+ if (sshAddr.startsWith("*:") || p == -1) {
+ cmd.append(Window.Location.getHostName());
+ } else {
+ cmd.append(sshAddr.substring(0, p));
+ }
+
+ cmd.append(":hooks/commit-msg ");
+
+ p = project.get().lastIndexOf('/');
+ if (p != -1) {
+ cmd.append(project.get().substring(p + 1));
+ } else {
+ cmd.append(project.get());
+ }
+
+ cmd.append("/.git/hooks/");
+
+ copyLabel.setText(cmd.toString());
+ }
+ }
+
public CopyableCommandLinkFactory(CopyableLabel label, Widget widget) {
copyLabel = label;
this.widget = widget;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
index 09d2e0b..5c88655 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
@@ -158,8 +158,35 @@
public final native String configuredValue() /*-{ return this.configured_value; }-*/;
public final native String inheritedValue() /*-{ return this.inherited_value; }-*/;
public final native JsArrayString permittedValues() /*-{ return this.permitted_values; }-*/;
+ public final native JsArrayString values() /*-{ return this.values; }-*/;
protected ConfigParameterInfo() {
}
}
+
+ public static class ConfigParameterValue extends JavaScriptObject {
+ final native void init() /*-{ this.values = []; }-*/;
+ final native void add_value(String v) /*-{ this.values.push(v); }-*/;
+ final native void set_value(String v) /*-{ if(v)this.value = v; }-*/;
+ public static ConfigParameterValue create() {
+ ConfigParameterValue v = createObject().cast();
+ return v;
+ }
+
+ public final ConfigParameterValue values(String[] values) {
+ init();
+ for (String v : values) {
+ add_value(v);
+ }
+ return this;
+ }
+
+ public final ConfigParameterValue value(String v) {
+ set_value(v);
+ return this;
+ }
+
+ protected ConfigParameterValue() {
+ }
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
index c7d3bc2..63f7e15 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
@@ -14,6 +14,7 @@
package com.google.gerrit.client.projects;
import com.google.gerrit.client.VoidResult;
+import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterValue;
import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.NativeString;
@@ -85,7 +86,7 @@
InheritableBoolean useContentMerge, InheritableBoolean useSignedOffBy,
InheritableBoolean requireChangeId, String maxObjectSizeLimit,
SubmitType submitType, Project.State state,
- Map<String, Map<String, String>> pluginConfigValues,
+ Map<String, Map<String, ConfigParameterValue>> pluginConfigValues,
AsyncCallback<ConfigInfo> cb) {
ConfigInput in = ConfigInput.create();
in.setDescription(description);
@@ -222,32 +223,32 @@
private final native void setStateRaw(String s)
/*-{ if(s)this.state=s; }-*/;
- final void setPluginConfigValues(Map<String, Map<String, String>> pluginConfigValues) {
+ final void setPluginConfigValues(Map<String, Map<String, ConfigParameterValue>> pluginConfigValues) {
if (!pluginConfigValues.isEmpty()) {
- NativeMap<StringMap> configValues = NativeMap.create().cast();
- for (Entry<String, Map<String, String>> e : pluginConfigValues.entrySet()) {
- StringMap values = StringMap.create();
+ NativeMap<ConfigParameterValueMap> configValues = NativeMap.create().cast();
+ for (Entry<String, Map<String, ConfigParameterValue>> e : pluginConfigValues.entrySet()) {
+ ConfigParameterValueMap values = ConfigParameterValueMap.create();
configValues.put(e.getKey(), values);
- for (Entry<String, String> e2 : e.getValue().entrySet()) {
+ for (Entry<String, ConfigParameterValue> e2 : e.getValue().entrySet()) {
values.put(e2.getKey(), e2.getValue());
}
}
setPluginConfigValuesRaw(configValues);
}
}
- private final native void setPluginConfigValuesRaw(NativeMap<StringMap> v)
+ private final native void setPluginConfigValuesRaw(NativeMap<ConfigParameterValueMap> v)
/*-{ this.plugin_config_values=v; }-*/;
}
- private static class StringMap extends JavaScriptObject {
- static StringMap create() {
- return (StringMap) createObject();
+ private static class ConfigParameterValueMap extends JavaScriptObject {
+ static ConfigParameterValueMap create() {
+ return createObject().cast();
}
- protected StringMap() {
+ protected ConfigParameterValueMap() {
}
- public final native void put(String n, String v) /*-{ this[n] = v; }-*/;
+ public final native void put(String n, ConfigParameterValue v) /*-{ this[n] = v; }-*/;
}
private static class BranchInput extends JavaScriptObject {
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
index c0247c6..0e5f9a5 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
@@ -180,28 +180,34 @@
}));
}-*/;
- /** TODO: Break this line after updating GWT */
public final native void on(String event, EventHandler handler) /*-{
this.on(event, $entry(function(cm, e) {
- handler.@net.codemirror.lib.CodeMirror.EventHandler::handle(Lnet/codemirror/lib/CodeMirror;Lcom/google/gwt/dom/client/NativeEvent;)(cm, e);
+ handler.@net.codemirror.lib.CodeMirror.EventHandler::handle(
+ Lnet/codemirror/lib/CodeMirror;Lcom/google/gwt/dom/client/NativeEvent;)(cm, e);
}));
}-*/;
public final native void on(String event, RenderLineHandler handler) /*-{
this.on(event, $entry(function(cm, h, ele) {
- handler.@net.codemirror.lib.CodeMirror.RenderLineHandler::handle(Lnet/codemirror/lib/CodeMirror;Lnet/codemirror/lib/CodeMirror$LineHandle;Lcom/google/gwt/dom/client/Element;)(cm, h, ele);
+ handler.@net.codemirror.lib.CodeMirror.RenderLineHandler::handle(
+ Lnet/codemirror/lib/CodeMirror;Lnet/codemirror/lib/CodeMirror$LineHandle;
+ Lcom/google/gwt/dom/client/Element;)(cm, h, ele);
}));
}-*/;
public final native void on(String event, GutterClickHandler handler) /*-{
this.on(event, $entry(function(cm, l, g, e) {
- handler.@net.codemirror.lib.CodeMirror.GutterClickHandler::handle(Lnet/codemirror/lib/CodeMirror;ILjava/lang/String;Lcom/google/gwt/dom/client/NativeEvent;)(cm, l, g, e);
+ handler.@net.codemirror.lib.CodeMirror.GutterClickHandler::handle(
+ Lnet/codemirror/lib/CodeMirror;ILjava/lang/String;
+ Lcom/google/gwt/dom/client/NativeEvent;)(cm, l, g, e);
}));
}-*/;
public final native void on(String event, BeforeSelectionChangeHandler handler) /*-{
this.on(event, $entry(function(cm, e) {
- handler.@net.codemirror.lib.CodeMirror.BeforeSelectionChangeHandler::handle(Lnet/codemirror/lib/CodeMirror;Lnet/codemirror/lib/LineCharacter;Lnet/codemirror/lib/LineCharacter;)(cm,e.anchor,e.head);
+ handler.@net.codemirror.lib.CodeMirror.BeforeSelectionChangeHandler::handle(
+ Lnet/codemirror/lib/CodeMirror;Lnet/codemirror/lib/LineCharacter;
+ Lnet/codemirror/lib/LineCharacter;)(cm,e.anchor,e.head);
}));
}-*/;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ProtoGen.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ProtoGen.java
index 4512078..12e1e99 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ProtoGen.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ProtoGen.java
@@ -26,6 +26,7 @@
import java.io.BufferedWriter;
import java.io.File;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
@@ -42,9 +43,9 @@
}
try {
JavaSchemaModel jsm = new JavaSchemaModel(ReviewDb.class);
- PrintWriter out = new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(lock.getOutputStream(), "UTF-8")));
- try {
+ try (OutputStream o = lock.getOutputStream();
+ PrintWriter out = new PrintWriter(
+ new BufferedWriter(new OutputStreamWriter(o, "UTF-8")))) {
String header;
InputStream in = getClass().getResourceAsStream("ProtoGenHeader.txt");
try {
@@ -60,8 +61,6 @@
out.write(header.replace("@@VERSION@@", version));
jsm.generateProto(out);
out.flush();
- } finally {
- out.close();
}
if (!lock.commit()) {
throw die("Could not write to " + file);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
index 25c6d09..bab4de7 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
@@ -26,7 +26,7 @@
private long lastTimeMillis;
private String lastTimeString;
- HttpLogLayout() {
+ public HttpLogLayout() {
final TimeZone tz = TimeZone.getDefault();
dateFormat = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z");
dateFormat.setTimeZone(tz);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java
index 85a8ee3..5584cf3 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java
@@ -68,24 +68,25 @@
@SuppressWarnings("resource")
private InitStep loadInitStep(File jar) {
try {
- ClassLoader pluginLoader =
+ URLClassLoader pluginLoader =
new URLClassLoader(new URL[] {jar.toURI().toURL()},
- InitPluginStepsLoader.class.getClassLoader());
- JarFile jarFile = new JarFile(jar);
- Attributes jarFileAttributes = jarFile.getManifest().getMainAttributes();
- String initClassName = jarFileAttributes.getValue("Gerrit-InitStep");
- if (initClassName == null) {
+ InitPluginStepsLoader.class.getClassLoader());
+ try (JarFile jarFile = new JarFile(jar)) {
+ Attributes jarFileAttributes = jarFile.getManifest().getMainAttributes();
+ String initClassName = jarFileAttributes.getValue("Gerrit-InitStep");
+ if (initClassName == null) {
+ return null;
+ }
+ @SuppressWarnings("unchecked")
+ Class<? extends InitStep> initStepClass =
+ (Class<? extends InitStep>) pluginLoader.loadClass(initClassName);
+ return getPluginInjector(jar).getInstance(initStepClass);
+ } catch (ClassCastException e) {
+ ui.message(
+ "WARN: InitStep from plugin %s does not implement %s (Exception: %s)",
+ jar.getName(), InitStep.class.getName(), e.getMessage());
return null;
}
- @SuppressWarnings("unchecked")
- Class<? extends InitStep> initStepClass =
- (Class<? extends InitStep>) pluginLoader.loadClass(initClassName);
- return getPluginInjector(jar).getInstance(initStepClass);
- } catch (ClassCastException e) {
- ui.message(
- "WARN: InitStep from plugin %s does not implement %s (Exception: %s)",
- jar.getName(), InitStep.class.getName(), e.getMessage());
- return null;
} catch (Exception e) {
ui.message(
"WARN: Cannot load and get plugin init step for %s (Exception: %s)",
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitUtil.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitUtil.java
index 0ad7560..dde0b06 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitUtil.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitUtil.java
@@ -162,10 +162,11 @@
static void extract(final File dst, final Class<?> sibling,
final String name) throws IOException {
- final InputStream in = open(sibling, name);
- if (in != null) {
- ByteBuffer buf = IO.readWholeStream(in, 8192);
- copy(dst, buf);
+ try (InputStream in = open(sibling, name)) {
+ if (in != null) {
+ ByteBuffer buf = IO.readWholeStream(in, 8192);
+ copy(dst, buf);
+ }
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
index f4a673c..7209990 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
@@ -106,21 +106,19 @@
}
private static String read(final String p) throws IOException {
- InputStream in = Libraries.class.getClassLoader().getResourceAsStream(p);
- if (in == null) {
- throw new FileNotFoundException("Cannot load resource " + p);
- }
- final Reader r = new InputStreamReader(in, "UTF-8");
- try {
- final StringBuilder buf = new StringBuilder();
- final char[] tmp = new char[512];
- int n;
- while (0 < (n = r.read(tmp))) {
- buf.append(tmp, 0, n);
+ try (InputStream in = Libraries.class.getClassLoader().getResourceAsStream(p)) {
+ if (in == null) {
+ throw new FileNotFoundException("Cannot load resource " + p);
}
- return buf.toString();
- } finally {
- r.close();
+ try (Reader r = new InputStreamReader(in, "UTF-8")) {
+ final StringBuilder buf = new StringBuilder();
+ final char[] tmp = new char[512];
+ int n;
+ while (0 < (n = r.read(tmp))) {
+ buf.append(tmp, 0, n);
+ }
+ return buf.toString();
+ }
}
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java
index dfe28b7..38f08c1 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java
@@ -20,6 +20,7 @@
import org.slf4j.LoggerFactory;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -165,11 +166,14 @@
}
protected void execResource(final String p) {
- InputStream in = JythonShell.class.getClassLoader().getResourceAsStream(p);
- if (in != null) {
- execStream(in, "resource " + p);
- } else {
- log.error("Cannot load resource " + p);
+ try (InputStream in = JythonShell.class.getClassLoader().getResourceAsStream(p)) {
+ if (in != null) {
+ execStream(in, "resource " + p);
+ } else {
+ log.error("Cannot load resource " + p);
+ }
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/IoUtil.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/IoUtil.java
index 1c3d1e3..d9a64fc 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/IoUtil.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/IoUtil.java
@@ -58,16 +58,14 @@
throw noAddURL("Not loaded by URLClassLoader", null);
}
- @SuppressWarnings("resource")
+ @SuppressWarnings("resource") // Leave open so classes can be loaded.
URLClassLoader urlClassLoader = (URLClassLoader) cl;
Method addURL;
try {
addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addURL.setAccessible(true);
- } catch (SecurityException e) {
- throw noAddURL("Method addURL not available", e);
- } catch (NoSuchMethodException e) {
+ } catch (SecurityException | NoSuchMethodException e) {
throw noAddURL("Method addURL not available", e);
}
@@ -78,11 +76,8 @@
if (have.add(url)) {
addURL.invoke(cl, url);
}
- } catch (MalformedURLException e) {
- throw noAddURL("addURL " + path + " failed", e);
- } catch (IllegalArgumentException e) {
- throw noAddURL("addURL " + path + " failed", e);
- } catch (IllegalAccessException e) {
+ } catch (MalformedURLException | IllegalArgumentException |
+ IllegalAccessException e) {
throw noAddURL("addURL " + path + " failed", e);
} catch (InvocationTargetException e) {
throw noAddURL("addURL " + path + " failed", e.getCause());
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java
index b5af54e..aed1b9a 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java
@@ -118,21 +118,15 @@
final File dst = new File(dir, src.getName() + ".gz");
final File tmp = new File(dir, ".tmp." + src.getName());
try {
- final InputStream in = new FileInputStream(src);
- try {
- OutputStream out = new GZIPOutputStream(new FileOutputStream(tmp));
- try {
- final byte[] buf = new byte[2048];
- int n;
- while (0 < (n = in.read(buf))) {
- out.write(buf, 0, n);
- }
- } finally {
- out.close();
+ try (InputStream in = new FileInputStream(src);
+ FileOutputStream fo = new FileOutputStream(tmp);
+ OutputStream out = new GZIPOutputStream(fo)) {
+ final byte[] buf = new byte[2048];
+ int n;
+ while (0 < (n = in.read(buf))) {
+ out.write(buf, 0, n);
}
tmp.setReadOnly();
- } finally {
- in.close();
}
if (!tmp.renameTo(dst)) {
throw new IOException("Cannot rename " + tmp + " to " + dst);
diff --git a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrettyFormatter.java b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrettyFormatter.java
index df3cd0e..d4aef2a 100644
--- a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrettyFormatter.java
+++ b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrettyFormatter.java
@@ -408,7 +408,7 @@
String line = src.get(index) + "\n";
for (int c = 0; c < line.length();) {
- if (charEdits.size() <= lastIdx) {
+ if (charEdits == null || (charEdits.size() <= lastIdx)) {
appendShowBareCR(buf, line.substring(c), false);
break;
}
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index 103092f..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',
@@ -141,12 +142,13 @@
],
)
+QUERY_TESTS = glob(
+ ['src/test/java/com/google/gerrit/server/query/**/*.java'],
+)
+
java_test(
- name = 'server_tests',
- srcs = glob(
- ['src/test/java/**/*.java'],
- excludes = TESTUTIL + PROLOG_TESTS + PROLOG_TEST_CASE
- ),
+ name = 'query_tests',
+ srcs = QUERY_TESTS,
deps = [
':server',
':testutil',
@@ -157,8 +159,6 @@
'//gerrit-extension-api:api',
'//gerrit-reviewdb:server',
'//gerrit-server/src/main/prolog:common',
- '//lib:args4j',
- '//lib:easymock',
'//lib:guava',
'//lib:gwtorm',
'//lib:junit',
@@ -167,8 +167,35 @@
'//lib/jgit:jgit',
'//lib/jgit:junit',
'//lib/joda:joda-time',
+ ],
+ source_under_test = [':server'],
+)
+
+java_test(
+ name = 'server_tests',
+ srcs = glob(
+ ['src/test/java/**/*.java'],
+ excludes = TESTUTIL + PROLOG_TESTS + PROLOG_TEST_CASE + QUERY_TESTS
+ ),
+ deps = [
+ ':server',
+ ':testutil',
+ '//gerrit-antlr:query_exception',
+ '//gerrit-common:annotations',
+ '//gerrit-common:server',
+ '//gerrit-extension-api:api',
+ '//gerrit-reviewdb:server',
+ '//gerrit-server/src/main/prolog:common',
+ '//lib:args4j',
+ '//lib:easymock',
+ '//lib:guava',
+ '//lib:gwtorm',
+ '//lib:junit',
+ '//lib/guice:guice',
+ '//lib/jgit:jgit',
+ '//lib/jgit:junit',
+ '//lib/joda:joda-time',
'//lib/prolog:prolog-cafe',
],
source_under_test = [':server'],
- vm_args = ['-Duser.timezone=US/Eastern'],
)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/Version.java b/gerrit-server/src/main/java/com/google/gerrit/common/Version.java
index 8ad4d48..e69360a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/Version.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/Version.java
@@ -14,12 +14,16 @@
package com.google.gerrit.common;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Version {
+ private static final Logger log = LoggerFactory.getLogger(Version.class);
private static final String version;
public static String getVersion() {
@@ -31,13 +35,11 @@
}
private static String loadVersion() {
- InputStream in = Version.class.getResourceAsStream("Version");
- if (in == null) {
- return null;
- }
- try {
- BufferedReader r = new BufferedReader(new InputStreamReader(in, "UTF-8"));
- try {
+ try (InputStream in = Version.class.getResourceAsStream("Version")) {
+ if (in == null) {
+ return null;
+ }
+ try (BufferedReader r = new BufferedReader(new InputStreamReader(in, "UTF-8"))) {
String vs = r.readLine();
if (vs != null && vs.startsWith("v")) {
vs = vs.substring(1);
@@ -46,10 +48,9 @@
vs = null;
}
return vs;
- } finally {
- r.close();
}
} catch (IOException e) {
+ log.error(e.getMessage(), e);
return null;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
index ab57059..8202ac2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
@@ -235,10 +235,9 @@
mf.getMainAttributes().putValue("Source-Commit", metaConfig.name());
mf.getMainAttributes().putValue("Source-Blob", rulesId.name());
- FileOutputStream stream = new FileOutputStream(tmpjar);
- JarOutputStream out = new JarOutputStream(stream, mf);
- byte buffer[] = new byte[10240];
- try {
+ try (FileOutputStream stream = new FileOutputStream(tmpjar);
+ JarOutputStream out = new JarOutputStream(stream, mf)) {
+ byte buffer[] = new byte[10240];
for (String path : toBeJared) {
JarEntry jarAdd = new JarEntry(path);
File f = new File(tempDir, path);
@@ -260,8 +259,6 @@
}
out.closeEntry();
}
- } finally {
- out.close();
}
if (!tmpjar.renameTo(archiveFile)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/DefaultFileExtensionRegistry.java b/gerrit-server/src/main/java/com/google/gerrit/server/DefaultFileExtensionRegistry.java
index bda15d0..0416ba4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/DefaultFileExtensionRegistry.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/DefaultFileExtensionRegistry.java
@@ -37,6 +37,7 @@
.put(".gitmodules", INI)
.put("project.config", INI)
.put("BUCK", PYTHON)
+ .put("bucklet", newMimeType(PYTHON.toString(), 1))
.put("defs", newMimeType(PYTHON.toString(), 1))
.put("py", newMimeType(PYTHON.toString(), 1))
.put("go", newMimeType("text/x-go", 1))
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/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 6d2dc58..cb852a8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -482,18 +482,12 @@
return;
}
- if (score != 0) {
- if (score == type.getMin().getValue()) {
- label.rejected = accountLoader.get(accountId);
- } else if (score == type.getMax().getValue()) {
- label.approved = accountLoader.get(accountId);
- } else if (score < 0) {
- label.disliked = accountLoader.get(accountId);
- label.value = score;
- } else if (score > 0 && label.disliked == null) {
- label.recommended = accountLoader.get(accountId);
- label.value = score;
- }
+ if (score < 0) {
+ label.disliked = accountLoader.get(accountId);
+ label.value = score;
+ } else if (score > 0 && label.disliked == null) {
+ label.recommended = accountLoader.get(accountId);
+ label.value = score;
}
}
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/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
index dd5101d..b581c67 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -104,6 +104,7 @@
private boolean draft;
private boolean runHooks;
private boolean sendMail;
+ private Account.Id uploader;
@Inject
public PatchSetInserter(ChangeHooks hooks,
@@ -205,6 +206,11 @@
return this;
}
+ public PatchSetInserter setUploader(Account.Id uploader) {
+ this.uploader = uploader;
+ return this;
+ }
+
public Change insert() throws InvalidChangeOperationException, OrmException,
IOException {
init();
@@ -321,6 +327,9 @@
patchSet.setRevision(new RevId(commit.name()));
}
patchSet.setDraft(draft);
+ if (uploader != null) {
+ patchSet.setUploader(uploader);
+ }
}
private void validate() throws InvalidChangeOperationException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
index b0fe0b5..8de6904 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
@@ -299,6 +299,7 @@
.setCopyLabels(true)
.setValidatePolicy(validate)
.setDraft(originalPatchSet.isDraft())
+ .setUploader(uploader.getAccountId())
.setSendMail(sendMail)
.setRunHooks(runHooks);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectConfigEntry.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectConfigEntry.java
index 3bb6389..96fba81 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectConfigEntry.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectConfigEntry.java
@@ -26,6 +26,7 @@
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
+import com.google.inject.ProvisionException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -40,7 +41,7 @@
@ExtensionPoint
public class ProjectConfigEntry {
public enum Type {
- STRING, INT, LONG, BOOLEAN, LIST
+ STRING, INT, LONG, BOOLEAN, LIST, ARRAY
}
private final String displayName;
@@ -145,7 +146,7 @@
}), inheritable, description);
}
- private ProjectConfigEntry(String displayName, String defaultValue,
+ public ProjectConfigEntry(String displayName, String defaultValue,
Type type, List<String> permittedValues, boolean inheritable,
String description) {
this.displayName = displayName;
@@ -154,6 +155,10 @@
this.permittedValues = permittedValues;
this.inheritable = inheritable;
this.description = description;
+ if (type == Type.ARRAY && inheritable) {
+ throw new ProvisionException(
+ "ARRAY doesn't support inheritable values");
+ }
}
public String getDisplayName() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
index 40699c8..8c1fdb6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
@@ -108,16 +108,10 @@
return true;
}
- @SuppressWarnings("resource")
private static PGPPublicKeyRingCollection readPubRing(final File pub) {
- try {
- InputStream in = new FileInputStream(pub);
- try {
- in = PGPUtil.getDecoderStream(in);
+ try (InputStream fin = new FileInputStream(pub);
+ InputStream in = PGPUtil.getDecoderStream(fin)) {
return new PGPPublicKeyRingCollection(in);
- } finally {
- in.close();
- }
} catch (IOException e) {
throw new ProvisionException("Cannot read " + pub, e);
} catch (PGPException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/HttpContactStoreConnection.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/HttpContactStoreConnection.java
index 123c20d..471f6a2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/HttpContactStoreConnection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/HttpContactStoreConnection.java
@@ -48,9 +48,9 @@
"application/x-www-form-urlencoded; charset=UTF-8");
conn.setDoOutput(true);
conn.setFixedLengthStreamingMode(body.length);
- final OutputStream out = conn.getOutputStream();
- out.write(body);
- out.close();
+ try (OutputStream out = conn.getOutputStream()) {
+ out.write(body);
+ }
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("Connection failed: " + conn.getResponseCode());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index ed4f9ab..847eaee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -27,6 +27,7 @@
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
import com.google.common.base.Function;
+import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
@@ -147,6 +148,7 @@
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -891,6 +893,13 @@
projectControl.getProjectState().getConfig()
.getPluginConfig(e.getPluginName())
.getString(e.getExportName());
+ if (configEntry.getType() == ProjectConfigEntry.Type.ARRAY) {
+ List<String> l =
+ Arrays.asList(projectControl.getProjectState()
+ .getConfig().getPluginConfig(e.getPluginName())
+ .getStringList(e.getExportName()));
+ oldValue = Joiner.on("\n").join(l);
+ }
if ((value == null ? oldValue != null : !value.equals(oldValue)) &&
!configEntry.isEditable(projectControl.getProjectState())) {
@@ -945,7 +954,7 @@
}
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
- if (ctl.canCreate(rp.getRevWalk(), obj)) {
+ if (ctl.canCreate(rp.getRevWalk(), obj, allRefs.values().contains(obj))) {
validateNewCommits(ctl, cmd);
batch.addCommand(cmd);
} else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
index 7a6ce7a..a97b3fd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
@@ -99,8 +99,9 @@
args.db, n.notes(), n.getPatchsetId())) {
approvals.add(new PatchSetApproval(newPatchSet.getId(), a));
}
- args.db.patchSetApprovals().insert(approvals);
-
+ // rebaseChange.rebase() may already have copied some approvals,
+ // use upsert, not insert, to avoid constraint violation on database
+ args.db.patchSetApprovals().upsert(approvals);
newMergeTip =
(CodeReviewCommit) args.rw.parseCommit(ObjectId
.fromString(newPatchSet.getRevision().get()));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
index 0ba20e2..9277f6b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
@@ -20,14 +20,10 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
-
-import org.eclipse.jgit.util.GitDateParser;
-import org.joda.time.DateTime;
+import com.google.gwtjsonrpc.common.JavaSqlTimestampHelper;
import java.sql.Timestamp;
-import java.text.ParseException;
import java.util.Date;
-import java.util.Locale;
public abstract class TimestampRangePredicate<I> extends IndexPredicate<I> {
@SuppressWarnings({"deprecation", "unchecked"})
@@ -46,11 +42,11 @@
return (FieldDef<ChangeData, Timestamp>) f;
}
- protected static Date parse(String value) throws QueryParseException {
+ protected static Timestamp parse(String value) throws QueryParseException {
try {
- return GitDateParser.parse(value, DateTime.now().toCalendar(Locale.US));
- } catch (ParseException e) {
- // ParseException's message is specific and helpful, so preserve it.
+ return JavaSqlTimestampHelper.parseTimestamp(value);
+ } catch (IllegalArgumentException e) {
+ // parseTimestamp's errors are specific and helpful, so preserve them.
throw new QueryParseException(e.getMessage(), e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
index 3264a67..a2348f3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -82,6 +82,9 @@
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("JVM lacks UTF-8 encoding", e);
}
+ } else if (!format.isJson()) {
+ throw new IllegalStateException(
+ "Text output requires that a display OutputStream is provided.");
}
Map<String, PluginInfo> output = Maps.newTreeMap();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
index af67f05..bb620a0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.git.TransferConfig;
import com.google.inject.util.Providers;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -153,7 +154,11 @@
p.configuredValue = configuredValue;
p.inheritedValue = getInheritedValue(project, cfgFactory, e);
} else {
- p.value = configuredValue != null ? configuredValue : configEntry.getDefaultValue();
+ if (configEntry.getType() == ProjectConfigEntry.Type.ARRAY) {
+ p.values = Arrays.asList(cfg.getStringList(e.getExportName()));
+ } else {
+ p.value = configuredValue != null ? configuredValue : configEntry.getDefaultValue();
+ }
}
Map<String, ConfigParameterInfo> pc = pluginConfig.get(e.getPluginName());
if (pc == null) {
@@ -204,5 +209,6 @@
public String configuredValue;
public String inheritedValue;
public List<String> permittedValues;
+ public List<String> values;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
index 621809a..8e8c844 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
@@ -127,7 +127,7 @@
}
}
- if (!refControl.canCreate(rw, object)) {
+ if (!refControl.canCreate(rw, object, true)) {
throw new AuthException("Cannot create \"" + ref + "\"");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
index 6fd611f..5f3d2da 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
@@ -37,6 +37,7 @@
import com.google.gerrit.server.group.GroupsCollection;
import com.google.gerrit.server.project.CreateProject.Input;
import com.google.gerrit.server.project.ProjectJson.ProjectInfo;
+import com.google.gerrit.server.project.PutConfig.ConfigValue;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
@@ -65,7 +66,7 @@
public InheritableBoolean useContentMerge;
public InheritableBoolean requireChangeId;
public String maxObjectSizeLimit;
- public Map<String, Map<String, String>> pluginConfigValues;
+ public Map<String, Map<String, ConfigValue>> pluginConfigValues;
}
public static interface Factory {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
index 6338b55..f8399c4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.project;
import com.google.common.base.CharMatcher;
+import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -44,12 +45,17 @@
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class PutConfig implements RestModifyView<ProjectResource, Input> {
private static final Logger log = LoggerFactory.getLogger(PutConfig.class);
-
+ public static class ConfigValue {
+ public String value;
+ public List<String> values;
+ }
public static class Input {
public String description;
public InheritableBoolean useContributorAgreements;
@@ -59,7 +65,7 @@
public String maxObjectSizeLimit;
public SubmitType submitType;
public Project.State state;
- public Map<String, Map<String, String>> pluginConfigValues;
+ public Map<String, Map<String, ConfigValue>> pluginConfigValues;
}
private final MetaDataUpdate.User metaDataUpdateFactory;
@@ -180,12 +186,12 @@
}
private void setPluginConfigValues(ProjectState projectState,
- ProjectConfig projectConfig, Map<String, Map<String, String>> pluginConfigValues)
+ ProjectConfig projectConfig, Map<String, Map<String, ConfigValue>> pluginConfigValues)
throws BadRequestException {
- for (Entry<String, Map<String, String>> e : pluginConfigValues.entrySet()) {
+ for (Entry<String, Map<String, ConfigValue>> e : pluginConfigValues.entrySet()) {
String pluginName = e.getKey();
PluginConfig cfg = projectConfig.getPluginConfig(pluginName);
- for (Entry<String, String> v : e.getValue().entrySet()) {
+ for (Entry<String, ConfigValue> v : e.getValue().entrySet()) {
ProjectConfigEntry projectConfigEntry =
pluginConfigEntries.get(pluginName, v.getKey());
if (projectConfigEntry != null) {
@@ -195,32 +201,41 @@
continue;
}
String oldValue = cfg.getString(v.getKey());
- if (Strings.emptyToNull(v.getValue()) != null) {
- if (!v.getValue().equals(oldValue)) {
+ String value = v.getValue().value;
+ if (projectConfigEntry.getType() == ProjectConfigEntry.Type.ARRAY) {
+ List<String> l = Arrays.asList(cfg.getStringList(v.getKey()));
+ oldValue = Joiner.on("\n").join(l);
+ value = Joiner.on("\n").join(v.getValue().values);
+ }
+ if (Strings.emptyToNull(value) != null) {
+ if (!value.equals(oldValue)) {
validateProjectConfigEntryIsEditable(projectConfigEntry,
projectState, e.getKey(), pluginName);
try {
switch (projectConfigEntry.getType()) {
case BOOLEAN:
- boolean newBooleanValue = Boolean.parseBoolean(v.getValue());
+ boolean newBooleanValue = Boolean.parseBoolean(value);
cfg.setBoolean(v.getKey(), newBooleanValue);
break;
case INT:
- int newIntValue = Integer.parseInt(v.getValue());
+ int newIntValue = Integer.parseInt(value);
cfg.setInt(v.getKey(), newIntValue);
break;
case LONG:
- long newLongValue = Long.parseLong(v.getValue());
+ long newLongValue = Long.parseLong(value);
cfg.setLong(v.getKey(), newLongValue);
break;
case LIST:
- if (!projectConfigEntry.getPermittedValues().contains(v.getValue())) {
+ if (!projectConfigEntry.getPermittedValues().contains(value)) {
throw new BadRequestException(String.format(
"The value '%s' is not permitted for parameter '%s' of plugin '"
- + pluginName + "'", v.getValue(), v.getKey()));
+ + pluginName + "'", value, v.getKey()));
}
case STRING:
- cfg.setString(v.getKey(), v.getValue());
+ cfg.setString(v.getKey(), value);
+ break;
+ case ARRAY:
+ cfg.setStringList(v.getKey(), v.getValue().values);
break;
default:
log.warn(String.format(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index a6ad374..858ff91 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -233,9 +233,10 @@
*
* @param rw revision pool {@code object} was parsed in.
* @param object the object the user will start the reference with.
+ * @param existsOnServer the object exists on server or not.
* @return {@code true} if the user specified can create a new Git ref
*/
- public boolean canCreate(RevWalk rw, RevObject object) {
+ public boolean canCreate(RevWalk rw, RevObject object, boolean existsOnServer) {
if (!canWrite()) {
return false;
}
@@ -253,8 +254,8 @@
if (object instanceof RevCommit) {
return getCurrentUser().getCapabilities().canAdministrateServer()
|| (owner && !isBlocked(Permission.CREATE))
- || (canPerform(Permission.CREATE) && projectControl.canReadCommit(rw,
- (RevCommit) object));
+ || (canPerform(Permission.CREATE) && (!existsOnServer && canUpdate() || projectControl
+ .canReadCommit(rw, (RevCommit) object)));
} else if (object instanceof RevTag) {
final RevTag tag = (RevTag) object;
try {
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg b/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
index 12dcd52..d8f009b 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
@@ -26,7 +26,7 @@
#
add_ChangeId() {
clean_message=`sed -e '
- /^diff --git a\/.*/{
+ /^diff --git .*/{
s///
q
}
@@ -81,7 +81,7 @@
# Skip the line starting with the diff command and everything after it,
# up to the end of the file, assuming it is only patch data.
# If more than one line before the diff was empty, strip all but one.
- /^diff --git a/ {
+ /^diff --git / {
blankLines = 0
while (getline) { }
next
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 295f841..4342684 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -736,20 +736,24 @@
}
@Test
- public void byBeforeAbsolute() throws Exception {
+ public void byBefore() throws Exception {
clockStepMs = MILLISECONDS.convert(30, HOURS);
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, null, null).insert();
Change change2 = newChange(repo, null, null, null, null).insert();
clockStepMs = 0;
- // GitDateParser drops unparsed portions of the string, so be very careful
- // with formats.
assertTrue(query("before:2009-09-29").isEmpty());
assertTrue(query("before:2009-09-30").isEmpty());
assertTrue(query("before:\"2009-09-30 16:59:00 -0400\"").isEmpty());
+ assertTrue(query("before:\"2009-09-30 20:59:00 -0000\"").isEmpty());
+ assertTrue(query("before:\"2009-09-30 20:59:00\"").isEmpty());
assertResultEquals(change1,
- queryOne("before:\"2009-09-30 21:02:00 -0400\""));
+ queryOne("before:\"2009-09-30 17:02:00 -0400\""));
+ assertResultEquals(change1,
+ queryOne("before:\"2009-10-01 21:02:00 -0000\""));
+ assertResultEquals(change1,
+ queryOne("before:\"2009-10-01 21:02:00\""));
assertResultEquals(change1, queryOne("before:2009-10-01"));
List<ChangeInfo> results;
@@ -760,41 +764,18 @@
}
@Test
- public void byBeforeRelative() throws Exception {
+ public void byAfter() throws Exception {
clockStepMs = MILLISECONDS.convert(30, HOURS);
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, null, null).insert();
Change change2 = newChange(repo, null, null, null, null).insert();
clockStepMs = 0;
- assertTrue(query("before:\"3 days ago\"").isEmpty());
- assertResultEquals(change1, queryOne("before:\"2 days ago\""));
-
- List<ChangeInfo> results;
- results = query("before:\"1 day ago\"");
- assertEquals(2, results.size());
- assertResultEquals(change2, results.get(0));
- assertResultEquals(change1, results.get(1));
-
- results = query("before:\"12 hours ago\"");
- assertEquals(2, results.size());
- assertResultEquals(change2, results.get(0));
- assertResultEquals(change1, results.get(1));
- }
-
- @Test
- public void byAfterAbsolute() throws Exception {
- clockStepMs = MILLISECONDS.convert(30, HOURS);
- TestRepository<InMemoryRepository> repo = createProject("repo");
- Change change1 = newChange(repo, null, null, null, null).insert();
- Change change2 = newChange(repo, null, null, null, null).insert();
- clockStepMs = 0;
-
- // GitDateParser drops unparsed portions of the string, so be very careful
- // with formats.
- assertTrue(query("after:2009-10-02").isEmpty());
+ assertTrue(query("after:2009-10-03").isEmpty());
assertResultEquals(change2,
queryOne("after:\"2009-10-01 20:59:59 -0400\""));
+ assertResultEquals(change2,
+ queryOne("after:\"2009-10-01 20:59:59 -0000\""));
assertResultEquals(change2, queryOne("after:2009-10-01"));
List<ChangeInfo> results;
@@ -804,29 +785,6 @@
assertResultEquals(change1, results.get(1));
}
- @Test
- public void byAfterRelative() throws Exception {
- clockStepMs = MILLISECONDS.convert(30, HOURS);
- TestRepository<InMemoryRepository> repo = createProject("repo");
- Change change1 = newChange(repo, null, null, null, null).insert();
- Change change2 = newChange(repo, null, null, null, null).insert();
- clockStepMs = 0;
-
- assertTrue(query("after:\"1 days ago\"").isEmpty());
- assertResultEquals(change2, queryOne("after:\"2 days ago\""));
-
- List<ChangeInfo> results;
- results = query("after:\"3 days ago\"");
- assertEquals(2, results.size());
- assertResultEquals(change2, results.get(0));
- assertResultEquals(change1, results.get(1));
-
- results = query("after:\"72 hours ago\"");
- assertEquals(2, results.size());
- assertResultEquals(change2, results.get(0));
- assertResultEquals(change1, results.get(1));
- }
-
protected ChangeInserter newChange(
TestRepository<InMemoryRepository> repo,
@Nullable RevCommit commit, @Nullable String key, @Nullable Integer owner,
diff --git a/gerrit-sshd/BUCK b/gerrit-sshd/BUCK
index 679072b..4728c31 100644
--- a/gerrit-sshd/BUCK
+++ b/gerrit-sshd/BUCK
@@ -38,3 +38,17 @@
srcs = SRCS,
visibility = ['PUBLIC'],
)
+
+java_test(
+ name = 'sshd_tests',
+ srcs = glob(
+ ['src/test/java/**/*.java'],
+ ),
+ deps = [
+ ':sshd',
+ '//gerrit-server:server',
+ '//lib:guava',
+ '//lib:junit',
+ ],
+ source_under_test = [':sshd'],
+)
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
index f012dac..67d2738 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -14,7 +14,9 @@
package com.google.gerrit.sshd.commands;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
+import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.errors.ProjectCreationFailedException;
@@ -28,6 +30,7 @@
import com.google.gerrit.server.project.CreateProject;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.PutConfig.ConfigValue;
import com.google.gerrit.server.project.SuggestParentCandidates;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
@@ -189,21 +192,29 @@
}
}
- private Map<String, Map<String, String>> parsePluginConfigValues(
+ @VisibleForTesting
+ Map<String, Map<String, ConfigValue>> parsePluginConfigValues(
List<String> pluginConfigValues) throws UnloggedFailure {
- Map<String, Map<String, String>> m = new HashMap<>();
+ Map<String, Map<String, ConfigValue>> m = new HashMap<>();
for (String pluginConfigValue : pluginConfigValues) {
String[] s = pluginConfigValue.split("=");
String[] s2 = s[0].split("\\.");
if (s.length != 2 || s2.length != 2) {
throw new UnloggedFailure(1, "Invalid plugin config value '"
+ pluginConfigValue
- + "', expected format '<plugin-name>.<parameter-name>=<value>'");
+ + "', expected format '<plugin-name>.<parameter-name>=<value>'"
+ + " or '<plugin-name>.<parameter-name>=<value1,value2,...>'");
}
- String value = s[1];
+ ConfigValue value = new ConfigValue();
+ String v = s[1];
+ if (v.contains(",")) {
+ value.values = Lists.newArrayList(Splitter.on(",").split(v));
+ } else {
+ value.value = v;
+ }
String pluginName = s2[0];
String paramName = s2[1];
- Map<String, String> l = m.get(pluginName);
+ Map<String, ConfigValue> l = m.get(pluginName);
if (l == null) {
l = new HashMap<>();
m.put(pluginName, l);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowQueue.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowQueue.java
index 40f7059..77d79b3 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowQueue.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowQueue.java
@@ -16,6 +16,7 @@
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+import com.google.common.base.Objects;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.TaskInfoFactory;
@@ -139,14 +140,17 @@
id(taskInfo.getTaskId()), start, startTime, "",
taskInfo.getTaskString(taskNameWidth)));
} else if (regularUserCanSee) {
- if (remoteName == null) {
- remoteName = projectName.get();
- } else {
- remoteName = remoteName + "/" + projectName;
+ if (projectName != null) {
+ if (remoteName == null) {
+ remoteName = projectName.get();
+ } else {
+ remoteName = remoteName + "/" + projectName.get();
+ }
}
- stdout.print(String.format("%8s %-12s %-4s %s\n", //
- id(taskInfo.getTaskId()), start, startTime, remoteName));
+ stdout.print(String.format("%8s %-12s %-4s %s\n",
+ id(taskInfo.getTaskId()), start, startTime,
+ Objects.firstNonNull(remoteName, "n/a")));
}
}
stdout.print("----------------------------------------------"
diff --git a/gerrit-sshd/src/test/java/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java b/gerrit-sshd/src/test/java/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java
new file mode 100644
index 0000000..a22bae2
--- /dev/null
+++ b/gerrit-sshd/src/test/java/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java
@@ -0,0 +1,56 @@
+// 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+
+import com.google.gerrit.server.project.PutConfig.ConfigValue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class ProjectConfigParamParserTest {
+
+ private CreateProjectCommand cmd;
+ @Before
+ public void setUp() {
+ cmd = new CreateProjectCommand();
+ }
+
+ @Test
+ public void parseSingleValue() throws Exception {
+ String in = "a.b=c";
+ Map<String, Map<String, ConfigValue>> r =
+ cmd.parsePluginConfigValues(Collections.singletonList(in));
+ ConfigValue configValue = r.get("a").get("b");
+ assertEquals("c", configValue.value);
+ assertNull(configValue.values);
+ }
+
+ @Test
+ public void parseMultipleValue() throws Exception {
+ String in = "a.b=c,d,e";
+ Map<String, Map<String, ConfigValue>> r =
+ cmd.parsePluginConfigValues(Collections.singletonList(in));
+ ConfigValue configValue = r.get("a").get("b");
+ assertArrayEquals(new String[] {"c", "d", "e"}, configValue.values.toArray());
+ assertNull(configValue.value);
+ }
+}
diff --git a/lib/BUCK b/lib/BUCK
index 67bf92a..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(
@@ -34,9 +35,9 @@
maven_jar(
name = 'gwtjsonrpc',
- id = 'gwtjsonrpc:gwtjsonrpc:1.4',
- bin_sha1 = '7500577cbf3afc3395ce15a2f5bccc4b02b0d497',
- src_sha1 = '589e8eeb4663ee7ba330275ccfac2d6b2c6f28db',
+ id = 'gwtjsonrpc:gwtjsonrpc:1.5',
+ bin_sha1 = '8995287e2c3c866e826d06993904e2c8d7961e4b',
+ src_sha1 = 'c9461f6c0490f26720e3ff15b5607320eab89d96',
license = 'Apache2.0',
repository = GERRIT,
)
@@ -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',
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index ea8c2ed..cccfb37 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit ea8c2ed60f0459669349d709883c788d4f4d0486
+Subproject commit cccfb378aedc773bd5a82dcb50b1b3d78ce034d0
diff --git a/plugins/replication b/plugins/replication
index f9dc2f1..a6f1995 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit f9dc2f125f7bba4f707904cc7fa2675d3736f2ba
+Subproject commit a6f1995cea9cc885f0a2faa174fbafbab5c9f85c
diff --git a/tools/default.defs b/tools/default.defs
index 0df8c1f..ff4b936 100644
--- a/tools/default.defs
+++ b/tools/default.defs
@@ -152,7 +152,8 @@
mf_src = []
mf_cmd += 'echo "Manifest-Version: 1.0" >$OUT;'
mf_cmd += 'echo "Gerrit-ApiType: %s" >>$OUT;' % type
- mf_cmd += 'echo "Implementation-Version: $v" >>$OUT'
+ mf_cmd += 'echo "Implementation-Version: $v" >>$OUT;'
+ mf_cmd += 'echo "Implementation-Vendor: Gerrit Code Review" >>$OUT'
for line in manifest_entries:
mf_cmd += ';echo "%s" >> $OUT' % line
genrule(