Merge "Centralized comment requests"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 02787a3..2b934b8 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1152,6 +1152,16 @@
+
The default limit is 1024kB.
+[[change.privateByDefault]]change.privateByDefault::
++
+If set to true, every change created will be private by default.
++
+Note that the newly created change will be public if the `is_private` field in
+link:rest-api-changes.html#change-input[ChangeInput] is set to `false` explicitly
+or the `remove-private` link:user-upload.html#private[PushOption] is used in the push.
++
+The default is false.
+
[[changeCleanup]]
=== Section changeCleanup
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 04ed56a..1ac1afb 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -2551,14 +2551,26 @@
Compiled plugins and extensions can be deployed to a running Gerrit
server using the link:cmd-plugin-install.html[plugin install] command.
-Web UI plugins distributed as single `.js` file can be deployed
-without the overhead of JAR packaging, for more information refer to
-link:cmd-plugin-install.html[plugin install] command.
+Web UI plugins distributed as a single `.js` file (or `.html' file for
+Polygerrit) can be deployed without the overhead of JAR packaging. For
+more information refer to link:cmd-plugin-install.html[plugin install]
+command.
-Plugins can also be copied directly into the server's
-directory at `$site_path/plugins/$name.(jar|js)`. The name of
-the JAR file, minus the `.jar` or `.js` extension, will be used as the
-plugin name. Unless disabled, servers periodically scan this
+Plugins can also be copied directly into the server's directory at
+`$site_path/plugins/$name.(jar|js|html)`. For Web UI plugins, the name
+of the file, minus the `.js` or `.html` extension, will be used as the
+plugin name. For JAR plugins, the value of the `Gerrit-PluginName`
+manifest attribute will be used, if provided, otherwise the name of
+the file, minus the `.jar` extension, will be used.
+
+For Web UI plugins, the plugin version is derived from the filename.
+If the filename contains one or more hyphens, the version is taken
+from the portion following the last hyphen. For example if the plugin
+filename is `my-plugin-1.0.js` the version will be `1.0`. For JAR
+plugins, the version is taken from the `Version` attribute in the
+manifest.
+
+Unless disabled, servers periodically scan the `$site_path/plugins`
directory for updated plugins. The time can be adjusted by
link:config-gerrit.html#plugins.checkFrequency[plugins.checkFrequency].
diff --git a/Documentation/rest-api-plugins.txt b/Documentation/rest-api-plugins.txt
index fb489d7..0f687bf 100644
--- a/Documentation/rest-api-plugins.txt
+++ b/Documentation/rest-api-plugins.txt
@@ -110,6 +110,74 @@
}
----
+Prefix(p)::
+Limit the results to those plugins that start with the specified
+prefix.
++
+The match is case sensitive. May not be used together with `m` or `r`.
++
+List all plugins that start with `delete`:
++
+.Request
+----
+ GET /plugins/?p=delete HTTP/1.0
+----
++
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "delete-project": {
+ "id": "delete-project",
+ "index_url": "plugins/delete-project/",
+ "version": "2.9-SNAPSHOT"
+ }
+ }
+----
++
+E.g. this feature can be used by suggestion client UI's to limit results.
+
+Regex(r)::
+Limit the results to those plugins that match the specified regex.
++
+Boundary matchers '^' and '$' are implicit. For example: the regex 'test.*' will
+match any plugins that start with 'test' and regex '.*test' will match any
+project that end with 'test'.
++
+The match is case sensitive. May not be used together with `m` or `p`.
++
+List all plugins that match regex `some.*plugin`:
++
+.Request
+----
+ GET /plugins/?r=some.*plugin HTTP/1.0
+----
++
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "some-plugin": {
+ "id": "some-plugin",
+ "index_url": "plugins/some-plugin/",
+ "version": "2.9-SNAPSHOT"
+ },
+ "some-other-plugin": {
+ "id": "some-other-plugin",
+ "index_url": "plugins/some-other-plugin/",
+ "version": "2.9-SNAPSHOT"
+ }
+ }
+
+----
Skip(S)::
Skip the given number of plugins from the beginning of the list.
@@ -138,6 +206,33 @@
}
----
+Substring(m)::
+Limit the results to those plugins that match the specified substring.
++
+The match is case insensitive. May not be used together with `r` or `p`.
++
+List all plugins that match substring `project`:
++
+.Request
+----
+ GET /plugins/?m=project HTTP/1.0
+----
++
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "delete-project": {
+ "id": "delete-project",
+ "index_url": "plugins/delete-project/",
+ "version": "2.9-SNAPSHOT"
+ }
+ }
+----
[[install-plugin]]
=== Install Plugin
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 1e7f069..3180eb4 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1182,7 +1182,7 @@
]
----
-Skip(s)::
+Skip(S)::
Skip the given number of branches from the beginning of the list.
+
.Request
@@ -1860,7 +1860,7 @@
]
----
-Skip(s)::
+Skip(S)::
Skip the given number of tags from the beginning of the list.
+
.Request
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index 551777a..256be82 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -16,7 +16,6 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.truth.TruthJUnit.assume;
import com.google.auto.value.AutoValue;
import com.google.common.base.MoreObjects;
@@ -208,10 +207,6 @@
*/
public static void init(Description desc, Config baseConfig, Path site) throws Exception {
checkArgument(!desc.memory(), "can't initialize site path for in-memory test: %s", desc);
- assume()
- .withMessage("FUSED mode not yet supported for on-disk sites")
- .that(NoteDbMode.get())
- .isNotEqualTo(NoteDbMode.FUSED);
Config cfg = desc.buildConfig(baseConfig);
Map<String, Config> pluginConfigs = desc.buildPluginConfigs();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
index 61aba93..3e1b2cb 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
+import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -27,7 +28,9 @@
import com.google.gerrit.extensions.api.plugins.Plugins.ListRequest;
import com.google.gerrit.extensions.common.InstallPluginInput;
import com.google.gerrit.extensions.common.PluginInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import java.util.List;
@@ -35,10 +38,16 @@
@NoHttpd
public class PluginIT extends AbstractDaemonTest {
- private static final byte[] JS_PLUGIN_CONTENT =
- "Gerrit.install(function(self){});\n".getBytes(UTF_8);
+ private static final String JS_PLUGIN = "Gerrit.install(function(self){});\n";
+ private static final String HTML_PLUGIN =
+ String.format("<dom-module id=\"test\"><script>%s</script></dom-module>", JS_PLUGIN);
+ private static final RawInput JS_PLUGIN_CONTENT = RawInputUtil.create(JS_PLUGIN.getBytes(UTF_8));
+ private static final RawInput HTML_PLUGIN_CONTENT =
+ RawInputUtil.create(HTML_PLUGIN.getBytes(UTF_8));
+
private static final List<String> PLUGINS =
- ImmutableList.of("plugin-a", "plugin-b", "plugin-c", "plugin-d");
+ ImmutableList.of(
+ "plugin-a.js", "plugin-b.html", "plugin-c.js", "plugin-d.html", "plugin_e.js");
@Test
@GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
@@ -50,12 +59,15 @@
PluginApi api;
// Install all the plugins
InstallPluginInput input = new InstallPluginInput();
- input.raw = RawInputUtil.create(JS_PLUGIN_CONTENT);
for (String plugin : PLUGINS) {
- api = gApi.plugins().install(plugin + ".js", input);
+ input.raw = plugin.endsWith(".js") ? JS_PLUGIN_CONTENT : HTML_PLUGIN_CONTENT;
+ api = gApi.plugins().install(plugin, input);
assertThat(api).isNotNull();
PluginInfo info = api.get();
- assertThat(info.id).isEqualTo(plugin);
+ String name = pluginName(plugin);
+ assertThat(info.id).isEqualTo(name);
+ assertThat(info.version).isEqualTo(pluginVersion(plugin));
+ assertThat(info.indexUrl).isEqualTo(String.format("plugins/%s/", name));
assertThat(info.disabled).isNull();
}
assertPlugins(list().get(), PLUGINS);
@@ -63,6 +75,24 @@
// With pagination
assertPlugins(list().start(1).limit(2).get(), PLUGINS.subList(1, 3));
+ // With prefix
+ assertPlugins(list().prefix("plugin-b").get(), ImmutableList.of("plugin-b.html"));
+ assertPlugins(list().prefix("PLUGIN-").get(), ImmutableList.of());
+
+ // With substring
+ assertPlugins(list().substring("lugin-").get(), PLUGINS.subList(0, PLUGINS.size() - 1));
+ assertPlugins(list().substring("lugin-").start(1).limit(2).get(), PLUGINS.subList(1, 3));
+
+ // With regex
+ assertPlugins(list().regex(".*in-b").get(), ImmutableList.of("plugin-b.html"));
+ assertPlugins(list().regex("plugin-.*").get(), PLUGINS.subList(0, PLUGINS.size() - 1));
+ assertPlugins(list().regex("plugin-.*").start(1).limit(2).get(), PLUGINS.subList(1, 3));
+
+ // Invalid match combinations
+ assertBadRequest(list().regex(".*in-b").substring("a"));
+ assertBadRequest(list().regex(".*in-b").prefix("a"));
+ assertBadRequest(list().substring(".*in-b").prefix("a"));
+
// Disable
api = gApi.plugins().name("plugin-a");
api.disable();
@@ -97,6 +127,28 @@
private void assertPlugins(List<PluginInfo> actual, List<String> expected) {
List<String> _actual = actual.stream().map(p -> p.id).collect(toList());
- assertThat(_actual).containsExactlyElementsIn(expected);
+ List<String> _expected = expected.stream().map(p -> pluginName(p)).collect(toList());
+ assertThat(_actual).containsExactlyElementsIn(_expected);
+ }
+
+ private String pluginName(String plugin) {
+ int dot = plugin.indexOf(".");
+ assertThat(dot).isGreaterThan(0);
+ return plugin.substring(0, dot);
+ }
+
+ private String pluginVersion(String plugin) {
+ String name = pluginName(plugin);
+ int dash = name.lastIndexOf("-");
+ return dash > 0 ? name.substring(dash + 1) : "";
+ }
+
+ private void assertBadRequest(ListRequest req) throws Exception {
+ try {
+ req.get();
+ fail("Expected BadRequestException");
+ } catch (BadRequestException e) {
+ // Expected
+ }
}
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
index d64d67f..8e3aeaf 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
@@ -748,7 +748,7 @@
@Test
public void retrySubmitAfterTornTopicOnLockFailure() throws Exception {
- assume().that(notesMigration.fuseUpdates()).isTrue();
+ assume().that(notesMigration.disableChangeReviewDb()).isTrue();
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> sub1 = createProjectWithPush("sub1");
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
index 29a9a1f..281e6cd 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
@@ -105,7 +105,7 @@
setUpOneChange();
migrate("--trial", "false");
- assertNotesMigrationState(NotesMigrationState.NOTE_DB_UNFUSED);
+ assertNotesMigrationState(NotesMigrationState.NOTE_DB);
try (ServerContext ctx = startServer()) {
GitRepositoryManager repoManager = ctx.getInjector().getInstance(GitRepositoryManager.class);
@@ -143,7 +143,7 @@
assertServerStartupFails();
migrate("--trial", "false");
- assertNotesMigrationState(NotesMigrationState.NOTE_DB_UNFUSED);
+ assertNotesMigrationState(NotesMigrationState.NOTE_DB);
status = new GerritIndexStatus(sitePaths);
assertThat(status.getReady(ChangeSchemaDefinitions.NAME, version)).isTrue();
@@ -175,7 +175,7 @@
u.runUpgrades();
assertThat(indexes.getSearchIndex().getSchema().getVersion()).isEqualTo(currVersion);
- assertNotesMigrationState(NotesMigrationState.NOTE_DB_UNFUSED);
+ assertNotesMigrationState(NotesMigrationState.NOTE_DB);
}
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 77b70d3..6cbc532 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -916,7 +916,7 @@
@Test
public void retrySubmitSingleChangeOnLockFailure() throws Exception {
- assume().that(notesMigration.fuseUpdates()).isTrue();
+ assume().that(notesMigration.disableChangeReviewDb()).isTrue();
PushOneCommit.Result change = createChange();
String id = change.getChangeId();
@@ -943,7 +943,7 @@
@Test
public void retrySubmitAfterTornTopicOnLockFailure() throws Exception {
- assume().that(notesMigration.fuseUpdates()).isTrue();
+ assume().that(notesMigration.disableChangeReviewDb()).isTrue();
assume().that(isSubmitWholeTopicEnabled()).isTrue();
String topic = "test-topic";
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
new file mode 100644
index 0000000..b880152
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2017 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.rest.change;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.ChangeInput;
+import org.junit.Test;
+
+public class PrivateByDefaultIT extends AbstractDaemonTest {
+ @Test
+ @GerritConfig(name = "change.privateByDefault", value = "true")
+ public void createChangeWithPrivateByDefaultEnabled() throws Exception {
+ ChangeInput input = new ChangeInput(project.get(), "master", "empty change");
+ assertThat(gApi.changes().create(input).get().isPrivate).isEqualTo(true);
+ }
+
+ @Test
+ @GerritConfig(name = "change.privateByDefault", value = "true")
+ public void createChangeBypassPrivateByDefaultEnabled() throws Exception {
+ ChangeInput input = new ChangeInput(project.get(), "master", "empty change");
+ input.isPrivate = false;
+ assertThat(gApi.changes().create(input).get().isPrivate).isNull();
+ }
+
+ @Test
+ public void createChangeWithPrivateByDefaultDisabled() throws Exception {
+ ChangeInfo info =
+ gApi.changes().create(new ChangeInput(project.get(), "master", "empty change")).get();
+ assertThat(info.isPrivate).isNull();
+ }
+
+ @Test
+ @GerritConfig(name = "change.privateByDefault", value = "true")
+ public void pushWithPrivateByDefaultEnabled() throws Exception {
+ assertThat(createChange().getChange().change().isPrivate()).isEqualTo(true);
+ }
+
+ @Test
+ @GerritConfig(name = "change.privateByDefault", value = "true")
+ public void pushBypassPrivateByDefaultEnabled() throws Exception {
+ assertThat(createChange("refs/for/master%remove-private").getChange().change().isPrivate())
+ .isEqualTo(false);
+ }
+
+ @Test
+ public void pushWithPrivateByDefaultDisabled() throws Exception {
+ assertThat(createChange().getChange().change().isPrivate()).isEqualTo(false);
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java
index 3f675ef..41f7d4a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java
@@ -15,11 +15,13 @@
package com.google.gerrit.acceptance.rest.config;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.config.ListCaches.CacheInfo;
import org.junit.Test;
@@ -27,15 +29,20 @@
@Test
public void flushCache() throws Exception {
- RestResponse r = adminRestSession.get("/config/server/caches/groups");
+ AccountGroup group = groupCache.get(new AccountGroup.NameKey("Administrators"));
+ assertWithMessage("Precondition: The group 'Administrators' was loaded by the group cache")
+ .that(group)
+ .isNotNull();
+
+ RestResponse r = adminRestSession.get("/config/server/caches/groups_byname");
CacheInfo result = newGson().fromJson(r.getReader(), CacheInfo.class);
assertThat(result.entries.mem).isGreaterThan((long) 0);
- r = adminRestSession.post("/config/server/caches/groups/flush");
+ r = adminRestSession.post("/config/server/caches/groups_byname/flush");
r.assertOK();
r.consume();
- r = adminRestSession.get("/config/server/caches/groups");
+ r = adminRestSession.get("/config/server/caches/groups_byname");
result = newGson().fromJson(r.getReader(), CacheInfo.class);
assertThat(result.entries.mem).isNull();
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
index 9efbe36..2cd1800 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
@@ -28,7 +28,6 @@
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateListener;
@@ -77,7 +76,7 @@
@Test
public void updateChangeFailureRollsBackRefUpdate() throws Exception {
- assume().that(notesMigration.fuseUpdates()).isTrue();
+ assume().that(notesMigration.disableChangeReviewDb()).isTrue();
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
@@ -149,7 +148,7 @@
@Test
public void retryOnLockFailureWithAtomicUpdates() throws Exception {
- assume().that(notesMigration.fuseUpdates()).isTrue();
+ assume().that(notesMigration.disableChangeReviewDb()).isTrue();
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
String master = "refs/heads/master";
@@ -195,52 +194,6 @@
}
}
- @Test
- public void noRetryOnLockFailureWithoutAtomicUpdates() throws Exception {
- assume().that(notesMigration.fuseUpdates()).isFalse();
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- String master = "refs/heads/master";
- ObjectId initial;
- try (Repository repo = repoManager.openRepository(project)) {
- initial = repo.exactRef(master).getObjectId();
- }
-
- AtomicInteger updateRepoCalledCount = new AtomicInteger();
- AtomicInteger updateChangeCalledCount = new AtomicInteger();
- AtomicInteger afterUpdateReposCalledCount = new AtomicInteger();
-
- try {
- retryHelper.execute(
- batchUpdateFactory -> {
- try (BatchUpdate bu = newBatchUpdate(batchUpdateFactory)) {
- bu.addOp(
- id, new UpdateRefAndAddMessageOp(updateRepoCalledCount, updateChangeCalledCount));
- bu.execute(new ConcurrentWritingListener(afterUpdateReposCalledCount));
- }
- return null;
- });
- assert_().fail("expected RestApiException");
- } catch (RestApiException e) {
- // Expected.
- }
-
- assertThat(updateRepoCalledCount.get()).isEqualTo(1);
- assertThat(afterUpdateReposCalledCount.get()).isEqualTo(1);
- assertThat(updateChangeCalledCount.get()).isEqualTo(0);
-
- // updateChange was never called, so no message was ever added.
- assertThat(getMessages(id)).doesNotContain(UpdateRefAndAddMessageOp.CHANGE_MESSAGE);
-
- try (Repository repo = repoManager.openRepository(project)) {
- // Op lost the race, so the other writer's commit happened first. Op didn't retry, because the
- // ref updates weren't atomic, so it didn't throw LockFailureException on failure.
- assertThat(commitMessages(repo, initial, repo.exactRef(master).getObjectId()))
- .containsExactly(ConcurrentWritingListener.MSG_PREFIX + "1");
- }
- }
-
private class ConcurrentWritingListener implements BatchUpdateListener {
static final String MSG_PREFIX = "Other writer ";
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
index 485bb1b..f08a16e 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
@@ -17,7 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
-import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB_UNFUSED;
+import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB;
import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_NO_SEQUENCE;
import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY;
import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY;
@@ -313,7 +313,7 @@
Change.Id id2 = r2.getChange().getId();
migrate(b -> b.setThreads(threads));
- assertNotesMigrationState(NOTE_DB_UNFUSED);
+ assertNotesMigrationState(NOTE_DB);
assertThat(sequences.nextChangeId()).isEqualTo(503);
@@ -372,7 +372,7 @@
assertThat(NoteDbMigrator.getAutoMigrate(gerritConfig)).isTrue();
migrate(b -> b);
- assertNotesMigrationState(NOTE_DB_UNFUSED);
+ assertNotesMigrationState(NOTE_DB);
assertThat(NoteDbMigrator.getAutoMigrate(gerritConfig)).isFalse();
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
index cf4cfcd..774e4ed 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
@@ -14,27 +14,24 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupById;
import com.google.gerrit.reviewdb.client.AccountGroupMember;
import java.util.List;
public class GroupDetail {
- public AccountGroup group;
- public List<AccountGroupMember> members;
- public List<AccountGroupById> includes;
+ private List<AccountGroupMember> members;
+ private List<AccountGroupById> includes;
- public GroupDetail() {}
-
- public void setGroup(AccountGroup g) {
- group = g;
+ public GroupDetail(List<AccountGroupMember> members, List<AccountGroupById> includes) {
+ this.members = members;
+ this.includes = includes;
}
- public void setMembers(List<AccountGroupMember> m) {
- members = m;
+ public List<AccountGroupMember> getMembers() {
+ return members;
}
- public void setIncludes(List<AccountGroupById> i) {
- includes = i;
+ public List<AccountGroupById> getIncludes() {
+ return includes;
}
}
diff --git a/gerrit-elasticsearch/BUILD b/gerrit-elasticsearch/BUILD
index 0affa12..ccaee55 100644
--- a/gerrit-elasticsearch/BUILD
+++ b/gerrit-elasticsearch/BUILD
@@ -5,7 +5,6 @@
deps = [
"//gerrit-antlr:query_exception",
"//gerrit-extension-api:api",
- "//gerrit-reviewdb:client",
"//gerrit-reviewdb:server",
"//gerrit-server:server",
"//lib:gson",
@@ -36,7 +35,7 @@
deps = [
":elasticsearch",
"//gerrit-extension-api:api",
- "//gerrit-reviewdb:client",
+ "//gerrit-reviewdb:server",
"//gerrit-server:server",
"//lib:gson",
"//lib:guava",
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/plugins/Plugins.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/plugins/Plugins.java
index ed0d7f6..2828db5 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/plugins/Plugins.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/plugins/Plugins.java
@@ -35,6 +35,9 @@
private boolean all;
private int limit;
private int start;
+ private String substring;
+ private String prefix;
+ private String regex;
public List<PluginInfo> get() throws RestApiException {
Map<String, PluginInfo> map = getAsMap();
@@ -73,6 +76,33 @@
public int getStart() {
return start;
}
+
+ public ListRequest substring(String substring) {
+ this.substring = substring;
+ return this;
+ }
+
+ public String getSubstring() {
+ return substring;
+ }
+
+ public ListRequest prefix(String prefix) {
+ this.prefix = prefix;
+ return this;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public ListRequest regex(String regex) {
+ this.regex = regex;
+ return this;
+ }
+
+ public String getRegex() {
+ return regex;
+ }
}
/**
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
index 8ab5fbd..19d503f 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
@@ -338,6 +338,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
break;
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
index 779d5d4..baf5a58 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
@@ -88,6 +88,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new ResourceConflictException("Failed to delete public key: " + saveResult);
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 41fcd04..7d1aceed 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -236,6 +236,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
// TODO(dborowitz): Backoff and retry on LOCK_FAILURE.
throw new ResourceConflictException("Failed to save public keys: " + saveResult);
diff --git a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
index 39e2cb4..04ed1de 100644
--- a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
@@ -323,6 +323,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new AssertionError(result);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
index b0b1e35..e4f5e576 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
@@ -43,6 +43,7 @@
"description",
"followup",
"hashtags",
+ "move",
"publish",
"rebase",
"restore",
@@ -58,6 +59,7 @@
private static final Binder uiBinder = GWT.create(Binder.class);
@UiField Button cherrypick;
+ @UiField Button move;
@UiField Button rebase;
@UiField Button revert;
@UiField Button submit;
@@ -124,6 +126,7 @@
if (hasUser) {
a2b(actions, "abandon", abandon);
a2b(actions, "/", deleteChange);
+ a2b(actions, "move", move);
a2b(actions, "restore", restore);
a2b(actions, "revert", revert);
a2b(actions, "followup", followUp);
@@ -236,6 +239,11 @@
CherryPickAction.call(cherrypick, changeInfo, revision, project, message);
}
+ @UiHandler("move")
+ void onMove(@SuppressWarnings("unused") ClickEvent e) {
+ MoveAction.call(move, changeInfo, project);
+ }
+
@UiHandler("revert")
void onRevert(@SuppressWarnings("unused") ClickEvent e) {
RevertAction.call(revert, changeId, project, revision, subject);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
index 60efc8c..8aeba90 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
@@ -63,6 +63,9 @@
<g:Button ui:field='cherrypick' styleName='' visible='false'>
<div><ui:msg>Cherry Pick</ui:msg></div>
</g:Button>
+ <g:Button ui:field='move' styleName='' visible='false'>
+ <div><ui:msg>Move Change</ui:msg></div>
+ </g:Button>
<g:Button ui:field='rebase' styleName='' visible='false'>
<div><ui:msg>Rebase</ui:msg></div>
</g:Button>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/MoveAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/MoveAction.java
new file mode 100644
index 0000000..e3e9525
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/MoveAction.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2017 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.client.change;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.changes.ChangeApi;
+import com.google.gerrit.client.changes.Util;
+import com.google.gerrit.client.info.ChangeInfo;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.MoveDialog;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.PopupPanel;
+
+class MoveAction {
+ static void call(Button b, ChangeInfo info, Project.NameKey project) {
+ b.setEnabled(false);
+ new MoveDialog(project) {
+ {
+ sendButton.setText(Util.C.moveChangeSend());
+ }
+
+ @Override
+ public void onSend() {
+ ChangeApi.move(
+ info.project(),
+ info.legacyId().get(),
+ getDestinationBranch(),
+ getMessageText(),
+ new GerritCallback<ChangeInfo>() {
+ @Override
+ public void onSuccess(ChangeInfo result) {
+ sent = true;
+ hide();
+ Gerrit.display(PageLinks.toChange(project, result.legacyId()));
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ enableButtons(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ @Override
+ public void onClose(CloseEvent<PopupPanel> event) {
+ super.onClose(event);
+ b.setEnabled(true);
+ }
+ }.center();
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index b1f7561..c353e6f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -228,6 +228,15 @@
call(project, id, commit, "cherrypick").post(cherryPickInput, cb);
}
+ /** Move change to another branch. */
+ public static void move(
+ String project, int id, String destination, String message, AsyncCallback<ChangeInfo> cb) {
+ MoveInput moveInput = MoveInput.create();
+ moveInput.setMessage(message);
+ moveInput.setDestinationBranch(destination);
+ change(project, id).view("move").post(moveInput, cb);
+ }
+
/** Edit commit message for specific revision of a change. */
public static void message(
@Nullable String project,
@@ -356,6 +365,18 @@
protected CherryPickInput() {}
}
+ private static class MoveInput extends JavaScriptObject {
+ static MoveInput create() {
+ return (MoveInput) createObject();
+ }
+
+ final native void setDestinationBranch(String d) /*-{ this.destination_branch = d; }-*/;
+
+ final native void setMessage(String m) /*-{ this.message = m; }-*/;
+
+ protected MoveInput() {}
+ }
+
private static class PrivateInput extends JavaScriptObject {
static PrivateInput create() {
return (PrivateInput) createObject();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
index 80049df..402179c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
@@ -145,6 +145,14 @@
String cherryPickTitle();
+ String moveChangeSend();
+
+ String headingMoveBranch();
+
+ String moveChangeMessage();
+
+ String moveTitle();
+
String buttonRebaseChangeSend();
String rebaseConfirmMessage();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
index 8a9f323..dd11a60 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
@@ -78,6 +78,11 @@
cherryPickCommitMessage = Cherry Pick Commit Message:
cherryPickTitle = Code Review - Cherry Pick Change to Another Branch
+headingMoveBranch = Move Change to Branch:
+moveChangeSend = Move Change
+moveChangeMessage = Move Change Message:
+moveTitle = Code Review - Move Change to Another Branch
+
buttonRebaseChangeSend = Rebase
rebaseConfirmMessage = Change parent revision
rebaseNotPossibleMessage = Change is already up to date
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 181fd73..73cc995 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
@@ -49,7 +49,7 @@
Project.NameKey name, String viewName, int limit, int start, String match) {
RestApi call = project(name).view(viewName);
call.addParameter("n", limit);
- call.addParameter("s", start);
+ call.addParameter("S", start);
if (match != null) {
if (match.startsWith("^")) {
call.addParameter("r", match);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MoveDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MoveDialog.java
new file mode 100644
index 0000000..3821e93
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MoveDialog.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2017 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.client.ui;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.changes.Util;
+import com.google.gerrit.client.projects.BranchInfo;
+import com.google.gerrit.client.projects.ProjectApi;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class MoveDialog extends TextAreaActionDialog {
+ private SuggestBox newBranch;
+ private List<BranchInfo> branches;
+
+ public MoveDialog(Project.NameKey project) {
+ super(Util.C.moveTitle(), Util.C.moveChangeMessage());
+ ProjectApi.getBranches(
+ project,
+ new GerritCallback<JsArray<BranchInfo>>() {
+ @Override
+ public void onSuccess(JsArray<BranchInfo> result) {
+ branches = Natives.asList(result);
+ }
+ });
+
+ newBranch =
+ new SuggestBox(
+ new HighlightSuggestOracle() {
+ @Override
+ protected void onRequestSuggestions(Request request, Callback done) {
+ List<BranchSuggestion> suggestions = new ArrayList<>();
+ for (BranchInfo b : branches) {
+ if (b.ref().contains(request.getQuery())) {
+ suggestions.add(new BranchSuggestion(b));
+ }
+ }
+ done.onSuggestionsReady(request, new Response(suggestions));
+ }
+ });
+
+ newBranch.setWidth("100%");
+ newBranch.getElement().getStyle().setProperty("boxSizing", "border-box");
+ message.setCharacterWidth(70);
+
+ FlowPanel mwrap = new FlowPanel();
+ mwrap.setStyleName(Gerrit.RESOURCES.css().commentedActionMessage());
+ mwrap.add(newBranch);
+
+ panel.insert(mwrap, 0);
+ panel.insert(new SmallHeading(Util.C.headingMoveBranch()), 0);
+ }
+
+ @Override
+ public void center() {
+ super.center();
+ GlobalKey.dialog(this);
+ newBranch.setFocus(true);
+ }
+
+ public String getDestinationBranch() {
+ return newBranch.getText();
+ }
+
+ static class BranchSuggestion implements Suggestion {
+ private BranchInfo branch;
+
+ BranchSuggestion(BranchInfo branch) {
+ this.branch = branch;
+ }
+
+ @Override
+ public String getDisplayString() {
+ String refsHeads = "refs/heads/";
+ if (branch.ref().startsWith(refsHeads)) {
+ return branch.ref().substring(refsHeads.length());
+ }
+ return branch.ref();
+ }
+
+ @Override
+ public String getReplacementString() {
+ return branch.getShortName();
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/QueryDocumentationFilter.java
similarity index 98%
rename from gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java
rename to gerrit-httpd/src/main/java/com/google/gerrit/httpd/QueryDocumentationFilter.java
index 1604997..7a89b3b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/QueryDocumentationFilter.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.httpd.rpc.doc;
+package com.google.gerrit.httpd;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableListMultimap;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
index e705fbd..1d116b7 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
@@ -30,7 +30,6 @@
import com.google.gerrit.httpd.restapi.ConfigRestApiServlet;
import com.google.gerrit.httpd.restapi.GroupsRestApiServlet;
import com.google.gerrit.httpd.restapi.ProjectsRestApiServlet;
-import com.google.gerrit.httpd.rpc.doc.QueryDocumentationFilter;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AuthConfig;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java
index fd825b8..c34b423 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java
@@ -128,6 +128,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new IOException(
"Failed to update " + getRefName() + " of " + project + ": " + r.name());
diff --git a/gerrit-plugin-api/BUILD b/gerrit-plugin-api/BUILD
index cc01802..d8d6838 100644
--- a/gerrit-plugin-api/BUILD
+++ b/gerrit-plugin-api/BUILD
@@ -20,6 +20,7 @@
"//gerrit-gwtexpui:server",
"//gerrit-reviewdb:server",
"//gerrit-server:prolog-common",
+ "//lib/commons:dbcp",
"//lib/commons:lang",
"//lib/commons:lang3",
"//lib/dropwizard:dropwizard-core",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeFinder.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeFinder.java
index ff7a5ce..731d156 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeFinder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeFinder.java
@@ -15,12 +15,14 @@
package com.google.gerrit.server;
import com.google.common.base.Throwables;
+import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.change.ChangeTriplet;
import com.google.gerrit.server.index.IndexConfig;
import com.google.gerrit.server.project.ChangeControl;
@@ -29,8 +31,10 @@
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import com.google.inject.name.Named;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -40,8 +44,19 @@
@Singleton
public class ChangeFinder {
+ private static final String CACHE_NAME = "changeid_project";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ cache(CACHE_NAME, Change.Id.class, String.class).maximumWeight(1024);
+ }
+ };
+ }
private final IndexConfig indexConfig;
+ private final Cache<Change.Id, String> changeIdProjectCache;
private final Provider<InternalChangeQuery> queryProvider;
private final Provider<ReviewDb> reviewDb;
private final ChangeControl.GenericFactory changeControlFactory;
@@ -49,10 +64,12 @@
@Inject
ChangeFinder(
IndexConfig indexConfig,
+ @Named(CACHE_NAME) Cache<Change.Id, String> changeIdProjectCache,
Provider<InternalChangeQuery> queryProvider,
Provider<ReviewDb> reviewDb,
ChangeControl.GenericFactory changeControlFactory) {
this.indexConfig = indexConfig;
+ this.changeIdProjectCache = changeIdProjectCache;
this.queryProvider = queryProvider;
this.reviewDb = reviewDb;
this.changeControlFactory = changeControlFactory;
@@ -72,69 +89,69 @@
return Collections.emptyList();
}
+ int z = id.lastIndexOf('~');
+ int y = id.lastIndexOf('~', z - 1);
+ if (y < 0 && z > 0) {
+ // Try project~numericChangeId
+ Integer n = Ints.tryParse(id.substring(z + 1));
+ if (n != null) {
+ return fromProjectNumber(user, id.substring(0, z), n.intValue());
+ }
+ }
+
+ if (y < 0 && z < 0) {
+ // Try numeric changeId
+ Integer n = Ints.tryParse(id);
+ if (n != null) {
+ return find(new Change.Id(n), user);
+ }
+ }
+
// Use the index to search for changes, but don't return any stored fields,
// to force rereading in case the index is stale.
InternalChangeQuery query = queryProvider.get().noFields();
-
- int numTwiddles = 0;
- for (char c : id.toCharArray()) {
- if (c == '~') {
- numTwiddles++;
- }
- }
-
- if (numTwiddles == 1) {
- // Try project~numericChangeId
- String project = id.substring(0, id.indexOf('~'));
- Integer n = Ints.tryParse(id.substring(project.length() + 1));
- if (n != null) {
- Change.Id changeId = new Change.Id(n);
- try {
- return ImmutableList.of(
- changeControlFactory.controlFor(
- reviewDb.get(), Project.NameKey.parse(project), changeId, user));
- } catch (NoSuchChangeException e) {
- return Collections.emptyList();
- } catch (IllegalArgumentException e) {
- String changeNotFound = String.format("change %s not found in ReviewDb", changeId);
- String projectNotFound =
- String.format(
- "passed project %s when creating ChangeNotes for %s, but actual project is",
- project, changeId);
- if (e.getMessage().equals(changeNotFound) || e.getMessage().startsWith(projectNotFound)) {
- return Collections.emptyList();
- }
- throw e;
- } catch (OrmException e) {
- // Distinguish between a RepositoryNotFoundException (project argument invalid) and
- // other OrmExceptions (failure in the persistence layer).
- if (Throwables.getRootCause(e) instanceof RepositoryNotFoundException) {
- return Collections.emptyList();
- }
- throw e;
- }
- }
- } else if (numTwiddles == 2) {
- // Try change triplet
- Optional<ChangeTriplet> triplet = ChangeTriplet.parse(id);
+ if (y > 0 && z > 0) {
+ // Try change triplet (project~branch~Ihash...)
+ Optional<ChangeTriplet> triplet = ChangeTriplet.parse(id, y, z);
if (triplet.isPresent()) {
- return asChangeControls(
- query.byBranchKey(triplet.get().branch(), triplet.get().id()), user);
+ ChangeTriplet t = triplet.get();
+ return asChangeControls(query.byBranchKey(t.branch(), t.id()), user);
}
}
- // Try numeric changeId
- if (id.charAt(0) != '0') {
- Integer n = Ints.tryParse(id);
- if (n != null) {
- return asChangeControls(query.byLegacyChangeId(new Change.Id(n)), user);
- }
- }
-
- // Try isolated changeId
+ // Try isolated Ihash... format ("Change-Id: Ihash").
return asChangeControls(query.byKeyPrefix(id), user);
}
+ private List<ChangeControl> fromProjectNumber(CurrentUser user, String project, int changeNumber)
+ throws OrmException {
+ Change.Id cId = new Change.Id(changeNumber);
+ try {
+ return ImmutableList.of(
+ changeControlFactory.controlFor(
+ reviewDb.get(), Project.NameKey.parse(project), cId, user));
+ } catch (NoSuchChangeException e) {
+ return Collections.emptyList();
+ } catch (IllegalArgumentException e) {
+ String changeNotFound = String.format("change %s not found in ReviewDb", cId);
+ String projectNotFound =
+ String.format(
+ "passed project %s when creating ChangeNotes for %s, but actual project is",
+ project, cId);
+ if (e.getMessage().equals(changeNotFound) || e.getMessage().startsWith(projectNotFound)) {
+ return Collections.emptyList();
+ }
+ throw e;
+ } catch (OrmException e) {
+ // Distinguish between a RepositoryNotFoundException (project argument invalid) and
+ // other OrmExceptions (failure in the persistence layer).
+ if (Throwables.getRootCause(e) instanceof RepositoryNotFoundException) {
+ return Collections.emptyList();
+ }
+ throw e;
+ }
+ }
+
public ChangeControl findOne(Change.Id id, CurrentUser user) throws OrmException {
List<ChangeControl> ctls = find(id, user);
if (ctls.size() != 1) {
@@ -144,10 +161,19 @@
}
public List<ChangeControl> find(Change.Id id, CurrentUser user) throws OrmException {
+ String project = changeIdProjectCache.getIfPresent(id);
+ if (project != null) {
+ return fromProjectNumber(user, project, id.get());
+ }
+
// Use the index to search for changes, but don't return any stored fields,
// to force rereading in case the index is stale.
InternalChangeQuery query = queryProvider.get().noFields();
- return asChangeControls(query.byLegacyChangeId(id), user);
+ List<ChangeData> r = query.byLegacyChangeId(id);
+ if (r.size() == 1) {
+ changeIdProjectCache.put(id, r.get(0).project().get());
+ }
+ return asChangeControls(r, user);
}
private List<ChangeControl> asChangeControls(List<ChangeData> cds, CurrentUser user)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
index 2199d3a..c1f0989 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -412,6 +412,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new OrmException(
String.format("Update star labels on ref %s failed: %s", refName, result.name()));
@@ -439,6 +441,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new OrmException(
String.format("Delete star ref %s failed: %s", refName, result.name()));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
index fb7d7e7..56df1d3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.account;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.GroupDetail;
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -34,20 +35,15 @@
private final ReviewDb db;
private final GroupControl.Factory groupControl;
- private final GroupCache groupCache;
private final AccountGroup.Id groupId;
private GroupControl control;
@Inject
GroupDetailFactory(
- ReviewDb db,
- GroupControl.Factory groupControl,
- GroupCache groupCache,
- @Assisted AccountGroup.Id groupId) {
+ ReviewDb db, GroupControl.Factory groupControl, @Assisted AccountGroup.Id groupId) {
this.db = db;
this.groupControl = groupControl;
- this.groupCache = groupCache;
this.groupId = groupId;
}
@@ -55,12 +51,9 @@
@Override
public GroupDetail call() throws OrmException, NoSuchGroupException {
control = groupControl.validateFor(groupId);
- AccountGroup group = groupCache.get(groupId);
- GroupDetail detail = new GroupDetail();
- detail.setGroup(group);
- detail.setMembers(loadMembers());
- detail.setIncludes(loadIncludes());
- return detail;
+ List<AccountGroupMember> members = loadMembers();
+ List<AccountGroupById> includes = loadIncludes();
+ return new GroupDetail(members, includes);
}
private List<AccountGroupMember> loadMembers() throws OrmException {
@@ -74,14 +67,14 @@
}
private List<AccountGroupById> loadIncludes() throws OrmException {
- List<AccountGroupById> groups = new ArrayList<>();
-
- for (AccountGroupById m : db.accountGroupById().byGroup(groupId)) {
- if (control.canSeeGroup()) {
- groups.add(m);
- }
+ if (!control.canSeeGroup()) {
+ return ImmutableList.of();
}
+ List<AccountGroupById> groups = new ArrayList<>();
+ for (AccountGroupById m : db.accountGroupById().byGroup(groupId)) {
+ groups.add(m);
+ }
return groups;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
index d84d051..9173bd2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
@@ -104,17 +104,13 @@
final GroupDetail groupDetail = groupDetailFactory.create(group.getId()).call();
final Set<Account> members = new HashSet<>();
- if (groupDetail.members != null) {
- for (AccountGroupMember member : groupDetail.members) {
- members.add(accountCache.get(member.getAccountId()).getAccount());
- }
+ for (AccountGroupMember member : groupDetail.getMembers()) {
+ members.add(accountCache.get(member.getAccountId()).getAccount());
}
- if (groupDetail.includes != null) {
- for (AccountGroupById groupInclude : groupDetail.includes) {
- final AccountGroup includedGroup = groupCache.get(groupInclude.getIncludeUUID());
- if (includedGroup != null && !seen.contains(includedGroup.getGroupUUID())) {
- members.addAll(listAccounts(includedGroup.getGroupUUID(), project, seen));
- }
+ for (AccountGroupById groupInclude : groupDetail.getIncludes()) {
+ final AccountGroup includedGroup = groupCache.get(groupInclude.getIncludeUUID());
+ if (includedGroup != null && !seen.contains(includedGroup.getGroupUUID())) {
+ members.addAll(listAccounts(includedGroup.getGroupUUID(), project, seen));
}
}
return members;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
index 2985504..46f27f2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
@@ -757,6 +757,8 @@
case NOT_ATTEMPTED:
case REJECTED:
case REJECTED_CURRENT_BRANCH:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new IOException("Updating external IDs failed with " + res);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
index d7f868c..84f4535 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
@@ -16,13 +16,11 @@
import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
-import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.extensions.api.groups.GroupApi;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.GroupAuditEventInfo;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.GroupOptionsInfo;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.group.AddIncludedGroups;
import com.google.gerrit.server.group.AddMembers;
@@ -144,8 +142,6 @@
in.name = name;
try {
putName.apply(rsrc, in);
- } catch (NoSuchGroupException e) {
- throw new ResourceNotFoundException(name, e);
} catch (Exception e) {
throw asRestApiException("Cannot put group name", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
index dead1ad..75fb350 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
@@ -63,6 +63,9 @@
list.setAll(this.getAll());
list.setStart(this.getStart());
list.setLimit(this.getLimit());
+ list.setMatchPrefix(this.getPrefix());
+ list.setMatchSubstring(this.getSubstring());
+ list.setMatchRegex(this.getRegex());
return list.apply();
}
};
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeTriplet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeTriplet.java
index 71a3db7..2daeb7c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeTriplet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeTriplet.java
@@ -38,20 +38,22 @@
* @return the triplet if the input string has the proper format, or absent if not.
*/
public static Optional<ChangeTriplet> parse(String triplet) {
- int t2 = triplet.lastIndexOf('~');
- int t1 = triplet.lastIndexOf('~', t2 - 1);
- if (t1 < 0 || t2 < 0) {
+ int z = triplet.lastIndexOf('~');
+ int y = triplet.lastIndexOf('~', z - 1);
+ return parse(triplet, y, z);
+ }
+
+ public static Optional<ChangeTriplet> parse(String triplet, int y, int z) {
+ if (y < 0 || z < 0) {
return Optional.empty();
}
- String project = Url.decode(triplet.substring(0, t1));
- String branch = Url.decode(triplet.substring(t1 + 1, t2));
- String changeId = Url.decode(triplet.substring(t2 + 1));
-
- ChangeTriplet result =
+ String project = Url.decode(triplet.substring(0, y));
+ String branch = Url.decode(triplet.substring(y + 1, z));
+ String changeId = Url.decode(triplet.substring(z + 1));
+ return Optional.of(
new AutoValue_ChangeTriplet(
- new Branch.NameKey(new Project.NameKey(project), branch), new Change.Key(changeId));
- return Optional.of(result);
+ new Branch.NameKey(new Project.NameKey(project), branch), new Change.Key(changeId)));
}
public final Project.NameKey project() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
index 9fcb13d..56f637e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -616,6 +616,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
p.status = Status.FIX_FAILED;
p.outcome = "Failed to update patch set ref: " + result;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
index bbe04f5..92ad46d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
@@ -105,6 +105,7 @@
private final ChangeFinder changeFinder;
private final PatchSetUtil psUtil;
private final boolean allowDrafts;
+ private final boolean privateByDefault;
private final MergeUtil.Factory mergeUtilFactory;
private final SubmitType submitType;
private final NotifyUtil notifyUtil;
@@ -143,6 +144,7 @@
this.changeFinder = changeFinder;
this.psUtil = psUtil;
this.allowDrafts = config.getBoolean("change", "allowDrafts", true);
+ this.privateByDefault = config.getBoolean("change", "privateByDefault", false);
this.submitType = config.getEnum("project", null, "submitType", SubmitType.MERGE_IF_NECESSARY);
this.mergeUtilFactory = mergeUtilFactory;
this.notifyUtil = notifyUtil;
@@ -258,7 +260,7 @@
}
ins.setTopic(topic);
ins.setDraft(input.status == ChangeStatus.DRAFT);
- ins.setPrivate(input.isPrivate != null && input.isPrivate);
+ ins.setPrivate(input.isPrivate == null ? privateByDefault : input.isPrivate);
ins.setWorkInProgress(input.workInProgress != null && input.workInProgress);
ins.setGroups(groups);
ins.setNotify(input.notify);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Move.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Move.java
index ffc0dc4..f895bf7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Move.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Move.java
@@ -23,6 +23,7 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Change.Status;
@@ -40,6 +41,7 @@
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.update.BatchUpdate;
@@ -60,7 +62,8 @@
import org.eclipse.jgit.revwalk.RevWalk;
@Singleton
-public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, ChangeInfo> {
+public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, ChangeInfo>
+ implements UiAction<ChangeResource> {
private final PermissionBackend permissionBackend;
private final Provider<ReviewDb> dbProvider;
private final ChangeJson.Factory json;
@@ -209,4 +212,16 @@
return true;
}
}
+
+ @Override
+ public UiAction.Description getDescription(ChangeResource rsrc) {
+ return new UiAction.Description()
+ .setLabel("Move Change")
+ .setTitle("Move change to a different branch")
+ .setVisible(
+ permissionBackend
+ .user(rsrc.getUser())
+ .project(rsrc.getProject())
+ .testOrFalse(ProjectPermission.CREATE_CHANGE));
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 5a8945d..31989e3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -74,6 +74,7 @@
import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.server.ChangeFinder;
import com.google.gerrit.server.CmdLineParserModule;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PluginUser;
@@ -218,6 +219,7 @@
install(AccountCacheImpl.module());
install(BatchUpdate.module());
install(ChangeKindCacheImpl.module());
+ install(ChangeFinder.module());
install(ConflictsCacheImpl.module());
install(GroupCacheImpl.module());
install(GroupIncludeCacheImpl.module());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index e51312b..0d84767 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -285,6 +285,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new IOException(String.format("Failed to delete ref %s: %s", refName, result));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index ff5c5ed..ea28fa9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -63,7 +63,6 @@
import com.google.gerrit.server.git.strategy.SubmitStrategyListener;
import com.google.gerrit.server.git.validators.MergeValidationException;
import com.google.gerrit.server.git.validators.MergeValidators;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.SubmitRuleOptions;
@@ -234,7 +233,6 @@
private final Provider<MergeOpRepoManager> ormProvider;
private final NotifyUtil notifyUtil;
private final RetryHelper retryHelper;
- private final NotesMigration notesMigration;
private Timestamp ts;
private RequestId submissionId;
@@ -262,8 +260,7 @@
Provider<MergeOpRepoManager> ormProvider,
NotifyUtil notifyUtil,
TopicMetrics topicMetrics,
- RetryHelper retryHelper,
- NotesMigration notesMigration) {
+ RetryHelper retryHelper) {
this.cmUtil = cmUtil;
this.batchUpdateFactory = batchUpdateFactory;
this.internalUserFactory = internalUserFactory;
@@ -276,7 +273,6 @@
this.notifyUtil = notifyUtil;
this.retryHelper = retryHelper;
this.topicMetrics = topicMetrics;
- this.notesMigration = notesMigration;
}
@Override
@@ -909,11 +905,6 @@
return "Error submitting changes";
}
if (p == 1) {
- if (!notesMigration.fuseUpdates()) {
- // No fused updates: any subset of changes might or might not have been submitted, so don't
- // make any strong claims.
- return "Error submitting changes";
- }
// Fused updates: it's correct to say that none of the n changes were submitted.
return "Error submitting " + c + " changes";
}
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 5cd2184..42fb1b3 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
@@ -2177,7 +2177,9 @@
changeInserterFactory
.create(changeId, commit, refName)
.setTopic(magicBranch.topic)
- .setPrivate(magicBranch.isPrivate)
+ .setPrivate(
+ magicBranch.isPrivate
+ || (receiveConfig.privateByDefault && !magicBranch.removePrivate))
.setWorkInProgress(magicBranch.workInProgress)
// Changes already validated in validateNewCommits.
.setValidate(false);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveConfig.java
index 2b9f594..a3f2a31 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveConfig.java
@@ -28,6 +28,7 @@
final boolean checkMagicRefs;
final boolean checkReferencedObjectsAreReachable;
final boolean allowDrafts;
+ final boolean privateByDefault;
private final int systemMaxBatchChanges;
private final AccountLimits.Factory limitsFactory;
@@ -37,6 +38,7 @@
checkReferencedObjectsAreReachable =
config.getBoolean("receive", null, "checkReferencedObjectsAreReachable", true);
allowDrafts = config.getBoolean("change", null, "allowDrafts", true);
+ privateByDefault = config.getBoolean("change", null, "privateByDefault", false);
systemMaxBatchChanges = config.getInt("receive", "maxBatchChanges", 0);
this.limitsFactory = limitsFactory;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
index 99661b3..c7b64c0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -359,6 +359,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new IOException(
"Cannot delete "
@@ -444,6 +446,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new IOException(
"Cannot update "
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
index 040550c..afc3f48 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
@@ -99,15 +99,16 @@
input = Input.init(input);
GroupControl control = resource.getControl();
+ if (!control.canAddGroup()) {
+ throw new AuthException(String.format("Cannot add groups to group %s", group.getName()));
+ }
+
Map<AccountGroup.UUID, AccountGroupById> newIncludedGroups = new HashMap<>();
List<GroupInfo> result = new ArrayList<>();
Account.Id me = control.getUser().getAccountId();
for (String includedGroup : input.groups) {
GroupDescription.Basic d = groupsCollection.parse(includedGroup);
- if (!control.canAddGroup()) {
- throw new AuthException(String.format("Cannot add group: %s", d.getName()));
- }
if (!newIncludedGroups.containsKey(d.getGroupUUID())) {
AccountGroupById.Key agiKey = new AccountGroupById.Key(group.getId(), d.getGroupUUID());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
index 04be41e..4263bbe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
@@ -124,6 +124,9 @@
input = Input.init(input);
GroupControl control = resource.getControl();
+ if (!control.canAddMember()) {
+ throw new AuthException("Cannot add members to group " + internalGroup.getName());
+ }
Set<Account.Id> newMemberIds = new HashSet<>();
for (String nameOrEmailOrId : input.members) {
@@ -132,10 +135,6 @@
throw new UnprocessableEntityException(
String.format("Account Inactive: %s", nameOrEmailOrId));
}
-
- if (!control.canAddMember()) {
- throw new AuthException("Cannot add member: " + a.getFullName());
- }
newMemberIds.add(a.getId());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
index 1e1008c7a..c447daf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
@@ -71,16 +71,16 @@
input = Input.init(input);
final GroupControl control = resource.getControl();
+ if (!control.canRemoveGroup()) {
+ throw new AuthException(
+ String.format("Cannot delete groups from group %s", internalGroup.getName()));
+ }
+
final Map<AccountGroup.UUID, AccountGroupById> includedGroups =
getIncludedGroups(internalGroup.getId());
final List<AccountGroupById> toRemove = new ArrayList<>();
-
for (String includedGroup : input.groups) {
GroupDescription.Basic d = groupsCollection.parse(includedGroup);
- if (!control.canRemoveGroup()) {
- throw new AuthException(String.format("Cannot delete group: %s", d.getName()));
- }
-
AccountGroupById g = includedGroups.remove(d.getGroupUUID());
if (g != null) {
toRemove.add(g);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
index 6be46d6..dd3e022 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
@@ -73,16 +73,14 @@
input = Input.init(input);
final GroupControl control = resource.getControl();
+ if (!control.canRemoveMember()) {
+ throw new AuthException("Cannot delete members from group " + internalGroup.getName());
+ }
+
final Map<Account.Id, AccountGroupMember> members = getMembers(internalGroup.getId());
final List<AccountGroupMember> toRemove = new ArrayList<>();
-
for (String nameOrEmail : input.members) {
Account a = accounts.parse(nameOrEmail).getAccount();
-
- if (!control.canRemoveMember()) {
- throw new AuthException("Cannot delete member: " + a.getFullName());
- }
-
final AccountGroupMember m = members.remove(a.getId());
if (m != null) {
toRemove.add(m);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetAuditLog.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetAuditLog.java
index e29b37f..e2a467a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetAuditLog.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetAuditLog.java
@@ -21,7 +21,6 @@
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -64,18 +63,14 @@
@Override
public List<? extends GroupAuditEventInfo> apply(GroupResource rsrc)
- throws AuthException, ResourceNotFoundException, MethodNotAllowedException, OrmException {
- if (rsrc.toAccountGroup() == null) {
+ throws AuthException, MethodNotAllowedException, OrmException {
+ AccountGroup group = rsrc.toAccountGroup();
+ if (group == null) {
throw new MethodNotAllowedException();
} else if (!rsrc.getControl().isOwner()) {
throw new AuthException("Not group owner");
}
- AccountGroup group = db.get().accountGroups().get(rsrc.toAccountGroup().getId());
- if (group == null) {
- throw new ResourceNotFoundException();
- }
-
AccountLoader accountLoader = accountLoaderFactory.create(true);
List<GroupAuditEventInfo> auditEvents = new ArrayList<>();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
index b24d094..84b7665 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListMembers.java
@@ -102,20 +102,16 @@
return Collections.emptyMap();
}
- if (groupDetail.members != null) {
- for (AccountGroupMember m : groupDetail.members) {
- if (!members.containsKey(m.getAccountId())) {
- members.put(m.getAccountId(), accountLoader.get(m.getAccountId()));
- }
+ for (AccountGroupMember m : groupDetail.getMembers()) {
+ if (!members.containsKey(m.getAccountId())) {
+ members.put(m.getAccountId(), accountLoader.get(m.getAccountId()));
}
}
if (recursive) {
- if (groupDetail.includes != null) {
- for (AccountGroupById includedGroup : groupDetail.includes) {
- if (!seenGroups.contains(includedGroup.getIncludeUUID())) {
- members.putAll(getMembers(includedGroup.getIncludeUUID(), seenGroups));
- }
+ for (AccountGroupById includedGroup : groupDetail.getIncludes()) {
+ if (!seenGroups.contains(includedGroup.getIncludeUUID())) {
+ members.putAll(getMembers(includedGroup.getIncludeUUID(), seenGroups));
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java
index b78f4a5..bdf7a3e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java
@@ -15,8 +15,7 @@
package com.google.gerrit.server.group;
import com.google.common.base.Strings;
-import com.google.gerrit.common.data.GroupDetail;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.DefaultInput;
@@ -28,7 +27,6 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.account.GroupDetailFactory;
import com.google.gerrit.server.git.RenameGroupOp;
import com.google.gerrit.server.group.PutName.Input;
import com.google.gwtorm.server.OrmException;
@@ -36,7 +34,6 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.Future;
@@ -50,7 +47,6 @@
private final Provider<ReviewDb> db;
private final GroupCache groupCache;
- private final GroupDetailFactory.Factory groupDetailFactory;
private final RenameGroupOp.Factory renameGroupOpFactory;
private final Provider<IdentifiedUser> currentUser;
@@ -58,12 +54,10 @@
PutName(
Provider<ReviewDb> db,
GroupCache groupCache,
- GroupDetailFactory.Factory groupDetailFactory,
RenameGroupOp.Factory renameGroupOpFactory,
Provider<IdentifiedUser> currentUser) {
this.db = db;
this.groupCache = groupCache;
- this.groupDetailFactory = groupDetailFactory;
this.renameGroupOpFactory = renameGroupOpFactory;
this.currentUser = currentUser;
}
@@ -71,7 +65,7 @@
@Override
public String apply(GroupResource rsrc, Input input)
throws MethodNotAllowedException, AuthException, BadRequestException,
- ResourceConflictException, OrmException, NoSuchGroupException, IOException {
+ ResourceConflictException, OrmException, IOException {
if (rsrc.toAccountGroup() == null) {
throw new MethodNotAllowedException();
} else if (!rsrc.getControl().isOwner()) {
@@ -88,25 +82,25 @@
return newName;
}
- return renameGroup(rsrc.toAccountGroup(), newName).group.getName();
+ return renameGroup(rsrc.toAccountGroup(), newName);
}
- private GroupDetail renameGroup(AccountGroup group, String newName)
- throws ResourceConflictException, OrmException, NoSuchGroupException, IOException {
+ private String renameGroup(AccountGroup group, String newName)
+ throws ResourceConflictException, OrmException, IOException {
AccountGroup.Id groupId = group.getId();
AccountGroup.NameKey old = group.getNameKey();
AccountGroup.NameKey key = new AccountGroup.NameKey(newName);
try {
AccountGroupName id = new AccountGroupName(key, groupId);
- db.get().accountGroupNames().insert(Collections.singleton(id));
+ db.get().accountGroupNames().insert(ImmutableList.of(id));
} catch (OrmException e) {
AccountGroupName other = db.get().accountGroupNames().get(key);
if (other != null) {
// If we are using this identity, don't report the exception.
//
if (other.getId().equals(groupId)) {
- return groupDetailFactory.create(groupId).call();
+ return newName;
}
// Otherwise, someone else has this identity.
@@ -117,12 +111,9 @@
}
group.setNameKey(key);
- db.get().accountGroups().update(Collections.singleton(group));
+ db.get().accountGroups().update(ImmutableList.of(group));
- AccountGroupName priorName = db.get().accountGroupNames().get(old);
- if (priorName != null) {
- db.get().accountGroupNames().delete(Collections.singleton(priorName));
- }
+ db.get().accountGroupNames().deleteKeys(ImmutableList.of(old));
groupCache.evict(group);
groupCache.evictAfterRename(old, key);
@@ -136,6 +127,6 @@
newName)
.start(0, TimeUnit.MILLISECONDS);
- return groupDetailFactory.create(groupId).call();
+ return newName;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/MutableNotesMigration.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/MutableNotesMigration.java
index e167635..7f4912b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/MutableNotesMigration.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/MutableNotesMigration.java
@@ -72,10 +72,6 @@
return set(b -> b.setDisableChangeReviewDb(disableChangeReviewDb));
}
- public MutableNotesMigration setFuseUpdates(boolean fuseUpdates) {
- return set(b -> b.setFuseUpdates(fuseUpdates));
- }
-
public MutableNotesMigration setFailOnLoadForTest(boolean failOnLoadForTest) {
return set(b -> b.setFailOnLoadForTest(failOnLoadForTest));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigration.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigration.java
index 9c7029c..e560ec8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigration.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigration.java
@@ -56,7 +56,6 @@
public static final String SECTION_NOTE_DB = "noteDb";
private static final String DISABLE_REVIEW_DB = "disableReviewDb";
- private static final String FUSE_UPDATES = "fuseUpdates";
private static final String PRIMARY_STORAGE = "primaryStorage";
private static final String READ = "read";
private static final String SEQUENCE = "sequence";
@@ -87,7 +86,6 @@
SECTION_NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, PrimaryStorage.REVIEW_DB))
.setDisableChangeReviewDb(
cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), DISABLE_REVIEW_DB, false))
- .setFuseUpdates(cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), FUSE_UPDATES, false))
.setFailOnLoadForTest(false) // Only set in tests, can't be set via config.
.build();
}
@@ -102,8 +100,6 @@
abstract boolean disableChangeReviewDb();
- abstract boolean fuseUpdates();
-
abstract boolean failOnLoadForTest();
abstract Builder toBuilder();
@@ -114,7 +110,6 @@
cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), SEQUENCE, readChangeSequence());
cfg.setEnum(SECTION_NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, changePrimaryStorage());
cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), DISABLE_REVIEW_DB, disableChangeReviewDb());
- cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), FUSE_UPDATES, fuseUpdates());
}
@AutoValue.Builder
@@ -129,8 +124,6 @@
abstract Builder setDisableChangeReviewDb(boolean disableChangeReviewDb);
- abstract Builder setFuseUpdates(boolean fuseUpdates);
-
abstract Builder setFailOnLoadForTest(boolean failOnLoadForTest);
abstract Snapshot autoBuild();
@@ -206,21 +199,6 @@
}
/**
- * Fuse meta ref updates in the same batch as code updates.
- *
- * <p>When set, each {@link com.google.gerrit.server.update.BatchUpdate} results in a single
- * {@link org.eclipse.jgit.lib.BatchRefUpdate} to update both code and meta refs atomically.
- * Setting this option with a repository backend that does not support atomic multi-ref
- * transactions ({@link org.eclipse.jgit.lib.RefDatabase#performsAtomicTransactions()}) is a
- * configuration error, and all updates will fail at runtime.
- *
- * <p>Has no effect if {@link #disableChangeReviewDb()} is false.
- */
- public final boolean fuseUpdates() {
- return snapshot.get().fuseUpdates();
- }
-
- /**
* Whether to fail when reading any data from NoteDb.
*
* <p>Used in conjunction with {@link #readChanges()} for tests.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigrationState.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigrationState.java
index 2469574..c682aed 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigrationState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigrationState.java
@@ -31,26 +31,19 @@
* in the order in which they are defined.
*/
public enum NotesMigrationState {
- REVIEW_DB(false, false, false, PrimaryStorage.REVIEW_DB, false, false),
+ REVIEW_DB(false, false, false, PrimaryStorage.REVIEW_DB, false),
- WRITE(false, true, false, PrimaryStorage.REVIEW_DB, false, false),
+ WRITE(false, true, false, PrimaryStorage.REVIEW_DB, false),
- READ_WRITE_NO_SEQUENCE(true, true, false, PrimaryStorage.REVIEW_DB, false, false),
+ READ_WRITE_NO_SEQUENCE(true, true, false, PrimaryStorage.REVIEW_DB, false),
- READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY(
- true, true, true, PrimaryStorage.REVIEW_DB, false, false),
+ READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY(true, true, true, PrimaryStorage.REVIEW_DB, false),
- READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY(true, true, true, PrimaryStorage.NOTE_DB, false, false),
+ READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY(true, true, true, PrimaryStorage.NOTE_DB, false),
- // TODO(dborowitz): This only exists as a separate state to support testing in different
- // NoteDbModes. Once FileRepository fuses BatchRefUpdates, we won't have separate fused/unfused
- // states.
- NOTE_DB_UNFUSED(true, true, true, PrimaryStorage.NOTE_DB, true, false),
+ NOTE_DB(true, true, true, PrimaryStorage.NOTE_DB, true);
- NOTE_DB(true, true, true, PrimaryStorage.NOTE_DB, true, true);
-
- // TODO(dborowitz): Replace with NOTE_DB when FileRepository fuses BatchRefUpdates.
- public static final NotesMigrationState FINAL = NOTE_DB_UNFUSED;
+ public static final NotesMigrationState FINAL = NOTE_DB;
public static Optional<NotesMigrationState> forConfig(Config cfg) {
return forSnapshot(Snapshot.create(cfg));
@@ -72,8 +65,7 @@
boolean rawWriteChangesSetting,
boolean readChangeSequence,
PrimaryStorage changePrimaryStorage,
- boolean disableChangeReviewDb,
- boolean fuseUpdates) {
+ boolean disableChangeReviewDb) {
this.snapshot =
Snapshot.builder()
.setReadChanges(readChanges)
@@ -81,7 +73,6 @@
.setReadChangeSequence(readChangeSequence)
.setChangePrimaryStorage(changePrimaryStorage)
.setDisableChangeReviewDb(disableChangeReviewDb)
- .setFuseUpdates(fuseUpdates)
.build();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
index 0a76294..7323c2e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
@@ -19,7 +19,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB_UNFUSED;
+import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB;
import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_NO_SEQUENCE;
import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY;
import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY;
@@ -419,7 +419,7 @@
}
boolean rebuilt = false;
- while (state.compareTo(NOTE_DB_UNFUSED) < 0) {
+ while (state.compareTo(NOTE_DB) < 0) {
if (state.equals(stopAtState)) {
return;
}
@@ -458,18 +458,15 @@
break;
case READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY:
// The only way we can get here is if there was a failure on a previous run of
- // setNoteDbPrimary, since that method moves to NOTE_DB_UNFUSED if it completes
+ // setNoteDbPrimary, since that method moves to NOTE_DB if it completes
// successfully. Assume that not all changes were converted and re-run the step.
// migrateToNoteDbPrimary is a relatively fast no-op for already-migrated changes, so this
// isn't actually repeating work.
state = setNoteDbPrimary(state);
break;
- case NOTE_DB_UNFUSED:
+ case NOTE_DB:
// Done!
break;
- case NOTE_DB:
- // TODO(dborowitz): Allow this state once FileRepository supports fused updates.
- // Until then, fallthrough and throw.
default:
throw new MigrationException(
"Migration out of the following state is not supported:\n" + state.toText());
@@ -561,7 +558,7 @@
}
private NotesMigrationState disableReviewDb(NotesMigrationState prev) throws IOException {
- return saveState(prev, NOTE_DB_UNFUSED, c -> setAutoMigrate(c, false));
+ return saveState(prev, NOTE_DB, c -> setAutoMigrate(c, false));
}
private Optional<NotesMigrationState> loadState() throws IOException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java
index 625bf9e..12028b60 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java
@@ -40,7 +40,11 @@
String fileName = getSrcFile().getFileName().toString();
int firstDash = fileName.indexOf("-");
if (firstDash > 0) {
- return fileName.substring(firstDash + 1, fileName.lastIndexOf(".js"));
+ int extension =
+ fileName.endsWith(".js") ? fileName.lastIndexOf(".js") : fileName.lastIndexOf(".html");
+ if (extension > 0) {
+ return fileName.substring(firstDash + 1, extension);
+ }
}
return "";
}
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 517c743..0e514d6 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
@@ -23,6 +23,7 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.common.PluginInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.Url;
@@ -31,9 +32,11 @@
import com.google.inject.Inject;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.kohsuke.args4j.Option;
@@ -45,6 +48,9 @@
private boolean all;
private int limit;
private int start;
+ private String matchPrefix;
+ private String matchSubstring;
+ private String matchRegex;
@Deprecated
@Option(name = "--format", usage = "(deprecated) output format")
@@ -79,6 +85,31 @@
this.start = start;
}
+ @Option(
+ name = "--prefix",
+ aliases = {"-p"},
+ metaVar = "PREFIX",
+ usage = "match plugin prefix"
+ )
+ public void setMatchPrefix(String matchPrefix) {
+ this.matchPrefix = matchPrefix;
+ }
+
+ @Option(
+ name = "--match",
+ aliases = {"-m"},
+ metaVar = "MATCH",
+ usage = "match plugin substring"
+ )
+ public void setMatchSubstring(String matchSubstring) {
+ this.matchSubstring = matchSubstring;
+ }
+
+ @Option(name = "-r", metaVar = "REGEX", usage = "match plugin regex")
+ public void setMatchRegex(String matchRegex) {
+ this.matchRegex = matchRegex;
+ }
+
@Inject
protected ListPlugins(PluginLoader pluginLoader) {
this.pluginLoader = pluginLoader;
@@ -94,20 +125,33 @@
}
@Override
- public Object apply(TopLevelResource resource) {
+ public Object apply(TopLevelResource resource) throws BadRequestException {
format = OutputFormat.JSON;
return display(null);
}
- public SortedMap<String, PluginInfo> apply() {
+ public SortedMap<String, PluginInfo> apply() throws BadRequestException {
format = OutputFormat.JSON;
return display(null);
}
- public SortedMap<String, PluginInfo> display(@Nullable PrintWriter stdout) {
+ public SortedMap<String, PluginInfo> display(@Nullable PrintWriter stdout)
+ throws BadRequestException {
SortedMap<String, PluginInfo> output = new TreeMap<>();
- Stream<Plugin> s =
- Streams.stream(pluginLoader.getPlugins(all)).sorted(comparing(Plugin::getName));
+ Stream<Plugin> s = Streams.stream(pluginLoader.getPlugins(all));
+ if (matchPrefix != null) {
+ checkMatchOptions(matchSubstring == null && matchRegex == null);
+ s = s.filter(p -> p.getName().startsWith(matchPrefix));
+ } else if (matchSubstring != null) {
+ checkMatchOptions(matchPrefix == null && matchRegex == null);
+ String substring = matchSubstring.toLowerCase(Locale.US);
+ s = s.filter(p -> p.getName().toLowerCase(Locale.US).contains(substring));
+ } else if (matchRegex != null) {
+ checkMatchOptions(matchPrefix == null && matchSubstring == null);
+ Pattern pattern = Pattern.compile(matchRegex);
+ s = s.filter(p -> pattern.matcher(p.getName()).matches());
+ }
+ s = s.sorted(comparing(Plugin::getName));
if (start > 0) {
s = s.skip(start);
}
@@ -148,6 +192,12 @@
return null;
}
+ private void checkMatchOptions(boolean cond) throws BadRequestException {
+ if (!cond) {
+ throw new BadRequestException("specify exactly one of p/m/r");
+ }
+ }
+
public static PluginInfo toPluginInfo(Plugin p) {
String id;
String version;
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 0c15063..4e2e327 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
@@ -163,6 +163,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
{
throw new IOException(result.name());
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 56151a7..8177e41 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
@@ -353,6 +353,8 @@
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
{
throw new IOException(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteRef.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteRef.java
index 9b6538c..759f1d8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteRef.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteRef.java
@@ -169,6 +169,8 @@
case NOT_ATTEMPTED:
case REJECTED:
case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
log.error("Cannot delete " + ref + ": " + result.name());
throw new ResourceConflictException("cannot delete: " + result.name());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
index 1a0aff0..e202132 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
@@ -69,7 +69,7 @@
@Option(
name = "--start",
- aliases = {"-s"},
+ aliases = {"-S", "-s"},
metaVar = "CNT",
usage = "number of branches to skip"
)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java
index 4d1f808..8d03b6a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java
@@ -67,7 +67,7 @@
@Option(
name = "--start",
- aliases = {"-s"},
+ aliases = {"-S", "-s"},
metaVar = "CNT",
usage = "number of tags to skip"
)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
index f55d576..65c7315 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
@@ -51,6 +51,12 @@
/** Invalidate the cached information about the given project. */
void evict(Project.NameKey p);
+ /**
+ * Remove information about the given project from the cache. It will no longer be returned from
+ * {@link #all()}.
+ */
+ void remove(Project p);
+
/** @return sorted iteration of projects. */
Iterable<Project.NameKey> all();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index 38ca891..6ee143c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -166,6 +166,21 @@
}
@Override
+ public void remove(Project p) {
+ listLock.lock();
+ try {
+ SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
+ n.remove(p.getNameKey());
+ list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n));
+ } catch (ExecutionException e) {
+ log.warn("Cannot list available projects", e);
+ } finally {
+ listLock.unlock();
+ }
+ evict(p);
+ }
+
+ @Override
public void onCreateProject(Project.NameKey newProjectName) {
listLock.lock();
try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
index 90d083b..eeb47df 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
@@ -100,6 +100,8 @@
case NOT_ATTEMPTED:
case REJECTED:
case REJECTED_CURRENT_BRANCH:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
default:
throw new IOException("Setting HEAD failed with " + res);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/BatchUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/BatchUpdate.java
index fdae8e9..cf88be0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/BatchUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/update/BatchUpdate.java
@@ -95,8 +95,7 @@
@Override
public void configure() {
factory(ReviewDbBatchUpdate.AssistedFactory.class);
- factory(FusedNoteDbBatchUpdate.AssistedFactory.class);
- factory(UnfusedNoteDbBatchUpdate.AssistedFactory.class);
+ factory(NoteDbBatchUpdate.AssistedFactory.class);
}
};
}
@@ -105,29 +104,23 @@
public static class Factory {
private final NotesMigration migration;
private final ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory;
- private final FusedNoteDbBatchUpdate.AssistedFactory fusedNoteDbBatchUpdateFactory;
- private final UnfusedNoteDbBatchUpdate.AssistedFactory unfusedNoteDbBatchUpdateFactory;
+ private final NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory;
// TODO(dborowitz): Make this non-injectable to force all callers to use RetryHelper.
@Inject
Factory(
NotesMigration migration,
ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory,
- FusedNoteDbBatchUpdate.AssistedFactory fusedNoteDbBatchUpdateFactory,
- UnfusedNoteDbBatchUpdate.AssistedFactory unfusedNoteDbBatchUpdateFactory) {
+ NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory) {
this.migration = migration;
this.reviewDbBatchUpdateFactory = reviewDbBatchUpdateFactory;
- this.fusedNoteDbBatchUpdateFactory = fusedNoteDbBatchUpdateFactory;
- this.unfusedNoteDbBatchUpdateFactory = unfusedNoteDbBatchUpdateFactory;
+ this.noteDbBatchUpdateFactory = noteDbBatchUpdateFactory;
}
public BatchUpdate create(
ReviewDb db, Project.NameKey project, CurrentUser user, Timestamp when) {
if (migration.disableChangeReviewDb()) {
- if (migration.fuseUpdates()) {
- return fusedNoteDbBatchUpdateFactory.create(db, project, user, when);
- }
- return unfusedNoteDbBatchUpdateFactory.create(db, project, user, when);
+ return noteDbBatchUpdateFactory.create(db, project, user, when);
}
return reviewDbBatchUpdateFactory.create(db, project, user, when);
}
@@ -147,15 +140,9 @@
// copy them into an ImmutableList so there is no chance the callee can pollute the input
// collection.
if (migration.disableChangeReviewDb()) {
- if (migration.fuseUpdates()) {
- ImmutableList<FusedNoteDbBatchUpdate> noteDbUpdates =
- (ImmutableList) ImmutableList.copyOf(updates);
- FusedNoteDbBatchUpdate.execute(noteDbUpdates, listener, requestId, dryRun);
- } else {
- ImmutableList<UnfusedNoteDbBatchUpdate> noteDbUpdates =
- (ImmutableList) ImmutableList.copyOf(updates);
- UnfusedNoteDbBatchUpdate.execute(noteDbUpdates, listener, requestId, dryRun);
- }
+ ImmutableList<NoteDbBatchUpdate> noteDbUpdates =
+ (ImmutableList) ImmutableList.copyOf(updates);
+ NoteDbBatchUpdate.execute(noteDbUpdates, listener, requestId, dryRun);
} else {
ImmutableList<ReviewDbBatchUpdate> reviewDbUpdates =
(ImmutableList) ImmutableList.copyOf(updates);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/FusedNoteDbBatchUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/NoteDbBatchUpdate.java
similarity index 95%
rename from gerrit-server/src/main/java/com/google/gerrit/server/update/FusedNoteDbBatchUpdate.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/update/NoteDbBatchUpdate.java
index 7db5e43..d23ccf9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/FusedNoteDbBatchUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/update/NoteDbBatchUpdate.java
@@ -62,14 +62,14 @@
* <p>Used when {@code noteDb.changes.disableReviewDb=true}, at which point ReviewDb is not
* consulted during updates.
*/
-class FusedNoteDbBatchUpdate extends BatchUpdate {
+class NoteDbBatchUpdate extends BatchUpdate {
interface AssistedFactory {
- FusedNoteDbBatchUpdate create(
+ NoteDbBatchUpdate create(
ReviewDb db, Project.NameKey project, CurrentUser user, Timestamp when);
}
static void execute(
- ImmutableList<FusedNoteDbBatchUpdate> updates,
+ ImmutableList<NoteDbBatchUpdate> updates,
BatchUpdateListener listener,
@Nullable RequestId requestId,
boolean dryrun)
@@ -88,11 +88,11 @@
try {
switch (order) {
case REPO_BEFORE_DB:
- for (FusedNoteDbBatchUpdate u : updates) {
+ for (NoteDbBatchUpdate u : updates) {
u.executeUpdateRepo();
}
listener.afterUpdateRepos();
- for (FusedNoteDbBatchUpdate u : updates) {
+ for (NoteDbBatchUpdate u : updates) {
handles.add(u.executeChangeOps(dryrun));
}
for (ChangesHandle h : handles) {
@@ -113,10 +113,10 @@
// TODO(dborowitz): This may still result in multiple updates to All-Users, but that's
// currently not a big deal because multi-change batches generally aren't affecting
// drafts anyway.
- for (FusedNoteDbBatchUpdate u : updates) {
+ for (NoteDbBatchUpdate u : updates) {
handles.add(u.executeChangeOps(dryrun));
}
- for (FusedNoteDbBatchUpdate u : updates) {
+ for (NoteDbBatchUpdate u : updates) {
u.executeUpdateRepo();
}
for (ChangesHandle h : handles) {
@@ -151,7 +151,7 @@
u -> u.gitRefUpdated.fire(u.project, u.batchRefUpdate, u.getAccount().orElse(null)));
if (!dryrun) {
- for (FusedNoteDbBatchUpdate u : updates) {
+ for (NoteDbBatchUpdate u : updates) {
u.executePostOps();
}
}
@@ -163,7 +163,7 @@
class ContextImpl implements Context {
@Override
public RepoView getRepoView() throws IOException {
- return FusedNoteDbBatchUpdate.this.getRepoView();
+ return NoteDbBatchUpdate.this.getRepoView();
}
@Override
@@ -272,7 +272,7 @@
private final ReviewDb db;
@Inject
- FusedNoteDbBatchUpdate(
+ NoteDbBatchUpdate(
GitRepositoryManager repoManager,
@GerritPersonIdent PersonIdent serverIdent,
ChangeNotes.Factory changeNotesFactory,
@@ -354,7 +354,7 @@
}
void execute() throws OrmException, IOException {
- FusedNoteDbBatchUpdate.this.batchRefUpdate = manager.execute(dryrun);
+ NoteDbBatchUpdate.this.batchRefUpdate = manager.execute(dryrun);
}
@SuppressWarnings("deprecation")
@@ -390,8 +390,7 @@
Repository repo = repoView.getRepository();
checkState(
repo.getRefDatabase().performsAtomicTransactions(),
- "cannot use noteDb.changes.fuseUpdates=true with a repository that does not support atomic"
- + " batch ref updates: %s",
+ "cannot use NoteDb with a repository that does not support atomic batch ref updates: %s",
repo);
ChangesHandle handle =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/RetryHelper.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/RetryHelper.java
index 25a4e3c..7a1de31 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/update/RetryHelper.java
@@ -51,15 +51,10 @@
@GerritServerConfig Config cfg,
NotesMigration migration,
ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory,
- FusedNoteDbBatchUpdate.AssistedFactory fusedNoteDbBatchUpdateFactory,
- UnfusedNoteDbBatchUpdate.AssistedFactory unfusedNoteDbBatchUpdateFactory) {
+ NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory) {
this.migration = migration;
this.updateFactory =
- new BatchUpdate.Factory(
- migration,
- reviewDbBatchUpdateFactory,
- fusedNoteDbBatchUpdateFactory,
- unfusedNoteDbBatchUpdateFactory);
+ new BatchUpdate.Factory(migration, reviewDbBatchUpdateFactory, noteDbBatchUpdateFactory);
this.stopStrategy =
StopStrategies.stopAfterDelay(
cfg.getTimeUnit("noteDb", null, "retryTimeout", SECONDS.toMillis(5), MILLISECONDS),
@@ -80,7 +75,7 @@
throws RestApiException, UpdateException {
try {
RetryerBuilder<T> builder = RetryerBuilder.newBuilder();
- if (migration.disableChangeReviewDb() && migration.fuseUpdates()) {
+ if (migration.disableChangeReviewDb()) {
builder
.withStopStrategy(stopStrategy)
.withWaitStrategy(waitStrategy)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/UnfusedNoteDbBatchUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/UnfusedNoteDbBatchUpdate.java
deleted file mode 100644
index ce96c0e..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/UnfusedNoteDbBatchUpdate.java
+++ /dev/null
@@ -1,459 +0,0 @@
-// Copyright (C) 2017 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.update;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.index.change.ChangeIndexer;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.util.RequestId;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * {@link BatchUpdate} implementation that only supports NoteDb.
- *
- * <p>Used when {@code noteDb.changes.disableReviewDb=true}, at which point ReviewDb is not
- * consulted during updates.
- */
-class UnfusedNoteDbBatchUpdate extends BatchUpdate {
- interface AssistedFactory {
- UnfusedNoteDbBatchUpdate create(
- ReviewDb db, Project.NameKey project, CurrentUser user, Timestamp when);
- }
-
- static void execute(
- ImmutableList<UnfusedNoteDbBatchUpdate> updates,
- BatchUpdateListener listener,
- @Nullable RequestId requestId,
- boolean dryrun)
- throws UpdateException, RestApiException {
- if (updates.isEmpty()) {
- return;
- }
- setRequestIds(updates, requestId);
-
- try {
- Order order = getOrder(updates, listener);
- // TODO(dborowitz): Fuse implementations to use a single BatchRefUpdate between phases. Note
- // that we may still need to respect the order, since op implementations may make assumptions
- // about the order in which their methods are called.
- switch (order) {
- case REPO_BEFORE_DB:
- for (UnfusedNoteDbBatchUpdate u : updates) {
- u.executeUpdateRepo();
- }
- listener.afterUpdateRepos();
- for (UnfusedNoteDbBatchUpdate u : updates) {
- u.executeRefUpdates(dryrun);
- }
- listener.afterUpdateRefs();
- for (UnfusedNoteDbBatchUpdate u : updates) {
- u.reindexChanges(u.executeChangeOps(dryrun), dryrun);
- }
- listener.afterUpdateChanges();
- break;
- case DB_BEFORE_REPO:
- for (UnfusedNoteDbBatchUpdate u : updates) {
- u.reindexChanges(u.executeChangeOps(dryrun), dryrun);
- }
- for (UnfusedNoteDbBatchUpdate u : updates) {
- u.executeUpdateRepo();
- }
- for (UnfusedNoteDbBatchUpdate u : updates) {
- u.executeRefUpdates(dryrun);
- }
- break;
- default:
- throw new IllegalStateException("invalid execution order: " + order);
- }
-
- ChangeIndexer.allAsList(
- updates.stream().flatMap(u -> u.indexFutures.stream()).collect(toList()))
- .get();
-
- // Fire ref update events only after all mutations are finished, since callers may assume a
- // patch set ref being created means the change was created, or a branch advancing meaning
- // some changes were closed.
- updates
- .stream()
- .filter(u -> u.batchRefUpdate != null)
- .forEach(
- u -> u.gitRefUpdated.fire(u.project, u.batchRefUpdate, u.getAccount().orElse(null)));
-
- if (!dryrun) {
- for (UnfusedNoteDbBatchUpdate u : updates) {
- u.executePostOps();
- }
- }
- } catch (Exception e) {
- wrapAndThrowException(e);
- }
- }
-
- class ContextImpl implements Context {
- @Override
- public RepoView getRepoView() throws IOException {
- return UnfusedNoteDbBatchUpdate.this.getRepoView();
- }
-
- @Override
- public RevWalk getRevWalk() throws IOException {
- return getRepoView().getRevWalk();
- }
-
- @Override
- public Project.NameKey getProject() {
- return project;
- }
-
- @Override
- public Timestamp getWhen() {
- return when;
- }
-
- @Override
- public TimeZone getTimeZone() {
- return tz;
- }
-
- @Override
- public ReviewDb getDb() {
- return db;
- }
-
- @Override
- public CurrentUser getUser() {
- return user;
- }
-
- @Override
- public Order getOrder() {
- return order;
- }
- }
-
- private class RepoContextImpl extends ContextImpl implements RepoContext {
- @Override
- public ObjectInserter getInserter() throws IOException {
- return getRepoView().getInserterWrapper();
- }
-
- @Override
- public void addRefUpdate(ReceiveCommand cmd) throws IOException {
- getRepoView().getCommands().add(cmd);
- }
- }
-
- private class ChangeContextImpl extends ContextImpl implements ChangeContext {
- private final ChangeControl ctl;
- private final Map<PatchSet.Id, ChangeUpdate> updates;
-
- private boolean deleted;
-
- protected ChangeContextImpl(ChangeControl ctl) {
- this.ctl = checkNotNull(ctl);
- updates = new TreeMap<>(comparing(PatchSet.Id::get));
- }
-
- @Override
- public ChangeUpdate getUpdate(PatchSet.Id psId) {
- ChangeUpdate u = updates.get(psId);
- if (u == null) {
- u = changeUpdateFactory.create(ctl, when);
- if (newChanges.containsKey(ctl.getId())) {
- u.setAllowWriteToNewRef(true);
- }
- u.setPatchSetId(psId);
- updates.put(psId, u);
- }
- return u;
- }
-
- @Override
- public ChangeControl getControl() {
- return ctl;
- }
-
- @Override
- public void dontBumpLastUpdatedOn() {
- // Do nothing; NoteDb effectively updates timestamp if and only if a commit was written to the
- // change meta ref.
- }
-
- @Override
- public void deleteChange() {
- deleted = true;
- }
- }
-
- /** Per-change result status from {@link #executeChangeOps}. */
- private enum ChangeResult {
- SKIPPED,
- UPSERTED,
- DELETED;
- }
-
- private final ChangeNotes.Factory changeNotesFactory;
- private final ChangeControl.GenericFactory changeControlFactory;
- private final ChangeUpdate.Factory changeUpdateFactory;
- private final NoteDbUpdateManager.Factory updateManagerFactory;
- private final ChangeIndexer indexer;
- private final GitReferenceUpdated gitRefUpdated;
- private final ReviewDb db;
-
- @SuppressWarnings("deprecation")
- private List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures;
-
- @Inject
- UnfusedNoteDbBatchUpdate(
- GitRepositoryManager repoManager,
- @GerritPersonIdent PersonIdent serverIdent,
- ChangeNotes.Factory changeNotesFactory,
- ChangeControl.GenericFactory changeControlFactory,
- ChangeUpdate.Factory changeUpdateFactory,
- NoteDbUpdateManager.Factory updateManagerFactory,
- ChangeIndexer indexer,
- GitReferenceUpdated gitRefUpdated,
- @Assisted ReviewDb db,
- @Assisted Project.NameKey project,
- @Assisted CurrentUser user,
- @Assisted Timestamp when) {
- super(repoManager, serverIdent, project, user, when);
- this.changeNotesFactory = changeNotesFactory;
- this.changeControlFactory = changeControlFactory;
- this.changeUpdateFactory = changeUpdateFactory;
- this.updateManagerFactory = updateManagerFactory;
- this.indexer = indexer;
- this.gitRefUpdated = gitRefUpdated;
- this.db = db;
- this.indexFutures = new ArrayList<>();
- }
-
- @Override
- public void execute(BatchUpdateListener listener) throws UpdateException, RestApiException {
- execute(ImmutableList.of(this), listener, requestId, false);
- }
-
- @Override
- protected Context newContext() {
- return new ContextImpl();
- }
-
- private void executeUpdateRepo() throws UpdateException, RestApiException {
- try {
- logDebug("Executing updateRepo on {} ops", ops.size());
- RepoContextImpl ctx = new RepoContextImpl();
- for (BatchUpdateOp op : ops.values()) {
- op.updateRepo(ctx);
- }
-
- logDebug("Executing updateRepo on {} RepoOnlyOps", repoOnlyOps.size());
- for (RepoOnlyOp op : repoOnlyOps) {
- op.updateRepo(ctx);
- }
-
- if (onSubmitValidators != null && !getRefUpdates().isEmpty()) {
- // Validation of refs has to take place here and not at the beginning of executeRefUpdates.
- // Otherwise, failing validation in a second BatchUpdate object will happen *after* the
- // first update's executeRefUpdates has finished, hence after first repo's refs have been
- // updated, which is too late.
- onSubmitValidators.validate(
- project, ctx.getRevWalk().getObjectReader(), repoView.getCommands());
- }
-
- // TODO(dborowitz): Don't flush when fusing phases.
- if (repoView != null) {
- logDebug("Flushing inserter");
- repoView.getInserter().flush();
- } else {
- logDebug("No objects to flush");
- }
- } catch (Exception e) {
- Throwables.throwIfInstanceOf(e, RestApiException.class);
- throw new UpdateException(e);
- }
- }
-
- // TODO(dborowitz): Don't execute non-change ref updates separately when fusing phases.
- private void executeRefUpdates(boolean dryrun) throws IOException, RestApiException {
- if (getRefUpdates().isEmpty()) {
- logDebug("No ref updates to execute");
- return;
- }
- // May not be opened if the caller added ref updates but no new objects.
- initRepository();
- batchRefUpdate = repoView.getRepository().getRefDatabase().newBatchUpdate();
- batchRefUpdate.setPushCertificate(pushCert);
- batchRefUpdate.setRefLogMessage(refLogMessage, true);
- batchRefUpdate.setAllowNonFastForwards(true);
- repoView.getCommands().addTo(batchRefUpdate);
- logDebug("Executing batch of {} ref updates", batchRefUpdate.getCommands().size());
- if (dryrun) {
- return;
- }
-
- // Force BatchRefUpdate to read newly referenced objects using a new RevWalk, rather than one
- // that might have access to unflushed objects.
- try (RevWalk updateRw = new RevWalk(repoView.getRepository())) {
- batchRefUpdate.execute(updateRw, NullProgressMonitor.INSTANCE);
- }
- boolean ok = true;
- for (ReceiveCommand cmd : batchRefUpdate.getCommands()) {
- if (cmd.getResult() != ReceiveCommand.Result.OK) {
- ok = false;
- break;
- }
- }
- if (!ok) {
- throw new RestApiException("BatchRefUpdate failed: " + batchRefUpdate);
- }
- }
-
- private Map<Change.Id, ChangeResult> executeChangeOps(boolean dryrun) throws Exception {
- logDebug("Executing change ops");
- Map<Change.Id, ChangeResult> result =
- Maps.newLinkedHashMapWithExpectedSize(ops.keySet().size());
- initRepository();
- Repository repo = repoView.getRepository();
- // TODO(dborowitz): Teach NoteDbUpdateManager to allow reusing the same inserter and batch ref
- // update as in executeUpdateRepo.
- try (ObjectInserter ins = repo.newObjectInserter();
- ObjectReader reader = ins.newReader();
- RevWalk rw = new RevWalk(reader);
- NoteDbUpdateManager updateManager =
- updateManagerFactory
- .create(project)
- .setChangeRepo(repo, rw, ins, new ChainedReceiveCommands(repo))) {
- if (user.isIdentifiedUser()) {
- updateManager.setRefLogIdent(user.asIdentifiedUser().newRefLogIdent(when, tz));
- }
- for (Map.Entry<Change.Id, Collection<BatchUpdateOp>> e : ops.asMap().entrySet()) {
- Change.Id id = e.getKey();
- ChangeContextImpl ctx = newChangeContext(id);
- boolean dirty = false;
- logDebug("Applying {} ops for change {}", e.getValue().size(), id);
- for (BatchUpdateOp op : e.getValue()) {
- dirty |= op.updateChange(ctx);
- }
- if (!dirty) {
- logDebug("No ops reported dirty, short-circuiting");
- result.put(id, ChangeResult.SKIPPED);
- continue;
- }
- for (ChangeUpdate u : ctx.updates.values()) {
- updateManager.add(u);
- }
- if (ctx.deleted) {
- logDebug("Change {} was deleted", id);
- updateManager.deleteChange(id);
- result.put(id, ChangeResult.DELETED);
- } else {
- result.put(id, ChangeResult.UPSERTED);
- }
- }
-
- if (!dryrun) {
- logDebug("Executing NoteDb updates");
- updateManager.execute();
- }
- }
- return result;
- }
-
- private ChangeContextImpl newChangeContext(Change.Id id) throws OrmException {
- logDebug("Opening change {} for update", id);
- Change c = newChanges.get(id);
- boolean isNew = c != null;
- if (!isNew) {
- // Pass a synthetic change into ChangeNotes.Factory, which will take care of checking for
- // existence and populating columns from the parsed notes state.
- // TODO(dborowitz): This dance made more sense when using Reviewdb; consider a nicer way.
- c = ChangeNotes.Factory.newNoteDbOnlyChange(project, id);
- } else {
- logDebug("Change {} is new", id);
- }
- ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c, !isNew);
- ChangeControl ctl = changeControlFactory.controlFor(notes, user);
- return new ChangeContextImpl(ctl);
- }
-
- private void reindexChanges(Map<Change.Id, ChangeResult> updateResults, boolean dryrun) {
- if (dryrun) {
- return;
- }
- logDebug("Reindexing {} changes", updateResults.size());
- for (Map.Entry<Change.Id, ChangeResult> e : updateResults.entrySet()) {
- Change.Id id = e.getKey();
- switch (e.getValue()) {
- case UPSERTED:
- indexFutures.add(indexer.indexAsync(project, id));
- break;
- case DELETED:
- indexFutures.add(indexer.deleteAsync(id));
- break;
- case SKIPPED:
- break;
- default:
- throw new IllegalStateException("unexpected result: " + e.getValue());
- }
- }
- }
-
- private void executePostOps() throws Exception {
- ContextImpl ctx = new ContextImpl();
- for (BatchUpdateOp op : ops.values()) {
- op.postUpdate(ctx);
- }
-
- for (RepoOnlyOp op : repoOnlyOps) {
- op.postUpdate(ctx);
- }
- }
-}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index 29f17b4..9b9cfff 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -239,6 +239,9 @@
public void evict(Project p) {}
@Override
+ public void remove(Project p) {}
+
+ @Override
public Iterable<Project.NameKey> all() {
return Collections.emptySet();
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java
index 0e7201a..e0c51b7 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java
@@ -19,7 +19,6 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.RepositoryCaseMismatchException;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.inject.Inject;
import java.util.HashMap;
import java.util.Map;
@@ -32,7 +31,7 @@
/** Repository manager that uses in-memory repositories. */
public class InMemoryRepositoryManager implements GitRepositoryManager {
public static InMemoryRepository newRepository(Project.NameKey name) {
- return new Repo(NoteDbMode.newNotesMigrationFromEnv(), name);
+ return new Repo(name);
}
public static class Description extends DfsRepositoryDescription {
@@ -51,15 +50,9 @@
public static class Repo extends InMemoryRepository {
private String description;
- private Repo(NotesMigration notesMigration, Project.NameKey name) {
+ private Repo(Project.NameKey name) {
super(new Description(name));
- // Normally, mimic the behavior of JGit FileRepository, the standard Gerrit repository
- // backend, and don't support atomic ref updates. The exception is when we're testing with
- // fused ref updates, which requires atomic ref updates to function.
- //
- // TODO(dborowitz): Change to match the behavior of JGit FileRepository after fixing
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=515678
- setPerformsAtomicTransactions(notesMigration.fuseUpdates());
+ setPerformsAtomicTransactions(true);
}
@Override
@@ -78,16 +71,10 @@
}
}
- private final NotesMigration notesMigration;
private final Map<String, Repo> repos;
- public InMemoryRepositoryManager() {
- this(NoteDbMode.newNotesMigrationFromEnv());
- }
-
@Inject
- InMemoryRepositoryManager(NotesMigration notesMigration) {
- this.notesMigration = notesMigration;
+ public InMemoryRepositoryManager() {
this.repos = new HashMap<>();
}
@@ -106,7 +93,7 @@
throw new RepositoryCaseMismatchException(name);
}
} catch (RepositoryNotFoundException e) {
- repo = new Repo(notesMigration, name);
+ repo = new Repo(name);
repos.put(normalize(name), repo);
}
return repo;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbMode.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbMode.java
index 7c19191..b51a011 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbMode.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbMode.java
@@ -35,11 +35,15 @@
PRIMARY(NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY),
/** All change tables are entirely disabled. */
- DISABLE_CHANGE_REVIEW_DB(NotesMigrationState.NOTE_DB_UNFUSED),
+ DISABLE_CHANGE_REVIEW_DB(NotesMigrationState.NOTE_DB),
/** All change tables are entirely disabled, and code/meta ref updates are fused. */
FUSED(NotesMigrationState.NOTE_DB),
+ // TODO(dborowitz): Change CI to use this, then remove FUSED and DISABLE_CHANGE_REVIEW_DB.
+ /** All change tables are entirely disabled, and code/meta ref updates are fused. */
+ ON(NotesMigrationState.NOTE_DB),
+
/**
* Run tests with NoteDb disabled, then convert ReviewDb to NoteDb and check that the results
* match.
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
index 49b468f..6ec3a28 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
@@ -14,7 +14,6 @@
package com.google.gerrit.sshd.commands;
-import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -52,7 +51,7 @@
PutName.Input input = new PutName.Input();
input.name = newGroupName;
putName.apply(rsrc, input);
- } catch (RestApiException | OrmException | IOException | NoSuchGroupException e) {
+ } catch (RestApiException | OrmException | IOException e) {
throw die(e);
}
}
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
index fd10fbb..b6f8d99 100644
--- a/lib/jgit/jgit.bzl
+++ b/lib/jgit/jgit.bzl
@@ -1,12 +1,12 @@
load("//tools/bzl:maven_jar.bzl", "GERRIT", "MAVEN_LOCAL", "MAVEN_CENTRAL", "maven_jar")
-_JGIT_VERS = "4.8.0.201706111038-r"
+_JGIT_VERS = "4.8.0.201706111038-r.71-g45da0fc6f"
_DOC_VERS = _JGIT_VERS # Set to _JGIT_VERS unless using a snapshot
JGIT_DOC_URL = "http://download.eclipse.org/jgit/site/" + _DOC_VERS + "/apidocs"
-_JGIT_REPO = MAVEN_CENTRAL # Leave here even if set to MAVEN_CENTRAL.
+_JGIT_REPO = GERRIT # Leave here even if set to MAVEN_CENTRAL.
# set this to use a local version.
# "/home/<user>/projects/jgit"
@@ -26,28 +26,28 @@
name = "jgit_lib",
artifact = "org.eclipse.jgit:org.eclipse.jgit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "f0978a9e868accf9a405d9387bec091a99d87633",
- src_sha1 = "93cefbf1d73f1179b40419a3898c53a64e52aa93",
+ sha1 = "7248b0a7d7f76dd4f7e55ed081b981cf4d8aa26e",
+ src_sha1 = "6ed203c95decc3f795f44ca17149e7554b92212d",
unsign = True,
)
maven_jar(
name = "jgit_servlet",
artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "3c099afdc063bad438a3b87eea643e9722a07de8",
+ sha1 = "f21fc0c651cc9475db92061432d919ba28b7a7ad",
unsign = True,
)
maven_jar(
name = "jgit_archive",
artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "1350a5cf1fad91dd33b66f9fb804dc8e68270890",
+ sha1 = "0f179321f527840dfc8ca79894eba2f6b255dbab",
)
maven_jar(
name = "jgit_junit",
artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "4f45f8f6714df649dbad8c1b1baf68b9510b5047",
+ sha1 = "7e5225064cf14115bddaae9448246c83c89f78ad",
unsign = True,
)
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
index 99efede..706d499 100644
--- a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
@@ -16,7 +16,7 @@
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>keyboard-shortcut-behavior</title>
+<title>base-url-behavior</title>
<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="../../bower_components/web-component-tester/browser.js"></script>
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
index 4178a0c..3d26b55 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
@@ -87,7 +87,7 @@
</template>
<template is="dom-if" if="[[_showPluginList]]" restamp="true">
<main class="table">
- <gr-plugin-list class="table"></gr-plugin-list>
+ <gr-plugin-list class="table" params="[[params]]"></gr-plugin-list>
</main>
</template>
<template is="dom-if" if="[[_showProjectDetailList]]" restamp="true">
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
index 788df49..7d12e96 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
@@ -18,33 +18,42 @@
<link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
<link rel="import" href="../../../styles/gr-table-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-list-view/gr-list-view.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-plugin-list">
<template>
<style include="shared-styles"></style>
<style include="gr-table-styles"></style>
- <table id="list" class="genericList">
- <tr class="headerRow">
- <th class="name topHeader">Plugin Name</th>
- <th class="version topHeader">Version</th>
- <th class="status topHeader">Status</th>
- </tr>
- <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
- <td>Loading...</td>
- </tr>
- <tbody class$="[[computeLoadingClass(_loading)]]">
- <template is="dom-repeat" items="[[_plugins]]">
- <tr class="table">
- <td class="name">
- <a href$="[[_computePluginUrl(item.index_url)]]">[[item.id]]</a>
- </td>
- <td class="version">[[item.version]]</td>
- <td class="status">[[_status(item)]]</td>
- </tr>
- </template>
- </tbody>
- </table>
+ <gr-list-view
+ filter="[[_filter]]"
+ items-per-page="[[_pluginsPerPage]]"
+ items="[[_plugins]]"
+ loading="[[_loading]]"
+ offset="[[_offset]]"
+ path="[[_path]]">
+ <table id="list" class="genericList">
+ <tr class="headerRow">
+ <th class="name topHeader">Plugin Name</th>
+ <th class="version topHeader">Version</th>
+ <th class="status topHeader">Status</th>
+ </tr>
+ <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
+ <td>Loading...</td>
+ </tr>
+ <tbody class$="[[computeLoadingClass(_loading)]]">
+ <template is="dom-repeat" items="[[_shownPlugins]]">
+ <tr class="table">
+ <td class="name">
+ <a href$="[[_computePluginUrl(item.index_url)]]">[[item.id]]</a>
+ </td>
+ <td class="version">[[item.version]]</td>
+ <td class="status">[[_status(item)]]</td>
+ </tr>
+ </template>
+ </tbody>
+ </table>
+ </gr-list-view>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-plugin-list.js"></script>
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
index b4fa9ad..a18a862 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
@@ -18,23 +18,61 @@
is: 'gr-plugin-list',
properties: {
+ /**
+ * URL params passed from the router.
+ */
+ params: {
+ type: Object,
+ observer: '_paramsChanged',
+ },
+ /**
+ * Offset of currently visible query results.
+ */
+ _offset: Number,
+ _path: {
+ type: String,
+ readOnly: true,
+ value: '/admin/plugins',
+ },
_plugins: Array,
+ /**
+ * Because we request one more than the pluginsPerPage, _shownPlugins
+ * maybe one less than _plugins.
+ * */
+ _shownPlugins: {
+ type: Array,
+ computed: 'computeShownItems(_plugins)',
+ },
+ _pluginsPerPage: {
+ type: Number,
+ value: 25,
+ },
_loading: {
type: Boolean,
value: true,
},
+ _filter: String,
},
behaviors: [
Gerrit.ListViewBehavior,
],
- ready() {
- this._getPlugins();
+ attached() {
+ this.fire('title-change', {title: 'Plugin List'});
},
- _getPlugins() {
- return this.$.restAPI.getPlugins()
+ _paramsChanged(params) {
+ this._loading = true;
+ this._filter = this.getFilterValue(params);
+ this._offset = this.getOffsetValue(params);
+
+ return this._getPlugins(this._filter, this._pluginsPerPage,
+ this._offset);
+ },
+
+ _getPlugins(filter, pluginsPerPage, offset) {
+ return this.$.restAPI.getPlugins(filter, pluginsPerPage, offset)
.then(plugins => {
if (!plugins) {
this._plugins = [];
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
index 8cff477..52f1a22 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
@@ -33,7 +33,7 @@
</test-fixture>
<script>
- let counter = 0;
+ let counter;
const pluginGenerator = () => {
return {
id: `test${++counter}`,
@@ -47,26 +47,93 @@
let element;
let plugins;
let sandbox;
+ let value;
setup(() => {
sandbox = sinon.sandbox.create();
- plugins = _.times(26, pluginGenerator);
- stub('gr-rest-api-interface', {
- getPlugins() { return Promise.resolve(plugins); },
- });
element = fixture('basic');
+ counter = 0;
});
teardown(() => {
sandbox.restore();
});
- test('_plugins item is formatted correctly', () => {
- return element._getPlugins().then(() => {
- assert.equal(element._plugins[2].id, 'test3');
- assert.equal(element._plugins[2].index_url, 'plugins/test3/');
- assert.equal(element._plugins[2].version, '3.0-SNAPSHOT');
- assert.equal(element._plugins[2].disabled, false);
+ suite('list with plugins', () => {
+ setup(done => {
+ plugins = _.times(26, pluginGenerator);
+
+ stub('gr-rest-api-interface', {
+ getPlugins(num, offset) {
+ return Promise.resolve(plugins);
+ },
+ });
+
+ element._paramsChanged(value).then(() => { flush(done); });
+ });
+
+ test('plugin in the list is formatted correctly', done => {
+ flush(() => {
+ assert.equal(element._plugins[2].id, 'test3');
+ assert.equal(element._plugins[2].index_url, 'plugins/test3/');
+ assert.equal(element._plugins[2].version, '3.0-SNAPSHOT');
+ assert.equal(element._plugins[2].disabled, false);
+ done();
+ });
+ });
+
+ test('_shownPlugins', () => {
+ assert.equal(element._shownPlugins.length, 25);
+ });
+ });
+
+ suite('list with less then 26 plugins', () => {
+ setup(done => {
+ plugins = _.times(25, pluginGenerator);
+
+ stub('gr-rest-api-interface', {
+ getPlugins(num, offset) {
+ return Promise.resolve(plugins);
+ },
+ });
+
+ element._paramsChanged(value).then(() => { flush(done); });
+ });
+
+ test('_shownPlugins', () => {
+ assert.equal(element._shownPlugins.length, 25);
+ });
+ });
+
+ suite('filter', () => {
+ test('_paramsChanged', done => {
+ sandbox.stub(element.$.restAPI, 'getPlugins', () => {
+ return Promise.resolve(plugins);
+ });
+ const value = {
+ filter: 'test',
+ offset: 25,
+ };
+ element._paramsChanged(value).then(() => {
+ assert.isTrue(element.$.restAPI.getPlugins.lastCall
+ .calledWithExactly('test', 25, 25));
+ done();
+ });
+ });
+ });
+
+ suite('loading', () => {
+ test('correct contents are displayed', () => {
+ assert.isTrue(element._loading);
+ assert.equal(element.computeLoadingClass(element._loading), 'loading');
+ assert.equal(getComputedStyle(element.$.loading).display, 'block');
+
+ element._loading = false;
+ element._plugins = _.times(25, pluginGenerator);
+
+ flushAsynchronousOperations();
+ assert.equal(element.computeLoadingClass(element._loading), '');
+ assert.equal(getComputedStyle(element.$.loading).display, 'none');
});
});
});
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index e7b15af..af5a3b0 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -103,6 +103,7 @@
'o enter': '_handleEnterKey',
'p [': '_handlePKey',
'shift+r': '_handleRKey',
+ 's': '_handleSKey',
},
attached() {
@@ -163,7 +164,9 @@
},
_computeLabelShortcut(labelName) {
- return labelName.replace(/[a-z-]/g, '');
+ return labelName.split('-').reduce((a, i) => {
+ return a + i[0].toUpperCase();
+ }, '');
},
_changesChanged(changes) {
@@ -260,6 +263,27 @@
window.location.reload();
},
+ _handleSKey(e) {
+ if (this.shouldSuppressKeyboardShortcut(e) ||
+ this.modifierPressed(e)) { return; }
+
+ e.preventDefault();
+ this._toggleStarForIndex(this.selectedIndex);
+ },
+
+ _toggleStarForIndex(index) {
+ const changeEls = this._getListItems();
+ if (index >= changeEls.length || !changeEls[index]) {
+ return;
+ }
+
+ const changeEl = changeEls[index];
+ const change = changeEl.change;
+ const newVal = !change.starred;
+ changeEl.set('change.starred', newVal);
+ this.$.restAPI.saveChangeStarred(change._number, newVal);
+ },
+
_changeURLForIndex(index) {
const changeEls = this._getListItems();
if (index < changeEls.length && changeEls[index]) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
index 378cd7b..a570bd5 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
@@ -115,6 +115,8 @@
assert.equal(element._computeLabelShortcut('Code-Review'), 'CR');
assert.equal(element._computeLabelShortcut('Verified'), 'V');
assert.equal(element._computeLabelShortcut('Library-Compliance'), 'LC');
+ assert.equal(element._computeLabelShortcut('PolyGerrit-Review'), 'PR');
+ assert.equal(element._computeLabelShortcut('polygerrit-review'), 'PR');
assert.equal(element._computeLabelShortcut(
'Some-Special-Label-7'), 'SSL7');
});
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index d751cff..18ba163 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -29,6 +29,7 @@
<link rel="import" href="../gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html">
<link rel="import" href="../gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html">
+<link rel="import" href="../gr-confirm-move-dialog/gr-confirm-move-dialog.html">
<link rel="import" href="../gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html">
<link rel="import" href="../gr-confirm-revert-dialog/gr-confirm-revert-dialog.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -124,6 +125,12 @@
on-cancel="_handleConfirmDialogCancel"
project="[[change.project]]"
hidden></gr-confirm-cherrypick-dialog>
+ <gr-confirm-move-dialog id="confirmMove"
+ class="confirmDialog"
+ on-confirm="_handleMoveConfirm"
+ on-cancel="_handleConfirmDialogCancel"
+ project="[[change.project]]"
+ hidden></gr-confirm-move-dialog>
<gr-confirm-revert-dialog id="confirmRevertDialog"
class="confirmDialog"
on-confirm="_handleRevertDialogConfirm"
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 3d5654e..329a343 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -51,6 +51,7 @@
ABANDON: 'abandon',
DELETE: '/',
IGNORE: 'ignore',
+ MOVE: 'move',
MUTE: 'mute',
PRIVATE: 'private',
PRIVATE_DELETE: 'private.delete',
@@ -75,6 +76,7 @@
abandon: 'Abandoning...',
cherrypick: 'Cherry-Picking...',
delete: 'Deleting...',
+ move: 'Moving..',
publish: 'Publishing...',
rebase: 'Rebasing...',
restore: 'Restoring...',
@@ -217,6 +219,10 @@
key: RevisionActions.CHERRYPICK,
},
{
+ type: ActionType.CHANGE,
+ key: ChangeActions.MOVE,
+ },
+ {
type: ActionType.REVISION,
key: RevisionActions.DOWNLOAD,
},
@@ -624,6 +630,9 @@
case ChangeActions.WIP:
this._handleWipTap();
break;
+ case ChangeActions.MOVE:
+ this._handleMoveTap();
+ break;
default:
this._fireAction(this._prependSlash(key), this.actions[key], false);
}
@@ -717,6 +726,25 @@
);
},
+ _handleMoveConfirm() {
+ const el = this.$.confirmMove;
+ if (!el.branch) {
+ this.fire('show-alert', {message: ERR_BRANCH_EMPTY});
+ return;
+ }
+ this.$.overlay.close();
+ el.hidden = true;
+ this._fireAction(
+ '/move',
+ this.actions.move,
+ false,
+ {
+ destination_branch: el.branch,
+ message: el.message,
+ }
+ );
+ },
+
_handleRevertDialogConfirm() {
const el = this.$.confirmRevertDialog;
this.$.overlay.close();
@@ -870,6 +898,12 @@
this._showActionDialog(this.$.confirmCherrypick);
},
+ _handleMoveTap() {
+ this.$.confirmMove.branch = '';
+ this.$.confirmMove.message = '';
+ this._showActionDialog(this.$.confirmMove);
+ },
+
_handleDownloadTap() {
this.fire('download-tap', null, {bubbles: false});
},
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index 0e1726b..1f96c31 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -398,6 +398,34 @@
});
});
+ suite('move change', () => {
+ let fireActionStub;
+
+ setup(() => {
+ fireActionStub = sandbox.stub(element, '_fireAction');
+ sandbox.stub(window, 'alert');
+ });
+
+ test('works', () => {
+ element._handleMoveTap();
+
+ element._handleMoveConfirm();
+ assert.equal(fireActionStub.callCount, 0);
+
+ element.$.confirmMove.branch = 'master';
+ element._handleMoveConfirm();
+ assert.equal(fireActionStub.callCount, 1);
+ });
+
+ test('branch name cleared when re-open move', () => {
+ const emptyBranchName = '';
+ element.$.confirmMove.branch = 'master';
+
+ element._handleMoveTap();
+ assert.equal(element.$.confirmMove.branch, emptyBranchName);
+ });
+ });
+
test('custom actions', done => {
// Add a button with the same key as a server-based one to ensure
// collisions are taken care of.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html
new file mode 100644
index 0000000..7260110
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html
@@ -0,0 +1,87 @@
+<!--
+Copyright (C) 2017 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.
+-->
+
+<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
+<link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+
+<dom-module id="gr-confirm-move-dialog">
+ <template>
+ <style include="shared-styles">
+ :host {
+ display: block;
+ width: 30em;
+ }
+ :host([disabled]) {
+ opacity: .5;
+ pointer-events: none;
+ }
+ label {
+ cursor: pointer;
+ }
+ iron-autogrow-textarea {
+ padding: 0;
+ }
+ .main {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ }
+ .main label,
+ .main input[type="text"] {
+ display: block;
+ font: inherit;
+ width: 100%;
+ }
+ .main .message {
+ border: groove;
+ width: 100%;
+ }
+ </style>
+ <gr-confirm-dialog
+ confirm-label="Move Change"
+ on-confirm="_handleConfirmTap"
+ on-cancel="_handleCancelTap">
+ <div class="header">Move Change to Another Branch</div>
+ <div class="main">
+ <label for="branchInput">
+ Move change to branch
+ </label>
+ <gr-autocomplete
+ id="branchInput"
+ text="{{branch}}"
+ query="[[_query]]"
+ placeholder="Destination branch">
+ </gr-autocomplete>
+ <label for="messageInput">
+ Move Change Commit Message
+ </label>
+ <iron-autogrow-textarea
+ id="messageInput"
+ class="message"
+ autocomplete="on"
+ rows="4"
+ max-rows="15"
+ bind-value="{{message}}"></iron-autogrow-textarea>
+ </div>
+ </gr-confirm-dialog>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+ <script src="gr-confirm-move-dialog.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
new file mode 100644
index 0000000..6d35dbf
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
@@ -0,0 +1,79 @@
+// Copyright (C) 2017 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.
+(function() {
+ 'use strict';
+
+ const SUGGESTIONS_LIMIT = 15;
+
+ Polymer({
+ is: 'gr-confirm-move-dialog',
+
+ /**
+ * Fired when the confirm button is pressed.
+ *
+ * @event confirm
+ */
+
+ /**
+ * Fired when the cancel button is pressed.
+ *
+ * @event cancel
+ */
+
+ properties: {
+ branch: String,
+ message: String,
+ project: String,
+ _query: {
+ type: Function,
+ value() {
+ return this._getProjectBranchesSuggestions.bind(this);
+ },
+ },
+ },
+
+ _handleConfirmTap(e) {
+ e.preventDefault();
+ this.fire('confirm', null, {bubbles: false});
+ },
+
+ _handleCancelTap(e) {
+ e.preventDefault();
+ this.fire('cancel', null, {bubbles: false});
+ },
+
+ _getProjectBranchesSuggestions(input) {
+ if (input.startsWith('refs/heads/')) {
+ input = input.substring('refs/heads/'.length);
+ }
+ return this.$.restAPI.getProjectBranches(
+ input, this.project, SUGGESTIONS_LIMIT).then(response => {
+ const branches = [];
+ let branch;
+ for (const key in response) {
+ if (!response.hasOwnProperty(key)) { continue; }
+ if (response[key].ref.startsWith('refs/heads/')) {
+ branch = response[key].ref.substring('refs/heads/'.length);
+ } else {
+ branch = response[key].ref;
+ }
+ branches.push({
+ name: branch,
+ });
+ }
+ return branches;
+ });
+ },
+ });
+})();
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html
new file mode 100644
index 0000000..caf61ba
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2017 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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-confirm-move-dialog</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-confirm-move-dialog.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-confirm-move-dialog></gr-confirm-move-dialog>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-confirm-move-dialog tests', () => {
+ let element;
+
+ setup(() => {
+ stub('gr-rest-api-interface', {
+ getProjectBranches(input) {
+ if (input.startsWith('test')) {
+ return Promise.resolve([
+ {
+ ref: 'refs/heads/test-branch',
+ revision: '67ebf73496383c6777035e374d2d664009e2aa5c',
+ can_delete: true,
+ },
+ ]);
+ } else {
+ return Promise.resolve({});
+ }
+ },
+ });
+ element = fixture('basic');
+ element.project = 'test-project';
+ });
+
+ test('with updated commit message', () => {
+ element.branch = 'master';
+ const myNewMessage = 'updated commit message';
+ element.message = myNewMessage;
+ flushAsynchronousOperations();
+ assert.equal(element.message, myNewMessage);
+ });
+
+ test('_getProjectBranchesSuggestions empty', done => {
+ element._getProjectBranchesSuggestions('nonexistent').then(branches => {
+ assert.equal(branches.length, 0);
+ done();
+ });
+ });
+
+ test('_getProjectBranchesSuggestions non-empty', done => {
+ element._getProjectBranchesSuggestions('test-branch').then(branches => {
+ assert.equal(branches.length, 1);
+ assert.equal(branches[0].name, 'test-branch');
+ done();
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 5ab161c..05e658f 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -133,7 +133,7 @@
'c': '_handleCKey',
'[': '_handleLeftBracketKey',
']': '_handleRightBracketKey',
- 'o': '_handleOKey',
+ 'o enter': '_handleOKey',
'n': '_handleNKey',
'p': '_handlePKey',
'r': '_handleRKey',
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
index 9325828..3f3350e 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
@@ -170,6 +170,10 @@
</td>
<td>Refresh list of changes</td>
</tr>
+ <tr>
+ <td><span class="key">s</span></td>
+ <td>Star (or unstar) change</td>
+ </tr>
</tbody>
<!-- Dashboard -->
<tbody hidden$="[[!_computeInView(view, 'dashboard')]]" hidden>
@@ -198,6 +202,10 @@
</td>
<td>Refresh list of changes</td>
</tr>
+ <tr>
+ <td><span class="key">s</span></td>
+ <td>Star (or unstar) change</td>
+ </tr>
</tbody>
<!-- Change View -->
<tbody hidden$="[[!_computeInView(view, 'change')]]" hidden>
@@ -243,7 +251,10 @@
<td>Select previous file</td>
</tr>
<tr>
- <td><span class="key">o</span></td>
+ <td>
+ <span class="key">Enter</span> or
+ <span class="key">o</span>
+ </td>
<td>Show selected file</td>
</tr>
<tr>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index d6e2e2f..713d771 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -72,7 +72,12 @@
/** @type {Function} */
_generateUrl: uninitialized,
- /** @type {Function} */
+ /**
+ * Handler for when a legacy URL can be upgraded. Router implementations
+ * may implement as a no-op if route upgrades are not needed.
+ *
+ * @type {Function}
+ */
_upgradeUrl: uninitialized,
_checkPatchRange(patchNum, basePatchNum) {
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index 2296567..61251af 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -64,6 +64,13 @@
const catchErrors = function(opt_context) {
const context = opt_context || window;
context.onerror = onError.bind(null, context.onerror);
+ context.addEventListener('unhandledrejection', e => {
+ const msg = e.reason.message;
+ const payload = {
+ error: e.reason,
+ };
+ GrReporting.prototype.reporter(ERROR.TYPE, ERROR.CATEGORY, msg, payload);
+ });
};
catchErrors();
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
index b99c5370..67cd329 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
@@ -183,7 +183,12 @@
setup(() => {
reporter = sandbox.stub(GrReporting.prototype, 'reporter');
- fakeWindow = {};
+ fakeWindow = {
+ handlers: {},
+ addEventListener(type, handler) {
+ this.handlers[type] = handler;
+ },
+ };
sandbox.stub(console, 'error');
window.GrReporting._catchErrors(fakeWindow);
});
@@ -204,6 +209,15 @@
test('prevent default event handler', () => {
assert.isTrue(emulateThrow());
});
+
+ test('unhandled rejection', () => {
+ fakeWindow.handlers['unhandledrejection']({
+ reason: {
+ message: 'bar',
+ },
+ });
+ assert.isTrue(reporter.calledWith('error', 'exception', 'bar'));
+ });
});
});
</script>
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index cc876fb..378744f 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -46,6 +46,13 @@
page.base(base);
}
+ /**
+ * While resolving Issue 6708, the need for some way to upgrade obsolete
+ * URLs in-place without page reloads became evident.
+ *
+ * This function aims to update the app params and the URL when the URL is
+ * found to be obsolete.
+ */
const upgradeUrl = params => {
const url = generateUrl(params);
if (url !== window.location.pathname) {
@@ -283,6 +290,51 @@
};
});
+ // Matches /admin/plugins[,<offset>][/].
+ page(/^\/admin\/plugins(,(\d+))?(\/)?$/, loadUser, data => {
+ restAPI.getLoggedIn().then(loggedIn => {
+ if (loggedIn) {
+ app.params = {
+ view: Gerrit.Nav.View.ADMIN,
+ adminView: 'gr-plugin-list',
+ offset: data.params[1] || 0,
+ filter: null,
+ };
+ } else {
+ redirectToLogin(data.canonicalPath);
+ }
+ });
+ });
+
+ page('/admin/plugins/q/filter::filter,:offset', loadUser, data => {
+ restAPI.getLoggedIn().then(loggedIn => {
+ if (loggedIn) {
+ app.params = {
+ view: Gerrit.Nav.View.ADMIN,
+ adminView: 'gr-plugin-list',
+ offset: data.params.offset,
+ filter: data.params.filter,
+ };
+ } else {
+ redirectToLogin(data.canonicalPath);
+ }
+ });
+ });
+
+ page('/admin/plugins/q/filter::filter', loadUser, data => {
+ restAPI.getLoggedIn().then(loggedIn => {
+ if (loggedIn) {
+ app.params = {
+ view: Gerrit.Nav.View.ADMIN,
+ adminView: 'gr-plugin-list',
+ filter: data.params.filter || null,
+ };
+ } else {
+ redirectToLogin(data.canonicalPath);
+ }
+ });
+ });
+
page(/^\/admin\/plugins(\/)?$/, loadUser, data => {
restAPI.getLoggedIn().then(loggedIn => {
if (loggedIn) {
@@ -335,7 +387,7 @@
// /c/<project>/+/<changeNum>/[<basePatchNum>..][<patchNum>]/[path].
// TODO(kaspern): Migrate completely to project based URLs, with backwards
// compatibility for change-only.
- page(/^\/c\/([^\/]+)\/\+\/(\d+)(\/?((\d+)(\.\.(\d+))?(\/(.+))?))?\/?$/,
+ page(/^\/c\/(.+)\/\+\/(\d+)(\/?((\d+)(\.\.(\d+))?(\/(.+))?))?\/?$/,
ctx => {
// Parameter order is based on the regex group number matched.
const params = {
@@ -395,7 +447,7 @@
data.params.view = Gerrit.Nav.View.AGREEMENTS;
app.params = data.params;
} else {
- page.redirect('/login/' + encodeURIComponent(data.canonicalPath));
+ redirectToLogin(data.canonicalPath);
}
});
});
@@ -408,7 +460,7 @@
emailToken: data.params[0],
};
} else {
- page.show('/login/' + encodeURIComponent(data.canonicalPath));
+ redirectToLogin(data.canonicalPath);
}
});
});
@@ -418,7 +470,7 @@
if (loggedIn) {
app.params = {view: Gerrit.Nav.View.SETTINGS};
} else {
- page.show('/login/' + encodeURIComponent(data.canonicalPath));
+ redirectToLogin(data.canonicalPath);
}
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 1865c4a..536bdb2 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -660,7 +660,7 @@
return this.fetchJSON(
`/projects/${encodeURIComponent(project)}/branches` +
- `?n=${projectsBranchesPerPage + 1}&s=${offset}` +
+ `?n=${projectsBranchesPerPage + 1}&S=${offset}` +
this._computeFilter(filter)
);
},
@@ -670,13 +670,18 @@
return this.fetchJSON(
`/projects/${encodeURIComponent(project)}/tags` +
- `?n=${projectsTagsPerPage + 1}&s=${offset}` +
+ `?n=${projectsTagsPerPage + 1}&S=${offset}` +
this._computeFilter(filter)
);
},
- getPlugins() {
- return this._fetchSharedCacheURL('/plugins/?all');
+ getPlugins(filter, pluginsPerPage, opt_offset) {
+ const offset = opt_offset || 0;
+
+ return this.fetchJSON(
+ `/plugins/?all&n=${pluginsPerPage + 1}&S=${offset}` +
+ this._computeFilter(filter)
+ );
},
getSuggestedGroups(inputVal, opt_n, opt_errFn, opt_ctx) {
diff --git a/polygerrit-ui/app/polylint_test.sh b/polygerrit-ui/app/polylint_test.sh
index 8f0d80f..ca9f9a9 100755
--- a/polygerrit-ui/app/polylint_test.sh
+++ b/polygerrit-ui/app/polylint_test.sh
@@ -15,6 +15,6 @@
exit 1
fi
-unzip polygerrit-ui/polygerrit_components.bower_components.zip -d polygerrit-ui/app
+unzip -o polygerrit-ui/polygerrit_components.bower_components.zip -d polygerrit-ui/app
${polylint_bin} --root polygerrit-ui/app --input elements/gr-app.html --b 'bower_components'
\ No newline at end of file
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 9678deb..355a7b6 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -51,6 +51,7 @@
'change/gr-comment-list/gr-comment-list_test.html',
'change/gr-commit-info/gr-commit-info_test.html',
'change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html',
+ 'change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html',
'change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html',
'change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html',
'change/gr-download-dialog/gr-download-dialog_test.html',
diff --git a/tools/bzl/gwt.bzl b/tools/bzl/gwt.bzl
index deeb5d5..ef182bf 100644
--- a/tools/bzl/gwt.bzl
+++ b/tools/bzl/gwt.bzl
@@ -52,10 +52,6 @@
"-XdisableCastChecking",
]
-PLUGIN_DEPS_NEVERLINK = [
- "//gerrit-plugin-api:lib-neverlink",
-]
-
GWT_PLUGIN_DEPS_NEVERLINK = [
"//gerrit-plugin-gwtui:gwtui-api-lib-neverlink",
"//lib/gwt:user-neverlink",
diff --git a/tools/bzl/plugin.bzl b/tools/bzl/plugin.bzl
index 59e7335..11ac572 100644
--- a/tools/bzl/plugin.bzl
+++ b/tools/bzl/plugin.bzl
@@ -5,12 +5,12 @@
"GWT_PLUGIN_DEPS_NEVERLINK",
"GWT_TRANSITIVE_DEPS",
"GWT_COMPILER_ARGS",
- "PLUGIN_DEPS_NEVERLINK",
"GWT_JVM_ARGS",
"gwt_binary",
)
PLUGIN_DEPS = ["//gerrit-plugin-api:lib"]
+PLUGIN_DEPS_NEVERLINK = ["//gerrit-plugin-api:lib-neverlink"]
PLUGIN_TEST_DEPS = [
"//gerrit-acceptance-framework:lib",