Merge "Add 'CherryPickOf' field for a change"
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index c97e4a4..036dfbf 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -342,8 +342,14 @@
=== Elasticsearch
Successfully running the Elasticsearch tests requires Docker, and
-may require setting the local
-link:https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html[virtual memory,role=external,window=_blank].
+may require setting the local virtual memory on
+link:https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html[linux,role=external,window=_blank] and
+link:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#_set_vm_max_map_count_to_at_least_262144[macOS,role=external,window=_blank].
+
+On macOS, if using link:https://docs.docker.com/docker-for-mac/[Docker Desktop,role=external,window=_blank],
+the effective memory value can be set in the Preferences, under the Advanced tab.
+The default value usually does not suffice and is causing premature container exits.
+That default is currently 2 GB and should be set to at least 5 (GB).
If Docker is not available, the Elasticsearch tests will be skipped.
Note that Bazel currently does not show
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index b373129..236792b 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -141,10 +141,10 @@
=== NoteDb
-* `notedb/update_latency`: NoteDb update latency by table.
-* `notedb/stage_update_latency`: Latency for staging updates to NoteDb by table.
-* `notedb/read_latency`: NoteDb read latency by table.
-* `notedb/parse_latency`: NoteDb parse latency by table.
+* `notedb/update_latency`: NoteDb update latency for changes.
+* `notedb/stage_update_latency`: Latency for staging change updates to NoteDb.
+* `notedb/read_latency`: NoteDb read latency for changes.
+* `notedb/parse_latency`: NoteDb parse latency for changes.
* `notedb/external_id_cache_load_count`: Total number of times the external ID
cache loader was called.
* `notedb/external_id_partial_read_latency`: Latency for generating a new external ID
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 27a31b1..e3e9009 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1251,7 +1251,6 @@
)]}'
{
"changes_per_page": 25,
- "download_command": "CHECKOUT",
"date_format": "STD",
"time_format": "HHMM_12",
"diff_view": "SIDE_BY_SIDE",
@@ -1305,7 +1304,6 @@
{
"changes_per_page": 50,
"expand_inline_diffs": true,
- "download_command": "CHECKOUT",
"date_format": "STD",
"time_format": "HHMM_12",
"size_bar_in_change_table": true,
@@ -1353,7 +1351,6 @@
{
"changes_per_page": 50,
"expand_inline_diffs": true,
- "download_command": "CHECKOUT",
"date_format": "STD",
"time_format": "HHMM_12",
"size_bar_in_change_table": true,
@@ -1415,7 +1412,6 @@
)]}'
{
"context": 10,
- "theme": "DEFAULT",
"ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
@@ -1446,7 +1442,6 @@
{
"context": 10,
- "theme": "ECLIPSE",
"ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
@@ -1472,7 +1467,6 @@
)]}'
{
"context": 10,
- "theme": "ECLIPSE",
"ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
@@ -1509,8 +1503,6 @@
)]}'
{
- "theme": "ECLIPSE",
- "key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"indent_unit": 2,
@@ -1542,8 +1534,6 @@
Content-Type: application/json;charset=UTF-8
{
- "theme": "ECLIPSE",
- "key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"indent_unit": 2,
@@ -1570,8 +1560,6 @@
)]}'
{
- "theme": "ECLIPSE",
- "key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"cursor_blink_rate": 530,
@@ -2392,13 +2380,18 @@
The `ContributorAgreementInfo` entity contains information about a
contributor agreement.
-[options="header",cols="1,6"]
-|=================================
-|Field Name |Description
-|`name` |The name of the agreement.
-|`description` |The description of the agreement.
-|`url` |The URL of the agreement.
-|=================================
+[options="header",cols="1,^1,5"]
+|================================
+|Field Name ||Description
+|`name` ||The unique name of the contributor agreement.
+|`description` ||The description of the contributor agreement.
+|`url` ||The URL of the contributor agreement.
+|`auto_verify_group`|optional|
+The group to which a user that signs the contributor agreement online
+is added automatically as a link:rest-api-groups.html#group-info[
+GroupInfo] entity. If not set, users cannot sign the contributor
+agreement online.
+|================================
[[contributor-agreement-input]]
=== ContributorAgreementInput
@@ -2452,9 +2445,6 @@
|Field Name ||Description
|`context` ||
The number of lines of context when viewing a patch.
-|`theme` ||
-The CodeMirror theme name in upper case, for example `DEFAULT`. All the themes
-from the CodeMirror release that Gerrit is using are available.
|`expand_all_comments` |not set if `false`|
Whether all inline comments should be automatically expanded.
|`ignore_whitespace` ||
@@ -2576,12 +2566,6 @@
[options="header",cols="1,^1,5"]
|===========================================
|Field Name ||Description
-|`theme` ||
-The CodeMirror theme name in upper case, for example `DEFAULT`. All the themes
-from the CodeMirror release that Gerrit is using are available.
-|`key_map_type` ||
-The CodeMirror key map. Currently only a subset of key maps are
-supported: `DEFAULT`, `EMACS`, `SUBLIME`, `VIM`.
|`tab_size` ||
Number of spaces that should be used to display one tab.
|`line_length` ||
@@ -2737,8 +2721,6 @@
The type of download URL the user prefers to use. May be any key from
the `schemes` map in
link:rest-api-config.html#download-info[DownloadInfo].
-|`download_command` ||
-The type of download command the user prefers to use.
|`date_format` ||
The format to display the date in.
Allowed values are `STD`, `US`, `ISO`, `EURO`, `UK`.
@@ -2799,8 +2781,6 @@
(PolyGerrit only).
|`download_scheme` |optional|
The type of download URL the user prefers to use.
-|`download_command` |optional|
-The type of download command the user prefers to use.
|`date_format` |optional|
The format to display the date in.
Allowed values are `STD`, `US`, `ISO`, `EURO`, `UK`.
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index d5abbe3..73ff085 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -73,7 +73,9 @@
Queries changes visible to the caller. The
link:user-search.html#_search_operators[query string] must be provided
by the `q` parameter. The `n` parameter can be used to limit the
-returned results.
+returned results. The `no-limit` parameter can be used remove the default
+limit on queries and return all results. This might not be supported by
+all index backends.
As result a list of link:#change-info[ChangeInfo] entries is returned.
The change output is sorted by the last update time, most recently
@@ -369,11 +371,6 @@
as link:#tracking-id-info[TrackingIdInfo].
--
-[[no-limit]]
---
-* `NO-LIMIT`: Return all results
---
-
.Request
----
GET /changes/?q=97&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES&o=DOWNLOAD_COMMANDS HTTP/1.0
@@ -1520,8 +1517,14 @@
Creates open revert changes for all of the changes of a certain submission.
The subject of each revert change will be 'Revert "<subject-of-reverted-change"'.
-If the subject is above 63 characters, the subject will be cut to 59 characters
-with "..." in the end.
+If the subject is above 60 characters, the subject will be cut to 56 characters
+with "..." in the end. However, whenever reverting the submission of a revert
+submission, the subject will be shortened from
+'Revert "Revert "<subject-of-reverted-change""' to
+'Revert^2 "<subject-of-reverted-change"'. Also, for every future revert submission,
+the only difference in the subject will be the number of the revert (instead of
+Revert^2 the subject will change to Revert^3 and so on).
+There are no guarantees about the subjects if the users change the default subjects.
Details for the revert can be specified in the request body inside a link:#revert-input[
RevertInput] The topic of all created revert changes will be
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index affe1ea..e9e3b94 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1477,8 +1477,8 @@
|`git_basic_auth_policy` |optional|
The link:config-gerrit.html#auth.gitBasicAuthPolicy[policy] to authenticate
Git over HTTP and REST API requests when
-link:config-gerrit.html#auth.type[authentication type] is `LDAP`.
-Can be `HTTP`, `LDAP` or `HTTP_LDAP`.
+link:config-gerrit.html#auth.type[authentication type] is `LDAP`,
+`LDAP_BIND` or `OAUTH`. Can be `HTTP`, `LDAP`, `HTTP_LDAP` or `OAUTH`.
|==========================================
[[cache-info]]
diff --git a/e2e-tests/load-tests/Dockerfile b/e2e-tests/load-tests/Dockerfile
new file mode 100644
index 0000000..ceae672
--- /dev/null
+++ b/e2e-tests/load-tests/Dockerfile
@@ -0,0 +1,32 @@
+FROM denvazh/gatling:3.2.1
+
+ARG gatling_git_version=1.0.9
+RUN apk add --no-cache maven
+RUN mvn dependency:get \
+ -DgroupId=com.gerritforge \
+ -DartifactId=gatling-git_2.12 \
+ -Dversion=$gatling_git_version \
+ -Dtype=pom
+RUN mvn dependency:copy-dependencies \
+ -f /root/.m2/repository/com/gerritforge/gatling-git_2.12/$gatling_git_version/gatling-git_2.12-$gatling_git_version.pom \
+ -DoutputDirectory=/opt/gatling/lib/
+RUN mvn dependency:get \
+ -Dartifact=com.gerritforge:gatling-git_2.12:$gatling_git_version:jar \
+ -Ddest=/opt/gatling/lib/gatling-git.jar
+
+ARG gatling_home=/home/gatling
+RUN addgroup -g 1000 -S appgroup && \
+ adduser -u 1000 -S gatling -G appgroup -h $gatling_home
+RUN cp -R /opt/gatling/* $gatling_home && \
+ chown -R gatling:appgroup $gatling_home
+
+WORKDIR $gatling_home
+USER gatling
+
+COPY ./src/test/scala/com/google/gerrit/scenarios $gatling_home/user-files/simulations
+COPY ./src/test/resources/application.conf $gatling_home/conf
+COPY ./src/test/resources/data $gatling_home/user-files/resources/data
+
+ENV GATLING_HOME=$gatling_home
+
+ENTRYPOINT ["/home/gatling/bin/gatling.sh"]
diff --git a/e2e-tests/load-tests/README.md b/e2e-tests/load-tests/README.md
new file mode 100644
index 0000000..534fde5
--- /dev/null
+++ b/e2e-tests/load-tests/README.md
@@ -0,0 +1,11 @@
+# How to build the Docker image
+
+```$shell
+docker build . -t e2e-tests
+```
+
+# How to run a test
+
+```$shell
+docker run -it e2e-tests -s com.google.gerrit.scenarios.ReplayRecordsFromFeederScenario
+```
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index fd61aa5..a933dac 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -124,6 +124,7 @@
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.restapi.change.Revisions;
import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.util.git.DelegateSystemReader;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.FakeEmailSender;
import com.google.gerrit.testing.FakeEmailSender.Message;
@@ -135,6 +136,7 @@
import com.google.inject.Provider;
import com.jcraft.jsch.KeyPair;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -170,11 +172,14 @@
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.TransportBundleStream;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@@ -289,6 +294,7 @@
private ProjectResetter resetter;
private List<Repository> toClose;
private String systemTimeZone;
+ private SystemReader oldSystemReader;
@Before
public void clearSender() {
@@ -385,6 +391,10 @@
}
protected void beforeTest(Description description) throws Exception {
+ // SystemReader must be overridden before creating any repos, since they read the user/system
+ // configs at initialization time, and are then stored in the RepositoryCache forever.
+ oldSystemReader = setFakeSystemReader(temporaryFolder.getRoot());
+
this.description = description;
GerritServer.Description classDesc =
GerritServer.Description.forTestClass(description, configName);
@@ -447,6 +457,28 @@
methodDesc.useSystemTime(), methodDesc.useClockStep(), methodDesc.useTimezone());
}
+ private static SystemReader setFakeSystemReader(File tempDir) {
+ SystemReader oldSystemReader = SystemReader.getInstance();
+ SystemReader.setInstance(
+ new DelegateSystemReader(oldSystemReader) {
+ @Override
+ public FileBasedConfig openJGitConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "jgit.config"), FS.detect());
+ }
+
+ @Override
+ public FileBasedConfig openUserConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "user.config"), FS.detect());
+ }
+
+ @Override
+ public FileBasedConfig openSystemConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "system.config"), FS.detect());
+ }
+ });
+ return oldSystemReader;
+ }
+
private void setTimeSettings(
boolean useSystemTime,
@Nullable UseClockStep useClockStep,
@@ -602,6 +634,8 @@
server.close();
server = null;
}
+ SystemReader.setInstance(oldSystemReader);
+ oldSystemReader = null;
}
protected void closeSsh() {
diff --git a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index a095daa..1d6f51f 100644
--- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -144,6 +144,11 @@
SystemReader.setInstance(
new DelegateSystemReader(oldSystemReader) {
@Override
+ public FileBasedConfig openJGitConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "jgit.config"), FS.detect());
+ }
+
+ @Override
public FileBasedConfig openUserConfig(Config parent, FS fs) {
return new FileBasedConfig(parent, new File(tempDir, "user.config"), FS.detect());
}
diff --git a/java/com/google/gerrit/acceptance/UseLocalDisk.java b/java/com/google/gerrit/acceptance/UseLocalDisk.java
index e177bb4..192caa0 100644
--- a/java/com/google/gerrit/acceptance/UseLocalDisk.java
+++ b/java/com/google/gerrit/acceptance/UseLocalDisk.java
@@ -21,6 +21,15 @@
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
+/**
+ * Annotation to mark tests that require a local disk for the execution.
+ *
+ * <p>Tests that do not have this annotation are executed in memory.
+ *
+ * <p>Using this annotation makes the execution of the test more expensive/slower. This is why it
+ * should only be used if the test requires a local disk (e.g. if the test triggers the Git garbage
+ * collection functionality which only works with a local disk).
+ */
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface UseLocalDisk {}
diff --git a/java/com/google/gerrit/acceptance/UseSsh.java b/java/com/google/gerrit/acceptance/UseSsh.java
index 5509140..12a9977 100644
--- a/java/com/google/gerrit/acceptance/UseSsh.java
+++ b/java/com/google/gerrit/acceptance/UseSsh.java
@@ -21,6 +21,14 @@
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
+/**
+ * Annotation to mark SSH tests.
+ *
+ * <p>When running tests the SSH functionality is disabled unless the {@link UseSsh} annotation is
+ * used.
+ *
+ * <p>SSH tests can be skipped when executing tests (see {@link com.google.gerrit.testing.SshMode}).
+ */
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface UseSsh {}
diff --git a/java/com/google/gerrit/acceptance/rest/PluginCollection.java b/java/com/google/gerrit/acceptance/rest/PluginCollection.java
index c0daf93..dc40cde 100644
--- a/java/com/google/gerrit/acceptance/rest/PluginCollection.java
+++ b/java/com/google/gerrit/acceptance/rest/PluginCollection.java
@@ -45,7 +45,7 @@
@Override
public PluginResource parse(ConfigResource parent, IdString id)
throws ResourceNotFoundException, Exception {
- throw new ResourceNotFoundException();
+ throw new ResourceNotFoundException(id);
}
@Override
diff --git a/java/com/google/gerrit/common/data/GroupDetail.java b/java/com/google/gerrit/common/data/GroupDetail.java
deleted file mode 100644
index 991d5df..0000000
--- a/java/com/google/gerrit/common/data/GroupDetail.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2008 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.common.data;
-
-import com.google.gerrit.entities.Account;
-import com.google.gerrit.entities.AccountGroup;
-import java.util.Set;
-
-public class GroupDetail {
- private Set<Account.Id> members;
- private Set<AccountGroup.UUID> includes;
-
- public GroupDetail(Set<Account.Id> members, Set<AccountGroup.UUID> includes) {
- this.members = members;
- this.includes = includes;
- }
-
- public Set<Account.Id> getMembers() {
- return members;
- }
-
- public Set<AccountGroup.UUID> getIncludes() {
- return includes;
- }
-}
diff --git a/java/com/google/gerrit/common/data/GroupInfo.java b/java/com/google/gerrit/common/data/GroupInfo.java
deleted file mode 100644
index 5c36104..0000000
--- a/java/com/google/gerrit/common/data/GroupInfo.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2011 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.common.data;
-
-import com.google.gerrit.entities.AccountGroup;
-
-/** Summary information about an {@link AccountGroup}, for simple tabular displays. */
-public class GroupInfo {
- protected AccountGroup.UUID uuid;
- protected String name;
- protected String description;
- protected String url;
-
- protected GroupInfo() {}
-
- /**
- * Create an anonymous group info, when only the id is known.
- *
- * <p>This constructor should only be a last-ditch effort, when the usual group lookup has failed
- * and a stale group id has been discovered in the data store.
- */
- public GroupInfo(AccountGroup.UUID uuid) {
- this.uuid = uuid;
- }
-
- /**
- * Create a group description from a real data store record.
- *
- * @param a the data store record holding the specific group details.
- */
- public GroupInfo(GroupDescription.Basic a) {
- uuid = a.getGroupUUID();
- name = a.getName();
- url = a.getUrl();
-
- if (a instanceof GroupDescription.Internal) {
- description = ((GroupDescription.Internal) a).getDescription();
- }
- }
-
- /** @return the unique local id of the group */
- public AccountGroup.UUID getId() {
- return uuid;
- }
-
- /** @return the name of the group; null if not supplied */
- public String getName() {
- return name;
- }
-
- /** @return the description of the group; null if not supplied */
- public String getDescription() {
- return description;
- }
-
- public String getUrl() {
- return url;
- }
-}
diff --git a/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java b/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
index 522ec88..ed01a4d 100644
--- a/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
@@ -60,7 +60,6 @@
public Boolean hideEmptyPane;
public Boolean matchBrackets;
public Boolean lineWrapping;
- public Theme theme;
public Whitespace ignoreWhitespace;
public Boolean retainHeader;
public Boolean skipDeleted;
@@ -88,7 +87,6 @@
i.hideEmptyPane = false;
i.matchBrackets = false;
i.lineWrapping = false;
- i.theme = Theme.DEFAULT;
i.ignoreWhitespace = Whitespace.IGNORE_NONE;
i.retainHeader = false;
i.skipDeleted = false;
diff --git a/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java b/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
index 7ab22e1..6672cb1 100644
--- a/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
@@ -30,8 +30,6 @@
public Boolean indentWithTabs;
public Boolean autoCloseBrackets;
public Boolean showBase;
- public Theme theme;
- public KeyMapType keyMapType;
public static EditPreferencesInfo defaults() {
EditPreferencesInfo i = new EditPreferencesInfo();
@@ -49,8 +47,6 @@
i.indentWithTabs = false;
i.autoCloseBrackets = false;
i.showBase = false;
- i.theme = Theme.DEFAULT;
- i.keyMapType = KeyMapType.DEFAULT;
return i;
}
}
diff --git a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
index 458bcf5..212f6da 100644
--- a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
@@ -124,8 +124,6 @@
public Integer changesPerPage;
/** Type of download URL the user prefers to use. */
public String downloadScheme;
- /** Type of download command the user prefers to use. */
- public DownloadCommand downloadCommand;
public DateFormat dateFormat;
public TimeFormat timeFormat;
@@ -184,7 +182,6 @@
GeneralPreferencesInfo p = new GeneralPreferencesInfo();
p.changesPerPage = DEFAULT_PAGESIZE;
p.downloadScheme = null;
- p.downloadCommand = DownloadCommand.CHECKOUT;
p.dateFormat = DateFormat.STD;
p.timeFormat = TimeFormat.HHMM_12;
p.expandInlineDiffs = false;
diff --git a/java/com/google/gerrit/extensions/client/KeyMapType.java b/java/com/google/gerrit/extensions/client/KeyMapType.java
deleted file mode 100644
index 9c85ac7..0000000
--- a/java/com/google/gerrit/extensions/client/KeyMapType.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.extensions.client;
-
-public enum KeyMapType {
- DEFAULT,
- EMACS,
- SUBLIME,
- VIM
-}
diff --git a/java/com/google/gerrit/extensions/client/Theme.java b/java/com/google/gerrit/extensions/client/Theme.java
deleted file mode 100644
index d7a5b80..0000000
--- a/java/com/google/gerrit/extensions/client/Theme.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.extensions.client;
-
-public enum Theme {
- // Light themes
- DEFAULT,
- DAY_3024,
- DUOTONE_LIGHT,
- BASE16_LIGHT,
- ECLIPSE,
- ELEGANT,
- MDN_LIKE,
- NEAT,
- NEO,
- PARAISO_LIGHT,
- SOLARIZED,
- TTCN,
- XQ_LIGHT,
- YETI,
-
- // Dark themes
- NIGHT_3024,
- ABCDEF,
- AMBIANCE,
- BASE16_DARK,
- BESPIN,
- BLACKBOARD,
- COBALT,
- COLORFORTH,
- DRACULA,
- DUOTONE_DARK,
- ERLANG_DARK,
- HOPSCOTCH,
- ICECODER,
- ISOTOPE,
- LESSER_DARK,
- LIQUIBYTE,
- MATERIAL,
- MBO,
- MIDNIGHT,
- MONOKAI,
- NIGHT,
- PARAISO_DARK,
- PASTEL_ON_DARK,
- RAILSCASTS,
- RUBYBLUE,
- SETI,
- THE_MATRIX,
- TOMORROW_NIGHT_BRIGHT,
- TOMORROW_NIGHT_EIGHTIES,
- TWILIGHT,
- VIBRANT_INK,
- XQ_DARK,
- ZENBURN;
-
- public boolean isDark() {
- switch (this) {
- case ABCDEF:
- case AMBIANCE:
- case BASE16_DARK:
- case BESPIN:
- case BLACKBOARD:
- case COBALT:
- case COLORFORTH:
- case DRACULA:
- case DUOTONE_DARK:
- case ERLANG_DARK:
- case HOPSCOTCH:
- case ICECODER:
- case ISOTOPE:
- case LESSER_DARK:
- case LIQUIBYTE:
- case MATERIAL:
- case MBO:
- case MIDNIGHT:
- case MONOKAI:
- case NIGHT:
- case NIGHT_3024:
- case PARAISO_DARK:
- case PASTEL_ON_DARK:
- case RAILSCASTS:
- case RUBYBLUE:
- case SETI:
- case THE_MATRIX:
- case TOMORROW_NIGHT_BRIGHT:
- case TOMORROW_NIGHT_EIGHTIES:
- case TWILIGHT:
- case VIBRANT_INK:
- case XQ_DARK:
- case ZENBURN:
- return true;
- case BASE16_LIGHT:
- case DEFAULT:
- case DAY_3024:
- case DUOTONE_LIGHT:
- case ECLIPSE:
- case ELEGANT:
- case MDN_LIKE:
- case NEAT:
- case NEO:
- case PARAISO_LIGHT:
- case SOLARIZED:
- case TTCN:
- case XQ_LIGHT:
- case YETI:
- default:
- return false;
- }
- }
-}
diff --git a/java/com/google/gerrit/extensions/common/AccountDetailInfo.java b/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
index 3ffa97a..14027ac 100644
--- a/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
@@ -16,6 +16,15 @@
import java.sql.Timestamp;
+/**
+ * Representation of a (detailed) account in the REST API.
+ *
+ * <p>This class determines the JSON format of (detailed) accounts in the REST API.
+ *
+ * <p>This class extends {@link AccountInfo} (which defines fields for account properties that are
+ * frequently used) and provides additional fields for account details which are needed only in some
+ * cases.
+ */
public class AccountDetailInfo extends AccountInfo {
public Timestamp registeredOn;
diff --git a/java/com/google/gerrit/extensions/common/AccountInfo.java b/java/com/google/gerrit/extensions/common/AccountInfo.java
index d1bbe88..3dcee71 100644
--- a/java/com/google/gerrit/extensions/common/AccountInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountInfo.java
@@ -18,6 +18,14 @@
import java.util.List;
import java.util.Objects;
+/**
+ * Representation of an account in the REST API.
+ *
+ * <p>This class determines the JSON format of accounts in the REST API.
+ *
+ * <p>This class defines fields for account properties that are frequently used. Additional fields
+ * are defined in {@link AccountDetailInfo}.
+ */
public class AccountInfo {
public Integer _accountId;
public String name;
diff --git a/java/com/google/gerrit/extensions/common/AccountsInfo.java b/java/com/google/gerrit/extensions/common/AccountsInfo.java
index e1c2825..a2a4826 100644
--- a/java/com/google/gerrit/extensions/common/AccountsInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountsInfo.java
@@ -14,6 +14,12 @@
package com.google.gerrit.extensions.common;
+/**
+ * Representation of account-related server configuration in the REST API.
+ *
+ * <p>This class determines the JSON format of account-related server configuration in the REST API.
+ */
public class AccountsInfo {
+ /** The value of the {@code accounts.visibility} parameter in {@code gerrit.config}. */
public AccountVisibility visibility;
}
diff --git a/java/com/google/gerrit/extensions/common/AgreementInfo.java b/java/com/google/gerrit/extensions/common/AgreementInfo.java
index 4242fcd..c092463 100644
--- a/java/com/google/gerrit/extensions/common/AgreementInfo.java
+++ b/java/com/google/gerrit/extensions/common/AgreementInfo.java
@@ -14,9 +14,25 @@
package com.google.gerrit.extensions.common;
+/**
+ * Representation of a contributor agreement in the REST API.
+ *
+ * <p>This class determines the JSON format of a contributor agreement in the REST API.
+ */
public class AgreementInfo {
+ /** The unique name of the contributor agreement. */
public String name;
+
+ /** The description of the contributor agreement. */
public String description;
+
+ /** The URL of the contributor agreement. */
public String url;
+
+ /**
+ * Group to which a user that signs the contributor agreement online is added automatically.
+ *
+ * <p>May be {@code null}. In this case users cannot sign the contributor agreement online.
+ */
public GroupInfo autoVerifyGroup;
}
diff --git a/java/com/google/gerrit/extensions/restapi/MethodNotAllowedException.java b/java/com/google/gerrit/extensions/restapi/MethodNotAllowedException.java
index 61c6345..2d59f27 100644
--- a/java/com/google/gerrit/extensions/restapi/MethodNotAllowedException.java
+++ b/java/com/google/gerrit/extensions/restapi/MethodNotAllowedException.java
@@ -18,10 +18,6 @@
public class MethodNotAllowedException extends RestApiException {
private static final long serialVersionUID = 1L;
- public MethodNotAllowedException() {
- super();
- }
-
/** @param msg error text for client describing why the method is not allowed. */
public MethodNotAllowedException(String msg) {
super(msg);
diff --git a/java/com/google/gerrit/extensions/restapi/PreconditionFailedException.java b/java/com/google/gerrit/extensions/restapi/PreconditionFailedException.java
index 1fa6cd0..b8c9d38 100644
--- a/java/com/google/gerrit/extensions/restapi/PreconditionFailedException.java
+++ b/java/com/google/gerrit/extensions/restapi/PreconditionFailedException.java
@@ -18,8 +18,6 @@
public class PreconditionFailedException extends RestApiException {
private static final long serialVersionUID = 1L;
- public PreconditionFailedException() {}
-
/** @param msg message to return to the client describing the error. */
public PreconditionFailedException(String msg) {
super(msg);
diff --git a/java/com/google/gerrit/extensions/restapi/RestApiException.java b/java/com/google/gerrit/extensions/restapi/RestApiException.java
index f3d7dec..de141be 100644
--- a/java/com/google/gerrit/extensions/restapi/RestApiException.java
+++ b/java/com/google/gerrit/extensions/restapi/RestApiException.java
@@ -21,13 +21,17 @@
private static final long serialVersionUID = 1L;
private CacheControl caching = CacheControl.NONE;
- public RestApiException() {}
+ public static RestApiException wrap(String msg, Exception e) {
+ return new RestApiException(msg, e);
+ }
- public RestApiException(String msg) {
+ protected RestApiException() {}
+
+ protected RestApiException(String msg) {
super(msg);
}
- public RestApiException(String msg, Throwable cause) {
+ protected RestApiException(String msg, Throwable cause) {
super(msg, cause);
}
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index f390ebc..de39472 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -1237,7 +1237,7 @@
return obj;
}
}
- throw new MethodNotAllowedException();
+ throw new MethodNotAllowedException("raw input not supported");
}
private Object parseString(String value, Type type)
diff --git a/java/com/google/gerrit/index/project/IndexedProjectQuery.java b/java/com/google/gerrit/index/project/IndexedProjectQuery.java
index cdfeabd..5fc74ca 100644
--- a/java/com/google/gerrit/index/project/IndexedProjectQuery.java
+++ b/java/com/google/gerrit/index/project/IndexedProjectQuery.java
@@ -22,6 +22,10 @@
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
+/**
+ * Wrapper around {@link Predicate}s that are returned by the {@link
+ * com.google.gerrit.index.IndexRewriter}. See {@link IndexedQuery}.
+ */
public class IndexedProjectQuery extends IndexedQuery<Project.NameKey, ProjectData>
implements DataSource<ProjectData> {
diff --git a/java/com/google/gerrit/index/project/ProjectData.java b/java/com/google/gerrit/index/project/ProjectData.java
index 2bf9a4b5..c70b823 100644
--- a/java/com/google/gerrit/index/project/ProjectData.java
+++ b/java/com/google/gerrit/index/project/ProjectData.java
@@ -23,6 +23,11 @@
import java.util.List;
import java.util.Optional;
+/**
+ * Representation of a Gerrit project in the project index.
+ *
+ * <p>Includes information about all parent projects.
+ */
public class ProjectData {
private final Project project;
private final Optional<ProjectData> parent;
diff --git a/java/com/google/gerrit/index/project/ProjectIndex.java b/java/com/google/gerrit/index/project/ProjectIndex.java
index 3e99d55..b2ddaff 100644
--- a/java/com/google/gerrit/index/project/ProjectIndex.java
+++ b/java/com/google/gerrit/index/project/ProjectIndex.java
@@ -19,6 +19,10 @@
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.query.Predicate;
+/**
+ * Index for Gerrit projects (repositories). This class is mainly used for typing the generic parent
+ * class that contains actual implementations.
+ */
public interface ProjectIndex extends Index<Project.NameKey, ProjectData> {
public interface Factory
diff --git a/java/com/google/gerrit/index/project/ProjectIndexCollection.java b/java/com/google/gerrit/index/project/ProjectIndexCollection.java
index 30227a3..7ff23c5 100644
--- a/java/com/google/gerrit/index/project/ProjectIndexCollection.java
+++ b/java/com/google/gerrit/index/project/ProjectIndexCollection.java
@@ -19,6 +19,7 @@
import com.google.gerrit.index.IndexCollection;
import com.google.inject.Singleton;
+/** Collection of active project indices. See {@link IndexCollection} for details on collections. */
@Singleton
public class ProjectIndexCollection
extends IndexCollection<Project.NameKey, ProjectData, ProjectIndex> {
diff --git a/java/com/google/gerrit/index/project/ProjectIndexRewriter.java b/java/com/google/gerrit/index/project/ProjectIndexRewriter.java
index 9e2bbdc..230161b 100644
--- a/java/com/google/gerrit/index/project/ProjectIndexRewriter.java
+++ b/java/com/google/gerrit/index/project/ProjectIndexRewriter.java
@@ -23,6 +23,7 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
+/** Rewriter for the project index. See {@link IndexRewriter} for details. */
@Singleton
public class ProjectIndexRewriter implements IndexRewriter<ProjectData> {
private final ProjectIndexCollection indexes;
diff --git a/java/com/google/gerrit/index/project/ProjectIndexer.java b/java/com/google/gerrit/index/project/ProjectIndexer.java
index bd5efeb2..34e8075 100644
--- a/java/com/google/gerrit/index/project/ProjectIndexer.java
+++ b/java/com/google/gerrit/index/project/ProjectIndexer.java
@@ -16,6 +16,7 @@
import com.google.gerrit.entities.Project;
+/** Interface for indexing a Gerrit project. */
public interface ProjectIndexer {
/**
diff --git a/java/com/google/gerrit/index/project/ProjectPredicate.java b/java/com/google/gerrit/index/project/ProjectPredicate.java
index 4926eef..11875ef 100644
--- a/java/com/google/gerrit/index/project/ProjectPredicate.java
+++ b/java/com/google/gerrit/index/project/ProjectPredicate.java
@@ -17,6 +17,7 @@
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.query.IndexPredicate;
+/** Predicate that is mapped to a field in the project index. */
public class ProjectPredicate extends IndexPredicate<ProjectData> {
public ProjectPredicate(FieldDef<ProjectData, ?> def, String value) {
super(def, value);
diff --git a/java/com/google/gerrit/index/query/IndexPredicate.java b/java/com/google/gerrit/index/query/IndexPredicate.java
index 7811a32..aac6682 100644
--- a/java/com/google/gerrit/index/query/IndexPredicate.java
+++ b/java/com/google/gerrit/index/query/IndexPredicate.java
@@ -17,7 +17,7 @@
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.FieldType;
-/** Index-aware predicate that includes a field type annotation. */
+/** Predicate that is mapped to a field in the index. */
public abstract class IndexPredicate<I> extends OperatorPredicate<I> {
private final FieldDef<I, ?> def;
diff --git a/java/com/google/gerrit/server/DynamicOptions.java b/java/com/google/gerrit/server/DynamicOptions.java
index 44d3493..41dc082 100644
--- a/java/com/google/gerrit/server/DynamicOptions.java
+++ b/java/com/google/gerrit/server/DynamicOptions.java
@@ -80,7 +80,7 @@
* .to(MyOptionsClassNameProvider.class);
*
* static class MyOptionsClassNameProvider implements DynamicOptions.ClassNameProvider {
- * @Override
+ * {@literal @}Override
* public String getClassName() {
* return "com.googlesource.gerrit.plugins.myplugin.CommandOptions";
* }
@@ -107,11 +107,11 @@
* .to(MyOptionsModulesClassNamesProvider.class);
*
* static class MyOptionsModulesClassNamesProvider implements DynamicOptions.ClassNameProvider {
- * @Override
+ * {@literal @}Override
* public String getClassName() {
* return "com.googlesource.gerrit.plugins.myplugin.CommandOptions";
* }
- * @Override
+ * {@literal @}Override
* public Iterable<String> getModulesClassNames()() {
* return "com.googlesource.gerrit.plugins.myplugin.MyOptionsModule";
* }
diff --git a/java/com/google/gerrit/server/account/GroupControl.java b/java/com/google/gerrit/server/account/GroupControl.java
index 2228525..64fd7c6 100644
--- a/java/com/google/gerrit/server/account/GroupControl.java
+++ b/java/com/google/gerrit/server/account/GroupControl.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.account;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.AccountGroup;
@@ -29,6 +30,7 @@
/** Access control management for a group of accounts managed in Gerrit. */
public class GroupControl {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Singleton
public static class GenericFactory {
@@ -111,15 +113,46 @@
/** Can this user see this group exists? */
public boolean isVisible() {
- /* Check for canAdministrateServer may seem redundant, but allows
- * for visibility of all groups that are not an internal group to
- * server administrators.
- */
- return user.isInternalUser()
- || groupBackend.isVisibleToAll(group.getGroupUUID())
- || user.getEffectiveGroups().contains(group.getGroupUUID())
- || isOwner()
- || canAdministrateServer();
+ if (user.isInternalUser()) {
+ logger.atFine().log(
+ "group %s is visible to internal user %s",
+ group.getGroupUUID().get(), user.getLoggableName());
+ return true;
+ }
+
+ if (groupBackend.isVisibleToAll(group.getGroupUUID())) {
+ logger.atFine().log(
+ "group %s is visible to user %s (group is visible to all users)",
+ group.getGroupUUID().get(), user.getLoggableName());
+ return true;
+ }
+
+ if (user.getEffectiveGroups().contains(group.getGroupUUID())) {
+ logger.atFine().log(
+ "group %s is visible to user %s (user is member of the group)",
+ group.getGroupUUID().get(), user.getLoggableName());
+ return true;
+ }
+
+ if (isOwner()) {
+ logger.atFine().log(
+ "group %s is visible to user %s (user is owner of the group)",
+ group.getGroupUUID().get(), user.getLoggableName());
+ return true;
+ }
+
+ // The check for canAdministrateServer may seem redundant, but it's needed to make external
+ // groups visible to server administrators.
+ if (canAdministrateServer()) {
+ logger.atFine().log(
+ "group %s is visible to user %s (user is admin)",
+ group.getGroupUUID().get(), user.getLoggableName());
+ return true;
+ }
+
+ logger.atFine().log(
+ "group %s is not visible to user %s", group.getGroupUUID().get(), user.getLoggableName());
+ return false;
}
public boolean isOwner() {
@@ -127,11 +160,28 @@
return isOwner;
}
- // Keep this logic in sync with VisibleRefFilter#isOwner(...).
+ // Keep this logic in sync with DefaultRefFilter#isGroupOwner(...).
if (group instanceof GroupDescription.Internal) {
AccountGroup.UUID ownerUUID = ((GroupDescription.Internal) group).getOwnerGroupUUID();
- isOwner = getUser().getEffectiveGroups().contains(ownerUUID) || canAdministrateServer();
+ if (getUser().getEffectiveGroups().contains(ownerUUID)) {
+ logger.atFine().log(
+ "user %s is owner of group %s", user.getLoggableName(), group.getGroupUUID().get());
+ isOwner = true;
+ } else if (canAdministrateServer()) {
+ logger.atFine().log(
+ "user %s is owner of group %s (user is admin)",
+ user.getLoggableName(), group.getGroupUUID().get());
+ isOwner = true;
+ } else {
+ logger.atFine().log(
+ "user %s is not an owner of group %s",
+ user.getLoggableName(), group.getGroupUUID().get());
+ isOwner = false;
+ }
} else {
+ logger.atFine().log(
+ "user %s is not an owner of external group %s",
+ user.getLoggableName(), group.getGroupUUID().get());
isOwner = false;
}
return isOwner;
@@ -141,7 +191,12 @@
try {
perm.check(GlobalPermission.ADMINISTRATE_SERVER);
return true;
- } catch (AuthException | PermissionBackendException denied) {
+ } catch (AuthException e) {
+ return false;
+ } catch (PermissionBackendException e) {
+ logger.atFine().log(
+ "Failed to check %s global capability for user %s",
+ GlobalPermission.ADMINISTRATE_SERVER, user.getLoggableName());
return false;
}
}
diff --git a/java/com/google/gerrit/server/account/Preferences.java b/java/com/google/gerrit/server/account/Preferences.java
index c15e6b0..ece610b 100644
--- a/java/com/google/gerrit/server/account/Preferences.java
+++ b/java/com/google/gerrit/server/account/Preferences.java
@@ -23,13 +23,10 @@
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DateFormat;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DefaultBase;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DownloadCommand;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.TimeFormat;
-import com.google.gerrit.extensions.client.KeyMapType;
import com.google.gerrit.extensions.client.MenuItem;
-import com.google.gerrit.extensions.client.Theme;
import java.util.Optional;
@AutoValue
@@ -40,8 +37,6 @@
public abstract Optional<String> downloadScheme();
- public abstract Optional<DownloadCommand> downloadCommand();
-
public abstract Optional<DateFormat> dateFormat();
public abstract Optional<TimeFormat> timeFormat();
@@ -82,8 +77,6 @@
abstract Builder downloadScheme(@Nullable String val);
- abstract Builder downloadCommand(@Nullable DownloadCommand val);
-
abstract Builder dateFormat(@Nullable DateFormat val);
abstract Builder timeFormat(@Nullable TimeFormat val);
@@ -125,7 +118,6 @@
return (new AutoValue_Preferences_General.Builder())
.changesPerPage(info.changesPerPage)
.downloadScheme(info.downloadScheme)
- .downloadCommand(info.downloadCommand)
.dateFormat(info.dateFormat)
.timeFormat(info.timeFormat)
.expandInlineDiffs(info.expandInlineDiffs)
@@ -150,7 +142,6 @@
GeneralPreferencesInfo info = new GeneralPreferencesInfo();
info.changesPerPage = changesPerPage().orElse(null);
info.downloadScheme = downloadScheme().orElse(null);
- info.downloadCommand = downloadCommand().orElse(null);
info.dateFormat = dateFormat().orElse(null);
info.timeFormat = timeFormat().orElse(null);
info.expandInlineDiffs = expandInlineDiffs().orElse(null);
@@ -202,10 +193,6 @@
public abstract Optional<Boolean> showBase();
- public abstract Optional<Theme> theme();
-
- public abstract Optional<KeyMapType> keyMapType();
-
@AutoValue.Builder
public abstract static class Builder {
abstract Builder tabSize(@Nullable Integer val);
@@ -236,10 +223,6 @@
abstract Builder showBase(@Nullable Boolean val);
- abstract Builder theme(@Nullable Theme val);
-
- abstract Builder keyMapType(@Nullable KeyMapType val);
-
abstract Edit build();
}
@@ -259,8 +242,6 @@
.indentWithTabs(info.indentWithTabs)
.autoCloseBrackets(info.autoCloseBrackets)
.showBase(info.showBase)
- .theme(info.theme)
- .keyMapType(info.keyMapType)
.build();
}
@@ -280,8 +261,6 @@
info.indentWithTabs = indentWithTabs().orElse(null);
info.autoCloseBrackets = autoCloseBrackets().orElse(null);
info.showBase = showBase().orElse(null);
- info.theme = theme().orElse(null);
- info.keyMapType = keyMapType().orElse(null);
return info;
}
}
@@ -326,8 +305,6 @@
public abstract Optional<Boolean> lineWrapping();
- public abstract Optional<Theme> theme();
-
public abstract Optional<Whitespace> ignoreWhitespace();
public abstract Optional<Boolean> retainHeader();
@@ -378,8 +355,6 @@
abstract Builder lineWrapping(@Nullable Boolean val);
- abstract Builder theme(@Nullable Theme val);
-
abstract Builder ignoreWhitespace(@Nullable Whitespace val);
abstract Builder retainHeader(@Nullable Boolean val);
@@ -414,7 +389,6 @@
.hideEmptyPane(info.hideEmptyPane)
.matchBrackets(info.matchBrackets)
.lineWrapping(info.lineWrapping)
- .theme(info.theme)
.ignoreWhitespace(info.ignoreWhitespace)
.retainHeader(info.retainHeader)
.skipDeleted(info.skipDeleted)
@@ -444,7 +418,6 @@
info.hideEmptyPane = hideEmptyPane().orElse(null);
info.matchBrackets = matchBrackets().orElse(null);
info.lineWrapping = lineWrapping().orElse(null);
- info.theme = theme().orElse(null);
info.ignoreWhitespace = ignoreWhitespace().orElse(null);
info.retainHeader = retainHeader().orElse(null);
info.skipDeleted = skipDeleted().orElse(null);
diff --git a/java/com/google/gerrit/server/api/ApiUtil.java b/java/com/google/gerrit/server/api/ApiUtil.java
index c5b8b12..8a8e37c 100644
--- a/java/com/google/gerrit/server/api/ApiUtil.java
+++ b/java/com/google/gerrit/server/api/ApiUtil.java
@@ -32,7 +32,7 @@
public static RestApiException asRestApiException(String msg, Exception e)
throws RuntimeException {
Throwables.throwIfUnchecked(e);
- return e instanceof RestApiException ? (RestApiException) e : new RestApiException(msg, e);
+ return e instanceof RestApiException ? (RestApiException) e : RestApiException.wrap(msg, e);
}
private ApiUtil() {}
diff --git a/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java b/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
index 3932177..59c396a 100644
--- a/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
+++ b/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
@@ -69,7 +69,11 @@
@Override
public void disable() throws RestApiException {
- disable.apply(resource, new Input());
+ try {
+ disable.apply(resource, new Input());
+ } catch (Exception e) {
+ throw asRestApiException("Cannot disable plugin", e);
+ }
}
@Override
diff --git a/java/com/google/gerrit/server/api/projects/ProjectsImpl.java b/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
index 5d25d1a..f311b35 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
@@ -156,7 +156,7 @@
.withStart(r.getStart())
.apply();
} catch (StorageException e) {
- throw new RestApiException("Cannot query projects", e);
+ throw asRestApiException("Cannot query projects", e);
}
}
}
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index 4980c4a..770f36c 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -566,10 +566,16 @@
reviewerInputs.stream(),
Streams.stream(
newAddReviewerInputFromCommitIdentity(
- change, patchSetInfo.getAuthor().getAccount(), NotifyHandling.NONE)),
+ change,
+ patchSetInfo.getCommitId(),
+ patchSetInfo.getAuthor().getAccount(),
+ NotifyHandling.NONE)),
Streams.stream(
newAddReviewerInputFromCommitIdentity(
- change, patchSetInfo.getCommitter().getAccount(), NotifyHandling.NONE)))
+ change,
+ patchSetInfo.getCommitId(),
+ patchSetInfo.getCommitter().getAccount(),
+ NotifyHandling.NONE)))
.collect(toImmutableList());
}
}
diff --git a/java/com/google/gerrit/server/change/ChangeMessages.java b/java/com/google/gerrit/server/change/ChangeMessages.java
index 6cd3726..6f2e1ef 100644
--- a/java/com/google/gerrit/server/change/ChangeMessages.java
+++ b/java/com/google/gerrit/server/change/ChangeMessages.java
@@ -23,6 +23,9 @@
}
public String revertChangeDefaultMessage;
+ public String revertSubmissionDefaultMessage;
+ public String revertSubmissionUserMessage;
+ public String revertSubmissionOfRevertSubmissionUserMessage;
public String reviewerCantSeeChange;
public String reviewerInvalid;
diff --git a/java/com/google/gerrit/server/change/ConsistencyChecker.java b/java/com/google/gerrit/server/change/ConsistencyChecker.java
index 0f03c88..61616c0 100644
--- a/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -225,7 +225,9 @@
problem("Missing change owner: " + change().getOwner());
}
} catch (IOException | ConfigInvalidException e) {
- error("Failed to look up owner", e);
+ ProblemInfo problem = problem("Failed to look up owner");
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s: %s", notes.getChangeId(), problem);
}
}
@@ -237,7 +239,9 @@
String.format("Current patch set %d not found", change().currentPatchSetId().get()));
}
} catch (StorageException e) {
- error("Failed to look up current patch set", e);
+ ProblemInfo problem = problem("Failed to look up current patch set");
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s: %s", notes.getChangeId(), problem);
}
}
@@ -249,9 +253,15 @@
rw = new RevWalk(oi.newReader());
return true;
} catch (RepositoryNotFoundException e) {
- return error("Destination repository not found: " + project, e);
+ ProblemInfo problem = problem("Destination repository not found: " + project);
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s: %s", notes.getChangeId(), problem);
+ return false;
} catch (IOException e) {
- return error("Failed to open repository: " + project, e);
+ ProblemInfo problem = problem("Failed to open repository: " + project);
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s: %s", notes.getChangeId(), problem);
+ return false;
}
}
@@ -261,7 +271,10 @@
// Iterate in descending order.
all = PS_ID_ORDER.sortedCopy(psUtil.byChange(notes));
} catch (StorageException e) {
- return error("Failed to look up patch sets", e);
+ ProblemInfo problem = problem("Failed to look up patch sets");
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s: %s", notes.getChangeId(), problem);
+ return false;
}
patchSetsBySha = MultimapBuilder.hashKeys(all.size()).treeSetValues(PS_ID_ORDER).build();
@@ -271,7 +284,9 @@
repo.getRefDatabase()
.exactRef(all.stream().map(ps -> ps.id().toRefName()).toArray(String[]::new));
} catch (IOException e) {
- error("error reading refs", e);
+ ProblemInfo problem = problem("Error reading refs");
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s: %s", notes.getChangeId(), problem);
refs = Collections.emptyMap();
}
@@ -430,7 +445,8 @@
continue;
}
} catch (StorageException e) {
- warn(e);
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s", notes.getChangeId());
// Include this patch set; should cause an error below, which is good.
}
thisCommitPsIds.add(psId);
@@ -480,7 +496,10 @@
break;
}
} catch (IOException e) {
- error("Error looking up expected merged commit " + fix.expectMergedAs, e);
+ ProblemInfo problem =
+ problem("Error looking up expected merged commit " + fix.expectMergedAs);
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s: %s", notes.getChangeId(), problem);
}
}
@@ -561,7 +580,8 @@
insertPatchSetProblem.status = Status.FIXED;
insertPatchSetProblem.outcome = "Inserted as patch set " + psId.get();
} catch (StorageException | IOException | UpdateException | RestApiException e) {
- warn(e);
+ logger.atWarning().withCause(e).log(
+ "Error in consistency check of change %s", notes.getChangeId());
for (ProblemInfo pi : currProblems) {
pi.status = Status.FIX_FAILED;
pi.outcome = "Error inserting merged patch set";
@@ -763,18 +783,6 @@
return problems.get(problems.size() - 1);
}
- private boolean error(String msg, Throwable t) {
- problem(msg);
- // TODO(dborowitz): Expose stack trace to administrators.
- warn(t);
- return false;
- }
-
- private void warn(Throwable t) {
- logger.atWarning().withCause(t).log(
- "Error in consistency check of change %s", notes.getChangeId());
- }
-
private Result result() {
return Result.create(notes, problems);
}
diff --git a/java/com/google/gerrit/server/change/ReviewerAdder.java b/java/com/google/gerrit/server/change/ReviewerAdder.java
index ba6ba21..4f66492 100644
--- a/java/com/google/gerrit/server/change/ReviewerAdder.java
+++ b/java/com/google/gerrit/server/change/ReviewerAdder.java
@@ -81,6 +81,7 @@
import java.util.function.Function;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
public class ReviewerAdder {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -125,12 +126,16 @@
}
public static Optional<InternalAddReviewerInput> newAddReviewerInputFromCommitIdentity(
- Change change, @Nullable Account.Id accountId, NotifyHandling notify) {
+ Change change, ObjectId commitId, @Nullable Account.Id accountId, NotifyHandling notify) {
if (accountId == null || accountId.equals(change.getOwner())) {
// If git ident couldn't be resolved to a user, or if it's not forged, do nothing.
return Optional.empty();
}
+ logger.atFine().log(
+ "Adding account %d from author/committer identity of commit %s as reviewer to change %d",
+ accountId.get(), commitId.name(), change.getChangeId());
+
InternalAddReviewerInput in = new InternalAddReviewerInput();
in.reviewer = accountId.toString();
in.state = REVIEWER;
diff --git a/java/com/google/gerrit/server/extensions/events/AbstractChangeEvent.java b/java/com/google/gerrit/server/extensions/events/AbstractChangeEvent.java
index a5800fb2..b7ee043 100644
--- a/java/com/google/gerrit/server/extensions/events/AbstractChangeEvent.java
+++ b/java/com/google/gerrit/server/extensions/events/AbstractChangeEvent.java
@@ -20,6 +20,7 @@
import com.google.gerrit.extensions.events.ChangeEvent;
import java.sql.Timestamp;
+/** Base class for all change events. */
public abstract class AbstractChangeEvent implements ChangeEvent {
private final ChangeInfo changeInfo;
private final AccountInfo who;
diff --git a/java/com/google/gerrit/server/extensions/events/AbstractRevisionEvent.java b/java/com/google/gerrit/server/extensions/events/AbstractRevisionEvent.java
index 6b72b5e..9d4d299 100644
--- a/java/com/google/gerrit/server/extensions/events/AbstractRevisionEvent.java
+++ b/java/com/google/gerrit/server/extensions/events/AbstractRevisionEvent.java
@@ -21,6 +21,7 @@
import com.google.gerrit.extensions.events.RevisionEvent;
import java.sql.Timestamp;
+/** Base class for all revision events. */
public abstract class AbstractRevisionEvent extends AbstractChangeEvent implements RevisionEvent {
private final RevisionInfo revisionInfo;
diff --git a/java/com/google/gerrit/server/extensions/events/AgreementSignup.java b/java/com/google/gerrit/server/extensions/events/AgreementSignup.java
index b692cf5..47fdd0e 100644
--- a/java/com/google/gerrit/server/extensions/events/AgreementSignup.java
+++ b/java/com/google/gerrit/server/extensions/events/AgreementSignup.java
@@ -21,6 +21,7 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
+/** Helper class to fire an event when a user has signed a contributor agreement. */
@Singleton
public class AgreementSignup {
private final PluginSetContext<AgreementSignupListener> listeners;
@@ -40,6 +41,7 @@
listeners.runEach(l -> l.onAgreementSignup(event));
}
+ /** Event to be fired when a user has signed a contributor agreement. */
private static class Event extends AbstractNoNotifyEvent
implements AgreementSignupListener.Event {
private final AccountInfo account;
diff --git a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
index a76b69b..2189690 100644
--- a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
@@ -27,6 +27,7 @@
import com.google.inject.Singleton;
import java.sql.Timestamp;
+/** Helper class to fire an event when a user has been set as assignee on a change. */
@Singleton
public class AssigneeChanged {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -58,6 +59,7 @@
}
}
+ /** Event to be fired when a user has been set as assignee on a change. */
private static class Event extends AbstractChangeEvent implements AssigneeChangedListener.Event {
private final AccountInfo oldAssignee;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
index b27ffb9..c7a9283 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.sql.Timestamp;
+/** Helper class to fire an event when a change has been abandoned. */
@Singleton
public class ChangeAbandoned {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -78,6 +79,7 @@
}
}
+ /** Event to be fired when a change has been abandoned. */
private static class Event extends AbstractRevisionEvent
implements ChangeAbandonedListener.Event {
private final String reason;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java b/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
index df71f27..1ed6209 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
@@ -27,6 +27,7 @@
import com.google.inject.Singleton;
import java.sql.Timestamp;
+/** Helper class to fire an event when a change has been deleted. */
@Singleton
public class ChangeDeleted {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -52,6 +53,7 @@
}
}
+ /** Event to be fired when a change has been deleted. */
private static class Event extends AbstractChangeEvent implements ChangeDeletedListener.Event {
Event(ChangeInfo change, AccountInfo deleter, Timestamp when) {
super(change, deleter, when, NotifyHandling.ALL);
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
index add1c51..06d0008 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.sql.Timestamp;
+/** Helper class to fire an event when a change has been merged. */
@Singleton
public class ChangeMerged {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -72,6 +73,7 @@
}
}
+ /** Event to be fired when a change has been merged. */
private static class Event extends AbstractRevisionEvent implements ChangeMergedListener.Event {
private final String newRevisionId;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
index fa91dbd..1af56d0 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.sql.Timestamp;
+/** Helper class to fire an event when a change has been restored. */
@Singleton
public class ChangeRestored {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -72,6 +73,7 @@
}
}
+ /** Event to be fired when a change has been restored. */
private static class Event extends AbstractRevisionEvent implements ChangeRestoredListener.Event {
private String reason;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
index 5fb004f..d608c52 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
@@ -25,6 +25,7 @@
import com.google.inject.Singleton;
import java.sql.Timestamp;
+/** Helper class to fire an event when a change has been reverted. */
@Singleton
public class ChangeReverted {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -50,6 +51,7 @@
}
}
+ /** Event to be fired when a change has been reverted. */
private static class Event extends AbstractChangeEvent implements ChangeRevertedListener.Event {
private final ChangeInfo revertChange;
diff --git a/java/com/google/gerrit/server/extensions/events/CommentAdded.java b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
index e45b206..151298c 100644
--- a/java/com/google/gerrit/server/extensions/events/CommentAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
@@ -36,6 +36,7 @@
import java.sql.Timestamp;
import java.util.Map;
+/** Helper class to fire an event when a comment or vote has been added to a change. */
@Singleton
public class CommentAdded {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -82,6 +83,7 @@
}
}
+ /** Event to be fired when a comment or vote has been added to a change. */
private static class Event extends AbstractRevisionEvent implements CommentAddedListener.Event {
private final String comment;
diff --git a/java/com/google/gerrit/server/extensions/events/EventUtil.java b/java/com/google/gerrit/server/extensions/events/EventUtil.java
index 3dcf3b8..a35140a 100644
--- a/java/com/google/gerrit/server/extensions/events/EventUtil.java
+++ b/java/com/google/gerrit/server/extensions/events/EventUtil.java
@@ -46,8 +46,8 @@
/**
* Formats change and revision info objects to serve as payload for Gerrit events.
*
- * <p>Uses configurable options ({@code event.payload.listChangeOptions}) to decide which fields to
- * populate.
+ * <p>Uses configurable options ({@code event.payload.listChangeOptions}) to decide which change
+ * fields to populate.
*/
@Singleton
public class EventUtil {
diff --git a/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java b/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
index 99f105e..6ed0a08 100644
--- a/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
+++ b/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
@@ -27,6 +27,7 @@
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.transport.ReceiveCommand;
+/** Helper class to fire an event when a Git reference has been updated. */
@Singleton
public class GitReferenceUpdated {
public static final GitReferenceUpdated DISABLED =
@@ -153,6 +154,7 @@
listeners.runEach(l -> l.onGitReferenceUpdated(event));
}
+ /** Event to be fired when a Git reference has been updated. */
public static class Event implements GitReferenceUpdatedListener.Event {
private final String projectName;
private final String ref;
diff --git a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
index df60ec0..5d9c5c2 100644
--- a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
@@ -30,6 +30,7 @@
import java.util.Collection;
import java.util.Set;
+/** Helper class to fire an event when the hashtags of a change has been edited. */
@Singleton
public class HashtagsEdited {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -63,6 +64,7 @@
}
}
+ /** Event to be fired when the hashtags of a change has been edited. */
private static class Event extends AbstractChangeEvent implements HashtagsEditedListener.Event {
private Collection<String> updatedHashtags;
diff --git a/java/com/google/gerrit/server/extensions/events/PluginEvent.java b/java/com/google/gerrit/server/extensions/events/PluginEvent.java
index 60d27c9..6723588 100644
--- a/java/com/google/gerrit/server/extensions/events/PluginEvent.java
+++ b/java/com/google/gerrit/server/extensions/events/PluginEvent.java
@@ -14,12 +14,15 @@
package com.google.gerrit.server.extensions.events;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.extensions.events.PluginEventListener;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+/** Helper class to let plugins fire a plugin-specific event. */
@Singleton
+@UsedAt(UsedAt.Project.PLUGINS_ALL)
public class PluginEvent {
private final PluginSetContext<PluginEventListener> listeners;
@@ -36,6 +39,7 @@
listeners.runEach(l -> l.onPluginEvent(e));
}
+ /** Event to be fired by plugins. */
private static class Event extends AbstractNoNotifyEvent implements PluginEventListener.Event {
private final String pluginName;
private final String type;
diff --git a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
index 72adff7..bcc6b8e 100644
--- a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
@@ -33,6 +33,7 @@
import java.io.IOException;
import java.sql.Timestamp;
+/** Helper class to fire an event when the private flag of a change has been toggled. */
@Singleton
public class PrivateStateChanged {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -67,6 +68,7 @@
}
}
+ /** Event to be fired when the private flag of a change has been toggled. */
private static class Event extends AbstractRevisionEvent
implements PrivateStateChangedListener.Event {
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
index 1af428cb..35e7828 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
@@ -36,6 +36,7 @@
import java.sql.Timestamp;
import java.util.List;
+/** Helper class to fire an event when reviewers have been added to a change. */
@Singleton
public class ReviewerAdded {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -79,6 +80,7 @@
}
}
+ /** Event to be fired when reviewers have been added to a change. */
private static class Event extends AbstractRevisionEvent implements ReviewerAddedListener.Event {
private final List<AccountInfo> reviewers;
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
index 61632f2..147f980 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
@@ -36,6 +36,7 @@
import java.sql.Timestamp;
import java.util.Map;
+/** Helper class to fire an event when a reviewer has been deleted from a change. */
@Singleton
public class ReviewerDeleted {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -86,6 +87,7 @@
}
}
+ /** Event to be fired when a reviewer has been deleted from a change. */
private static class Event extends AbstractRevisionEvent
implements ReviewerDeletedListener.Event {
private final AccountInfo reviewer;
diff --git a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
index bdfa8c1..8179e9a 100644
--- a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
+++ b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
@@ -35,6 +35,7 @@
import java.io.IOException;
import java.sql.Timestamp;
+/** Helper class to fire an event when a revision has been created for a change. */
@Singleton
public class RevisionCreated {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -93,6 +94,7 @@
}
}
+ /** Event to be fired when a revision has been created for a change. */
private static class Event extends AbstractRevisionEvent
implements RevisionCreatedListener.Event {
diff --git a/java/com/google/gerrit/server/extensions/events/TopicEdited.java b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
index cb982a1..e4089b1 100644
--- a/java/com/google/gerrit/server/extensions/events/TopicEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
@@ -27,6 +27,7 @@
import com.google.inject.Singleton;
import java.sql.Timestamp;
+/** Helper class to fire an event when the topic of a change has been edited. */
@Singleton
public class TopicEdited {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -53,6 +54,7 @@
}
}
+ /** Event to be fired when the topic of a change has been edited. */
private static class Event extends AbstractChangeEvent implements TopicEditedListener.Event {
private final String oldTopic;
diff --git a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
index 8533a65..ef4e461 100644
--- a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
@@ -36,6 +36,7 @@
import java.sql.Timestamp;
import java.util.Map;
+/** Helper class to fire an event when a vote has been deleted from a change. */
@Singleton
public class VoteDeleted {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -86,6 +87,7 @@
}
}
+ /** Event to be fired when a vote has been deleted from a change. */
private static class Event extends AbstractRevisionEvent implements VoteDeletedListener.Event {
private final AccountInfo reviewer;
private final Map<String, ApprovalInfo> approvals;
diff --git a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
index 16c5e25..359a3a8 100644
--- a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
@@ -33,6 +33,7 @@
import java.io.IOException;
import java.sql.Timestamp;
+/** Helper class to fire an event when the work-in-progress state of a change has been toggled. */
@Singleton
public class WorkInProgressStateChanged {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -68,6 +69,7 @@
}
}
+ /** Event to be fired when the work-in-progress state of a change has been toggled. */
private static class Event extends AbstractRevisionEvent
implements WorkInProgressStateChangedListener.Event {
diff --git a/java/com/google/gerrit/server/git/CommitUtil.java b/java/com/google/gerrit/server/git/CommitUtil.java
index 393ccb7..818212c 100644
--- a/java/com/google/gerrit/server/git/CommitUtil.java
+++ b/java/com/google/gerrit/server/git/CommitUtil.java
@@ -14,19 +14,41 @@
package com.google.gerrit.server.git;
+import static com.google.common.base.MoreObjects.firstNonNull;
+
import com.google.common.base.Strings;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Account.Id;
import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.PatchSet;
-import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
+import com.google.gerrit.extensions.api.changes.RevertInput;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommonConverters;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.ReviewerSet;
+import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeMessages;
+import com.google.gerrit.server.change.NotifyResolver;
+import com.google.gerrit.server.extensions.events.ChangeReverted;
+import com.google.gerrit.server.mail.send.RevertedSender;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.gerrit.server.notedb.ReviewerStateInternal;
+import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.update.BatchUpdateOp;
+import com.google.gerrit.server.update.ChangeContext;
+import com.google.gerrit.server.update.Context;
+import com.google.gerrit.server.update.UpdateException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -34,6 +56,10 @@
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -47,14 +73,41 @@
/** Static utilities for working with {@link RevCommit}s. */
@Singleton
public class CommitUtil {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final GitRepositoryManager repoManager;
private final Provider<PersonIdent> serverIdent;
+ private final Sequences seq;
+ private final ApprovalsUtil approvalsUtil;
+ private final ChangeInserter.Factory changeInserterFactory;
+ private final NotifyResolver notifyResolver;
+ private final RevertedSender.Factory revertedSenderFactory;
+ private final ChangeMessagesUtil cmUtil;
+ private final ChangeReverted changeReverted;
+ private final BatchUpdate.Factory updateFactory;
@Inject
CommitUtil(
- GitRepositoryManager repoManager, @GerritPersonIdent Provider<PersonIdent> serverIdent) {
+ GitRepositoryManager repoManager,
+ @GerritPersonIdent Provider<PersonIdent> serverIdent,
+ Sequences seq,
+ ApprovalsUtil approvalsUtil,
+ ChangeInserter.Factory changeInserterFactory,
+ NotifyResolver notifyResolver,
+ RevertedSender.Factory revertedSenderFactory,
+ ChangeMessagesUtil cmUtil,
+ ChangeReverted changeReverted,
+ BatchUpdate.Factory updateFactory) {
this.repoManager = repoManager;
this.serverIdent = serverIdent;
+ this.seq = seq;
+ this.approvalsUtil = approvalsUtil;
+ this.changeInserterFactory = changeInserterFactory;
+ this.notifyResolver = notifyResolver;
+ this.revertedSenderFactory = revertedSenderFactory;
+ this.cmUtil = cmUtil;
+ this.changeReverted = changeReverted;
+ this.updateFactory = updateFactory;
}
public static CommitInfo toCommitInfo(RevCommit commit) throws IOException {
@@ -81,49 +134,79 @@
}
/**
- * Allows creating a revert commit.
+ * Allows creating a revert change.
*
- * @param message Commit message for the revert commit.
* @param notes ChangeNotes of the change being reverted.
* @param user Current User performing the revert.
+ * @param input the RevertInput entity for conducting the revert.
+ * @param timestamp timestamp for the created change.
* @return ObjectId that represents the newly created commit.
- * @throws ResourceConflictException Can't revert the initial commit.
- * @throws IOException Thrown in case of I/O errors.
*/
- public ObjectId createRevertCommit(String message, ChangeNotes notes, CurrentUser user)
- throws ResourceConflictException, IOException {
- message = Strings.emptyToNull(message);
+ public Change.Id createRevertChange(
+ ChangeNotes notes, CurrentUser user, RevertInput input, Timestamp timestamp)
+ throws RestApiException, UpdateException, ConfigInvalidException, IOException {
+ String message = Strings.emptyToNull(input.message);
- Project.NameKey project = notes.getProjectName();
- try (Repository git = repoManager.openRepository(project);
+ try (Repository git = repoManager.openRepository(notes.getProjectName());
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
RevWalk revWalk = new RevWalk(reader)) {
- return createRevertCommit(message, notes, user, null, TimeUtil.nowTs(), oi, revWalk);
+ ObjectId generatedChangeId = Change.generateChangeId();
+ ObjectId revCommit =
+ createRevertCommit(message, notes, user, timestamp, oi, revWalk, generatedChangeId);
+ return createRevertChangeFromCommit(
+ revCommit, input, notes, user, generatedChangeId, timestamp, oi, revWalk, git);
+ } catch (RepositoryNotFoundException e) {
+ throw new ResourceNotFoundException(notes.getChangeId().toString(), e);
}
}
/**
+ * Wrapper function for creating a revert Commit.
+ *
* @param message Commit message for the revert commit.
* @param notes ChangeNotes of the change being reverted.
* @param user Current User performing the revert.
- * @param generatedChangeId The changeId for the commit message, can be null since it is not
- * needed for commits, only for changes.
+ * @param ts Timestamp of creation for the commit.
+ * @return ObjectId that represents the newly created commit.
+ */
+ public ObjectId createRevertCommit(
+ String message, ChangeNotes notes, CurrentUser user, Timestamp ts)
+ throws RestApiException, IOException {
+
+ try (Repository git = repoManager.openRepository(notes.getProjectName());
+ ObjectInserter oi = git.newObjectInserter();
+ ObjectReader reader = oi.newReader();
+ RevWalk revWalk = new RevWalk(reader)) {
+ return createRevertCommit(message, notes, user, ts, oi, revWalk, null);
+ } catch (RepositoryNotFoundException e) {
+ throw new ResourceNotFoundException(notes.getProjectName().toString(), e);
+ }
+ }
+
+ /**
+ * Creates a revert commit.
+ *
+ * @param message Commit message for the revert commit.
+ * @param notes ChangeNotes of the change being reverted.
+ * @param user Current User performing the revert.
* @param ts Timestamp of creation for the commit.
* @param oi ObjectInserter for inserting the newly created commit.
* @param revWalk Used for parsing the original commit.
+ * @param generatedChangeId The changeId for the commit message, can be null since it is not
+ * needed for commits, only for changes.
* @return ObjectId that represents the newly created commit.
* @throws ResourceConflictException Can't revert the initial commit.
* @throws IOException Thrown in case of I/O errors.
*/
- public ObjectId createRevertCommit(
+ private ObjectId createRevertCommit(
String message,
ChangeNotes notes,
CurrentUser user,
- @Nullable ObjectId generatedChangeId,
Timestamp ts,
ObjectInserter oi,
- RevWalk revWalk)
+ RevWalk revWalk,
+ @Nullable ObjectId generatedChangeId)
throws ResourceConflictException, IOException {
PatchSet patch = notes.getCurrentPatchSet();
@@ -162,4 +245,94 @@
oi.flush();
return id;
}
+
+ private Change.Id createRevertChangeFromCommit(
+ ObjectId revertCommitId,
+ RevertInput input,
+ ChangeNotes notes,
+ CurrentUser user,
+ @Nullable ObjectId generatedChangeId,
+ Timestamp ts,
+ ObjectInserter oi,
+ RevWalk revWalk,
+ Repository git)
+ throws IOException, RestApiException, UpdateException, ConfigInvalidException {
+ RevCommit revertCommit = revWalk.parseCommit(revertCommitId);
+ Change changeToRevert = notes.getChange();
+ Change.Id changeId = Change.id(seq.nextChangeId());
+ NotifyResolver.Result notify =
+ notifyResolver.resolve(firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails);
+
+ ChangeInserter ins =
+ changeInserterFactory
+ .create(changeId, revertCommit, notes.getChange().getDest().branch())
+ .setTopic(input.topic == null ? changeToRevert.getTopic() : input.topic.trim());
+ ins.setMessage("Uploaded patch set 1.");
+
+ ReviewerSet reviewerSet = approvalsUtil.getReviewers(notes);
+
+ Set<Id> reviewers = new HashSet<>();
+ reviewers.add(changeToRevert.getOwner());
+ reviewers.addAll(reviewerSet.byState(ReviewerStateInternal.REVIEWER));
+ reviewers.remove(user.getAccountId());
+ Set<Account.Id> ccs = new HashSet<>(reviewerSet.byState(ReviewerStateInternal.CC));
+ ccs.remove(user.getAccountId());
+ ins.setReviewersAndCcs(reviewers, ccs);
+ ins.setRevertOf(notes.getChangeId());
+
+ try (BatchUpdate bu = updateFactory.create(notes.getProjectName(), user, ts)) {
+ bu.setRepository(git, revWalk, oi);
+ bu.setNotify(notify);
+ bu.insertChange(ins);
+ bu.addOp(changeId, new NotifyOp(changeToRevert, ins));
+ bu.addOp(changeToRevert.getId(), new PostRevertedMessageOp(generatedChangeId));
+ bu.execute();
+ }
+ return changeId;
+ }
+
+ private class NotifyOp implements BatchUpdateOp {
+ private final Change change;
+ private final ChangeInserter ins;
+
+ NotifyOp(Change change, ChangeInserter ins) {
+ this.change = change;
+ this.ins = ins;
+ }
+
+ @Override
+ public void postUpdate(Context ctx) throws Exception {
+ changeReverted.fire(change, ins.getChange(), ctx.getWhen());
+ try {
+ RevertedSender cm = revertedSenderFactory.create(ctx.getProject(), change.getId());
+ cm.setFrom(ctx.getAccountId());
+ cm.setNotify(ctx.getNotify(change.getId()));
+ cm.send();
+ } catch (Exception err) {
+ logger.atSevere().withCause(err).log(
+ "Cannot send email for revert change %s", change.getId());
+ }
+ }
+ }
+
+ private class PostRevertedMessageOp implements BatchUpdateOp {
+ private final ObjectId computedChangeId;
+
+ PostRevertedMessageOp(ObjectId computedChangeId) {
+ this.computedChangeId = computedChangeId;
+ }
+
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ Change change = ctx.getChange();
+ PatchSet.Id patchSetId = change.currentPatchSetId();
+ ChangeMessage changeMessage =
+ ChangeMessagesUtil.newMessage(
+ ctx,
+ "Created a revert of this change as I" + computedChangeId.name(),
+ ChangeMessagesUtil.TAG_REVERT);
+ cmUtil.addChangeMessage(ctx.getUpdate(patchSetId), changeMessage);
+ return true;
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index e03e0fc..b188224 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -293,7 +293,7 @@
} else if ((e instanceof ExecutionException) && (e.getCause() instanceof RestApiException)) {
return (RestApiException) e.getCause();
}
- return new RestApiException("Error inserting change/patchset", e);
+ return RestApiException.wrap("Error inserting change/patchset", e);
}
// ReceiveCommits has a lot of fields, sorry. Here and in the constructor they are split up
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index 24154d60..62114b0 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -368,10 +368,16 @@
Streams.concat(
Streams.stream(
newAddReviewerInputFromCommitIdentity(
- change, psInfo.getAuthor().getAccount(), NotifyHandling.NONE)),
+ change,
+ psInfo.getCommitId(),
+ psInfo.getAuthor().getAccount(),
+ NotifyHandling.NONE)),
Streams.stream(
newAddReviewerInputFromCommitIdentity(
- change, psInfo.getCommitter().getAccount(), NotifyHandling.NONE)));
+ change,
+ psInfo.getCommitId(),
+ psInfo.getCommitter().getAccount(),
+ NotifyHandling.NONE)));
if (magicBranch != null) {
inputs =
Streams.concat(
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index e8f6096..90d6b66 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -44,6 +44,11 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.git.validators.ValidationMessage.Type;
+import com.google.gerrit.server.patch.DiffSummary;
+import com.google.gerrit.server.patch.DiffSummaryKey;
+import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.patch.PatchListKey;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission;
@@ -76,7 +81,8 @@
import org.eclipse.jgit.util.SystemReader;
/**
- * Represents a list of CommitValidationListeners to run for a push to one branch of one project.
+ * Represents a list of {@link CommitValidationListener}s to run for a push to one branch of one
+ * project.
*/
public class CommitValidators {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -94,15 +100,16 @@
private final AllProjectsName allProjects;
private final ExternalIdsConsistencyChecker externalIdsConsistencyChecker;
private final AccountValidator accountValidator;
- private final String installCommitMsgHookCommand;
private final ProjectCache projectCache;
private final ProjectConfig.Factory projectConfigFactory;
+ private final PatchListCache patchListCache;
+ private final Config config;
@Inject
Factory(
@GerritPersonIdent PersonIdent gerritIdent,
DynamicItem<UrlFormatter> urlFormatter,
- @GerritServerConfig Config cfg,
+ @GerritServerConfig Config config,
PluginSetContext<CommitValidationListener> pluginValidators,
GitRepositoryManager repoManager,
AllUsersName allUsers,
@@ -110,19 +117,20 @@
ExternalIdsConsistencyChecker externalIdsConsistencyChecker,
AccountValidator accountValidator,
ProjectCache projectCache,
- ProjectConfig.Factory projectConfigFactory) {
+ ProjectConfig.Factory projectConfigFactory,
+ PatchListCache patchListCache) {
this.gerritIdent = gerritIdent;
this.urlFormatter = urlFormatter;
+ this.config = config;
this.pluginValidators = pluginValidators;
this.repoManager = repoManager;
this.allUsers = allUsers;
this.allProjects = allProjects;
this.externalIdsConsistencyChecker = externalIdsConsistencyChecker;
this.accountValidator = accountValidator;
- this.installCommitMsgHookCommand =
- cfg != null ? cfg.getString("gerrit", null, "installCommitMsgHookCommand") : null;
this.projectCache = projectCache;
this.projectConfigFactory = projectConfigFactory;
+ this.patchListCache = patchListCache;
}
public CommitValidators forReceiveCommits(
@@ -146,12 +154,7 @@
new CommitterUploaderValidator(user, perm, urlFormatter.get()),
new SignedOffByValidator(user, perm, projectState),
new ChangeIdValidator(
- projectState,
- user,
- urlFormatter.get(),
- installCommitMsgHookCommand,
- sshInfo,
- change),
+ projectState, user, urlFormatter.get(), config, sshInfo, change),
new ConfigValidator(projectConfigFactory, branch, user, rw, allUsers, allProjects),
new BannedCommitsValidator(rejectCommits),
new PluginCommitValidationListener(pluginValidators, skipValidation),
@@ -176,14 +179,10 @@
new ProjectStateValidationListener(projectState),
new AmendedGerritMergeCommitValidationListener(perm, gerritIdent),
new AuthorUploaderValidator(user, perm, urlFormatter.get()),
+ new FileCountValidator(patchListCache, config),
new SignedOffByValidator(user, perm, projectCache.checkedGet(branch.project())),
new ChangeIdValidator(
- projectState,
- user,
- urlFormatter.get(),
- installCommitMsgHookCommand,
- sshInfo,
- change),
+ projectState, user, urlFormatter.get(), config, sshInfo, change),
new ConfigValidator(projectConfigFactory, branch, user, rw, allUsers, allProjects),
new PluginCommitValidationListener(pluginValidators),
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker),
@@ -268,14 +267,14 @@
ProjectState projectState,
IdentifiedUser user,
UrlFormatter urlFormatter,
- String installCommitMsgHookCommand,
+ Config config,
SshInfo sshInfo,
Change change) {
this.projectState = projectState;
- this.urlFormatter = urlFormatter;
- this.installCommitMsgHookCommand = installCommitMsgHookCommand;
- this.sshInfo = sshInfo;
this.user = user;
+ this.urlFormatter = urlFormatter;
+ installCommitMsgHookCommand = config.getString("gerrit", null, "installCommitMsgHookCommand");
+ this.sshInfo = sshInfo;
this.change = change;
}
@@ -387,6 +386,40 @@
}
}
+ /** Limits the number of files per change. */
+ private static class FileCountValidator implements CommitValidationListener {
+
+ private final PatchListCache patchListCache;
+ private final int maxFileCount;
+
+ FileCountValidator(PatchListCache patchListCache, Config config) {
+ this.patchListCache = patchListCache;
+ maxFileCount = config.getInt("change", null, "maxFiles", 50_000);
+ }
+
+ @Override
+ public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
+ throws CommitValidationException {
+ PatchListKey patchListKey =
+ PatchListKey.againstBase(
+ receiveEvent.commit.getId(), receiveEvent.commit.getParentCount());
+ DiffSummaryKey diffSummaryKey = DiffSummaryKey.fromPatchListKey(patchListKey);
+ try {
+ DiffSummary diffSummary =
+ patchListCache.getDiffSummary(diffSummaryKey, receiveEvent.project.getNameKey());
+ if (diffSummary.getPaths().size() > maxFileCount) {
+ throw new CommitValidationException(
+ String.format(
+ "Exceeding maximum number of files per change (%d > %d)",
+ diffSummary.getPaths().size(), maxFileCount));
+ }
+ } catch (PatchListNotAvailableException e) {
+ logger.atWarning().withCause(e).log("Failed to validate file count");
+ }
+ return Collections.emptyList();
+ }
+ }
+
/** If this is the special project configuration branch, validate the config. */
public static class ConfigValidator implements CommitValidationListener {
private final ProjectConfig.Factory projectConfigFactory;
diff --git a/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java b/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
index 9e6db44..d8ecbd9 100644
--- a/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
+++ b/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
@@ -27,7 +27,7 @@
import com.google.gerrit.server.account.AccountState;
/**
- * Wrapper around {@link Predicate}s that is returned by the {@link
+ * Wrapper around {@link Predicate}s that are returned by the {@link
* com.google.gerrit.index.IndexRewriter}. See {@link IndexedQuery}.
*/
public class IndexedAccountQuery extends IndexedQuery<Account.Id, AccountState>
diff --git a/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java b/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
index 680db12..319b834 100644
--- a/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
+++ b/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
@@ -27,7 +27,7 @@
import java.util.Set;
/**
- * Wrapper around {@link Predicate}s that is returned by the {@link
+ * Wrapper around {@link Predicate}s that are returned by the {@link
* com.google.gerrit.index.IndexRewriter}. See {@link IndexedQuery}.
*/
public class IndexedGroupQuery extends IndexedQuery<AccountGroup.UUID, InternalGroup>
diff --git a/java/com/google/gerrit/server/logging/Metadata.java b/java/com/google/gerrit/server/logging/Metadata.java
index d312530..3bb4770 100644
--- a/java/com/google/gerrit/server/logging/Metadata.java
+++ b/java/com/google/gerrit/server/logging/Metadata.java
@@ -116,9 +116,6 @@
// Type of a sequence in NoteDb (ACCOUNTS, CHANGES, GROUPS).
public abstract Optional<String> noteDbSequenceType();
- // Name of a "table" in NoteDb (if set, always CHANGES).
- public abstract Optional<String> noteDbTable();
-
// The ID of a patch set.
public abstract Optional<Integer> patchSetId();
@@ -164,10 +161,10 @@
* httpStatus=Optional.empty, indexName=Optional.empty, indexVersion=Optional[0],
* methodName=Optional.empty, multiple=Optional.empty, operationName=Optional.empty,
* partial=Optional.empty, noteDbFilePath=Optional.empty, noteDbRefName=Optional.empty,
- * noteDbSequenceType=Optional.empty, noteDbTable=Optional.empty, patchSetId=Optional.empty,
- * pluginMetadata=[], pluginName=Optional.empty, projectName=Optional.empty,
- * pushType=Optional.empty, resourceCount=Optional.empty, restViewName=Optional.empty,
- * revision=Optional.empty, username=Optional.empty}
+ * noteDbSequenceType=Optional.empty, patchSetId=Optional.empty, pluginMetadata=[],
+ * pluginName=Optional.empty, projectName=Optional.empty, pushType=Optional.empty,
+ * resourceCount=Optional.empty, restViewName=Optional.empty, revision=Optional.empty,
+ * username=Optional.empty}
* </pre>
*
* <p>That's hard to read in logs. This is why this method
@@ -314,8 +311,6 @@
public abstract Builder noteDbSequenceType(@Nullable String noteDbSequenceType);
- public abstract Builder noteDbTable(@Nullable String noteDbTable);
-
public abstract Builder patchSetId(int patchSetId);
abstract ImmutableList.Builder<PluginMetadata> pluginMetadataBuilder();
diff --git a/java/com/google/gerrit/server/mail/MailUtil.java b/java/com/google/gerrit/server/mail/MailUtil.java
index 1040a55..ff22d23 100644
--- a/java/com/google/gerrit/server/mail/MailUtil.java
+++ b/java/com/google/gerrit/server/mail/MailUtil.java
@@ -17,6 +17,7 @@
import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -33,6 +34,7 @@
import org.eclipse.jgit.revwalk.FooterLine;
public class MailUtil {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static MailRecipients getRecipientsFromFooters(
AccountResolver accountResolver, List<FooterLine> footerLines)
@@ -41,11 +43,19 @@
for (FooterLine footerLine : footerLines) {
try {
if (isReviewer(footerLine)) {
- recipients.reviewers.add(toAccountId(accountResolver, footerLine.getValue().trim()));
+ Account.Id accountId = toAccountId(accountResolver, footerLine.getValue().trim());
+ recipients.reviewers.add(accountId);
+ logger.atFine().log(
+ "Added account %d from footer line \"%s\" as reviewer", accountId.get(), footerLine);
} else if (footerLine.matches(FooterKey.CC)) {
- recipients.cc.add(toAccountId(accountResolver, footerLine.getValue().trim()));
+ Account.Id accountId = toAccountId(accountResolver, footerLine.getValue().trim());
+ recipients.cc.add(accountId);
+ logger.atFine().log(
+ "Added account %d from footer line \"%s\" as cc", accountId.get(), footerLine);
}
} catch (UnprocessableEntityException e) {
+ logger.atFine().log(
+ "Skip adding reviewer/cc from footer line \"%s\": %s", footerLine, e.getMessage());
continue;
}
}
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index 9a1ba35..9b5b4d4 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.notedb;
-import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
@@ -23,7 +22,7 @@
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.metrics.Timer0;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -141,7 +140,7 @@
if (args.failOnLoadForTest.get()) {
throw new StorageException("Reading from NoteDb is disabled");
}
- try (Timer1.Context<NoteDbTable> timer = args.metrics.readLatency.start(CHANGES);
+ try (Timer0.Context timer = args.metrics.readLatency.start();
Repository repo = args.repoManager.openRepository(getProjectName());
// Call openHandle even if reading is disabled, to trigger
// auto-rebuilding before this object may get passed to a ChangeUpdate.
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 369d1f2..5468e23 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -37,7 +37,6 @@
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_TOPIC;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_WORK_IN_PROGRESS;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.parseCommitMessageRange;
-import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.joining;
@@ -66,7 +65,7 @@
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.metrics.Timer0;
import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
@@ -187,7 +186,7 @@
walk.reset();
walk.markStart(walk.parseCommit(tip));
- try (Timer1.Context<NoteDbTable> timer = metrics.parseLatency.start(CHANGES)) {
+ try (Timer0.Context timer = metrics.parseLatency.start()) {
ChangeNotesCommit commit;
while ((commit = walk.next()) != null) {
parse(commit);
diff --git a/java/com/google/gerrit/server/notedb/IntBlob.java b/java/com/google/gerrit/server/notedb/IntBlob.java
index c0af37f..61b9ae0 100644
--- a/java/com/google/gerrit/server/notedb/IntBlob.java
+++ b/java/com/google/gerrit/server/notedb/IntBlob.java
@@ -39,6 +39,7 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
+/** An object blob in a Git repository that stores a single integer value. */
@AutoValue
public abstract class IntBlob {
public static Optional<IntBlob> parse(Repository repo, String refName) throws IOException {
diff --git a/java/com/google/gerrit/server/notedb/NoteDbMetrics.java b/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
index 18ffd17..246e7e1 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
@@ -16,69 +16,61 @@
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
-import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.metrics.Timer1;
-import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.metrics.Timer0;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+/** Metrics for accessing and updating changes in NoteDb. */
@Singleton
class NoteDbMetrics {
/** End-to-end latency for writing a collection of updates. */
- final Timer1<NoteDbTable> updateLatency;
+ final Timer0 updateLatency;
/**
* The portion of {@link #updateLatency} due to preparing the sequence of updates.
*
* <p>May include some I/O (e.g. reading old refs), but excludes writes.
*/
- final Timer1<NoteDbTable> stageUpdateLatency;
+ final Timer0 stageUpdateLatency;
/** End-to-end latency for reading changes from NoteDb, including reading ref(s) and parsing. */
- final Timer1<NoteDbTable> readLatency;
+ final Timer0 readLatency;
/**
* The portion of {@link #readLatency} due to parsing commits, but excluding I/O (to a best
* effort).
*/
- final Timer1<NoteDbTable> parseLatency;
+ final Timer0 parseLatency;
@Inject
NoteDbMetrics(MetricMaker metrics) {
- Field<NoteDbTable> tableField =
- Field.ofEnum(NoteDbTable.class, "table", Metadata.Builder::noteDbTable).build();
-
updateLatency =
metrics.newTimer(
"notedb/update_latency",
- new Description("NoteDb update latency by table")
+ new Description("NoteDb update latency for changes")
.setCumulative()
- .setUnit(Units.MILLISECONDS),
- tableField);
+ .setUnit(Units.MILLISECONDS));
stageUpdateLatency =
metrics.newTimer(
"notedb/stage_update_latency",
- new Description("Latency for staging updates to NoteDb by table")
+ new Description("Latency for staging change updates to NoteDb")
.setCumulative()
- .setUnit(Units.MICROSECONDS),
- tableField);
+ .setUnit(Units.MICROSECONDS));
readLatency =
metrics.newTimer(
"notedb/read_latency",
- new Description("NoteDb read latency by table")
+ new Description("NoteDb read latency for changes")
.setCumulative()
- .setUnit(Units.MILLISECONDS),
- tableField);
+ .setUnit(Units.MILLISECONDS));
parseLatency =
metrics.newTimer(
"notedb/parse_latency",
- new Description("NoteDb parse latency by table")
+ new Description("NoteDb parse latency for changes")
.setCumulative()
- .setUnit(Units.MICROSECONDS),
- tableField);
+ .setUnit(Units.MICROSECONDS));
}
}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbTable.java b/java/com/google/gerrit/server/notedb/NoteDbTable.java
deleted file mode 100644
index e299fdf..0000000
--- a/java/com/google/gerrit/server/notedb/NoteDbTable.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2016 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.notedb;
-
-public enum NoteDbTable {
- ACCOUNTS,
- GROUPS,
- CHANGES;
-
- public String key() {
- return name().toLowerCase();
- }
-
- @Override
- public String toString() {
- return key();
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java b/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
index 1b92c0e..2d1a04a 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
@@ -17,7 +17,6 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
@@ -27,7 +26,7 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.metrics.Timer0;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -273,7 +272,7 @@
* @throws IOException if a storage layer error occurs.
*/
private void stage() throws IOException {
- try (Timer1.Context<NoteDbTable> timer = metrics.stageUpdateLatency.start(CHANGES)) {
+ try (Timer0.Context timer = metrics.stageUpdateLatency.start()) {
if (isEmpty()) {
return;
}
@@ -298,7 +297,7 @@
executed = true;
return null;
}
- try (Timer1.Context<NoteDbTable> timer = metrics.updateLatency.start(CHANGES)) {
+ try (Timer0.Context timer = metrics.updateLatency.start()) {
stage();
// ChangeUpdates must execute before ChangeDraftUpdates.
//
diff --git a/java/com/google/gerrit/server/patch/PatchListCacheImpl.java b/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
index 15fa0f4..b663b9d 100644
--- a/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
+++ b/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
@@ -138,11 +138,10 @@
throws PatchListNotAvailableException {
Project.NameKey project = change.getProject();
ObjectId b = patchSet.commitId();
- Whitespace ws = Whitespace.IGNORE_NONE;
if (parentNum != null) {
- return get(PatchListKey.againstParentNum(parentNum, b, ws), project);
+ return get(PatchListKey.againstParentNum(parentNum, b, Whitespace.IGNORE_NONE), project);
}
- return get(PatchListKey.againstDefaultBase(b, ws), project);
+ return get(PatchListKey.againstDefaultBase(b, Whitespace.IGNORE_NONE), project);
}
@Override
diff --git a/java/com/google/gerrit/server/patch/PatchListKey.java b/java/com/google/gerrit/server/patch/PatchListKey.java
index bf38029..386c50d 100644
--- a/java/com/google/gerrit/server/patch/PatchListKey.java
+++ b/java/com/google/gerrit/server/patch/PatchListKey.java
@@ -59,6 +59,12 @@
return new PatchListKey(otherCommitId, newId, whitespace);
}
+ public static PatchListKey againstBase(ObjectId id, int parentCount) {
+ return parentCount > 1
+ ? PatchListKey.againstParentNum(1, id, Whitespace.IGNORE_NONE)
+ : PatchListKey.againstDefaultBase(id, Whitespace.IGNORE_NONE);
+ }
+
/**
* Old patch-set ID
*
diff --git a/java/com/google/gerrit/server/permissions/PermissionBackend.java b/java/com/google/gerrit/server/permissions/PermissionBackend.java
index d831ab6..8ee0fee 100644
--- a/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -70,7 +70,7 @@
* private final PermissionBackend permissions;
* private final Provider<CurrentUser> user;
*
- * @Inject
+ * {@literal @}Inject
* Foo(PermissionBackend permissions, Provider<CurrentUser> user) {
* this.permissions = permissions;
* this.user = user;
diff --git a/java/com/google/gerrit/server/plugins/DisablePlugin.java b/java/com/google/gerrit/server/plugins/DisablePlugin.java
index 8adae52..9e238f8 100644
--- a/java/com/google/gerrit/server/plugins/DisablePlugin.java
+++ b/java/com/google/gerrit/server/plugins/DisablePlugin.java
@@ -45,12 +45,9 @@
}
@Override
- public Response<PluginInfo> apply(PluginResource resource, Input input) throws RestApiException {
- try {
- permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
- } catch (PermissionBackendException e) {
- throw new RestApiException("Could not check permission", e);
- }
+ public Response<PluginInfo> apply(PluginResource resource, Input input)
+ throws RestApiException, PermissionBackendException {
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
loader.checkRemoteAdminEnabled();
String name = resource.getName();
if (mandatoryPluginsCollection.contains(name)) {
diff --git a/java/com/google/gerrit/server/query/account/AccountPredicates.java b/java/com/google/gerrit/server/query/account/AccountPredicates.java
index 1eed7ea..e4da946 100644
--- a/java/com/google/gerrit/server/query/account/AccountPredicates.java
+++ b/java/com/google/gerrit/server/query/account/AccountPredicates.java
@@ -29,6 +29,7 @@
import com.google.gerrit.server.notedb.ChangeNotes;
import java.util.List;
+/** Utility class to create predicates for account index queries. */
public class AccountPredicates {
public static boolean hasActive(Predicate<AccountState> p) {
return QueryBuilder.find(p, AccountPredicate.class, AccountField.ACTIVE.getName()) != null;
@@ -130,6 +131,7 @@
return new CanSeeChangePredicate(args.permissionBackend, changeNotes);
}
+ /** Predicate that is mapped to a field in the account index. */
static class AccountPredicate extends IndexPredicate<AccountState>
implements Matchable<AccountState> {
AccountPredicate(FieldDef<AccountState, ?> def, String value) {
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 381dc6e..8879388 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -45,7 +45,6 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.server.ApprovalsUtil;
@@ -397,12 +396,7 @@
return Optional.empty();
}
- ObjectId id = ps.commitId();
- Whitespace ws = Whitespace.IGNORE_NONE;
- PatchListKey pk =
- parentCount > 1
- ? PatchListKey.againstParentNum(1, id, ws)
- : PatchListKey.againstDefaultBase(id, ws);
+ PatchListKey pk = PatchListKey.againstBase(ps.commitId(), parentCount);
DiffSummaryKey key = DiffSummaryKey.fromPatchListKey(pk);
try {
diffSummary = Optional.of(patchListCache.getDiffSummary(key, c.getProject()));
diff --git a/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java
index 7428e3a..a176a58 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java
@@ -19,6 +19,7 @@
import com.google.gerrit.index.query.Matchable;
import com.google.gerrit.index.query.Predicate;
+/** Predicate that is mapped to a field in the change index. */
public abstract class ChangeIndexPredicate extends IndexPredicate<ChangeData>
implements Matchable<ChangeData> {
/**
diff --git a/java/com/google/gerrit/server/query/group/GroupPredicates.java b/java/com/google/gerrit/server/query/group/GroupPredicates.java
index 17a7000..5231c5a 100644
--- a/java/com/google/gerrit/server/query/group/GroupPredicates.java
+++ b/java/com/google/gerrit/server/query/group/GroupPredicates.java
@@ -23,6 +23,7 @@
import com.google.gerrit.server.index.group.GroupField;
import java.util.Locale;
+/** Utility class to create predicates for group index queries. */
public class GroupPredicates {
public static Predicate<InternalGroup> id(AccountGroup.Id groupId) {
return new GroupPredicate(GroupField.ID, groupId.toString());
@@ -63,6 +64,7 @@
return new GroupPredicate(GroupField.SUBGROUP, subgroupUuid.get());
}
+ /** Predicate that is mapped to a field in the group index. */
static class GroupPredicate extends IndexPredicate<InternalGroup> {
GroupPredicate(FieldDef<InternalGroup, ?> def, String value) {
super(def, value);
diff --git a/java/com/google/gerrit/server/query/project/ProjectPredicates.java b/java/com/google/gerrit/server/query/project/ProjectPredicates.java
index 4e56fac..8b4048f 100644
--- a/java/com/google/gerrit/server/query/project/ProjectPredicates.java
+++ b/java/com/google/gerrit/server/query/project/ProjectPredicates.java
@@ -22,6 +22,7 @@
import com.google.gerrit.index.query.Predicate;
import java.util.Locale;
+/** Utility class to create predicates for project index queries. */
public class ProjectPredicates {
public static Predicate<ProjectData> name(Project.NameKey nameKey) {
return new ProjectPredicate(ProjectField.NAME, nameKey.get());
diff --git a/java/com/google/gerrit/server/restapi/access/AccessCollection.java b/java/com/google/gerrit/server/restapi/access/AccessCollection.java
index 8ae2ce7..d8832a2 100644
--- a/java/com/google/gerrit/server/restapi/access/AccessCollection.java
+++ b/java/com/google/gerrit/server/restapi/access/AccessCollection.java
@@ -24,6 +24,7 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
+/** REST collection that serves requests to {@code /access/}. */
@Singleton
public class AccessCollection implements RestCollection<TopLevelResource, AccessResource> {
private final Provider<ListAccess> list;
diff --git a/java/com/google/gerrit/server/restapi/access/AccessResource.java b/java/com/google/gerrit/server/restapi/access/AccessResource.java
index 915165b..4847da4 100644
--- a/java/com/google/gerrit/server/restapi/access/AccessResource.java
+++ b/java/com/google/gerrit/server/restapi/access/AccessResource.java
@@ -18,6 +18,13 @@
import com.google.gerrit.extensions.restapi.RestView;
import com.google.inject.TypeLiteral;
+/**
+ * REST resource that represents members in {@link AccessCollection}.
+ *
+ * <p>{@link AccessCollection} doesn't support accessing single members, hence this class only
+ * defines the {@link TypeLiteral} for the resource kind that is needed for binding the REST
+ * collection.
+ */
public class AccessResource implements RestResource {
public static final TypeLiteral<RestView<AccessResource>> ACCESS_KIND =
new TypeLiteral<RestView<AccessResource>>() {};
diff --git a/java/com/google/gerrit/server/restapi/access/ListAccess.java b/java/com/google/gerrit/server/restapi/access/ListAccess.java
index 2520821..1e1bade 100644
--- a/java/com/google/gerrit/server/restapi/access/ListAccess.java
+++ b/java/com/google/gerrit/server/restapi/access/ListAccess.java
@@ -27,6 +27,11 @@
import java.util.TreeMap;
import org.kohsuke.args4j.Option;
+/**
+ * REST endpoint to list members of the {@link AccessCollection}.
+ *
+ * <p>This REST endpoint handles {@code GET /access/} requests.
+ */
public class ListAccess implements RestReadView<TopLevelResource> {
@Option(
diff --git a/java/com/google/gerrit/server/restapi/access/Module.java b/java/com/google/gerrit/server/restapi/access/Module.java
index 7da2e26b..3a4955d 100644
--- a/java/com/google/gerrit/server/restapi/access/Module.java
+++ b/java/com/google/gerrit/server/restapi/access/Module.java
@@ -19,6 +19,7 @@
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestApiModule;
+/** Guice module that binds all REST endpoints for {@code /access/}. */
public class Module extends RestApiModule {
@Override
protected void configure() {
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index e220f35..aa36b73 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -30,7 +30,6 @@
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
@@ -120,7 +119,8 @@
@Override
public Response<?> apply(ChangeResource resource, IdString id, FileContentInput input)
- throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
+ throws AuthException, BadRequestException, ResourceConflictException, IOException,
+ PermissionBackendException {
putEdit.apply(resource, id.get(), input);
return Response.none();
}
@@ -280,19 +280,24 @@
@Override
public Response<?> apply(ChangeEditResource rsrc, FileContentInput input)
- throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
+ throws AuthException, BadRequestException, ResourceConflictException, IOException,
+ PermissionBackendException {
return apply(rsrc.getChangeResource(), rsrc.getPath(), input);
}
public Response<?> apply(ChangeResource rsrc, String path, FileContentInput input)
- throws ResourceConflictException, AuthException, IOException, PermissionBackendException {
+ throws AuthException, BadRequestException, ResourceConflictException, IOException,
+ PermissionBackendException {
if (Strings.isNullOrEmpty(path) || path.charAt(0) == '/') {
throw new ResourceConflictException("Invalid path: " + path);
}
- RawInput newContent = input.content;
+ if (input.content == null) {
+ throw new BadRequestException("new content required");
+ }
+
try (Repository repository = repositoryManager.openRepository(rsrc.getProject())) {
- editModifier.modifyFile(repository, rsrc.getNotes(), path, newContent);
+ editModifier.modifyFile(repository, rsrc.getNotes(), path, input.content);
} catch (InvalidChangeOperationException e) {
throw new ResourceConflictException(e.getMessage());
}
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java
index 725defc..2902eca 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPick.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -39,7 +39,6 @@
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.submit.IntegrationException;
-import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -52,7 +51,6 @@
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final PermissionBackend permissionBackend;
- private final BatchUpdate.Factory updateFactory;
private final CherryPickChange cherryPickChange;
private final ChangeJson.Factory json;
private final ContributorAgreementsChecker contributorAgreements;
@@ -61,13 +59,11 @@
@Inject
CherryPick(
PermissionBackend permissionBackend,
- BatchUpdate.Factory updateFactory,
CherryPickChange cherryPickChange,
ChangeJson.Factory json,
ContributorAgreementsChecker contributorAgreements,
ProjectCache projectCache) {
this.permissionBackend = permissionBackend;
- this.updateFactory = updateFactory;
this.cherryPickChange = cherryPickChange;
this.json = json;
this.contributorAgreements = contributorAgreements;
@@ -96,7 +92,6 @@
try {
CherryPickChange.Result cherryPickResult =
cherryPickChange.cherryPick(
- updateFactory,
rsrc.getChange(),
rsrc.getPatchSet(),
input,
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 255c15c..3496491 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -109,6 +109,7 @@
private final ProjectCache projectCache;
private final ApprovalsUtil approvalsUtil;
private final NotifyResolver notifyResolver;
+ private final BatchUpdate.Factory batchUpdateFactory;
@Inject
CherryPickChange(
@@ -124,7 +125,8 @@
ChangeNotes.Factory changeNotesFactory,
ProjectCache projectCache,
ApprovalsUtil approvalsUtil,
- NotifyResolver notifyResolver) {
+ NotifyResolver notifyResolver,
+ BatchUpdate.Factory batchUpdateFactory) {
this.seq = seq;
this.queryProvider = queryProvider;
this.gitManager = gitManager;
@@ -138,12 +140,12 @@
this.projectCache = projectCache;
this.approvalsUtil = approvalsUtil;
this.notifyResolver = notifyResolver;
+ this.batchUpdateFactory = batchUpdateFactory;
}
/**
* This function is used for cherry picking a change.
*
- * @param batchUpdateFactory Used for applying changes to the database.
* @param change Change to cherry pick.
* @param patch The patch of that change.
* @param input Input object for different configurations of cherry pick.
@@ -158,22 +160,17 @@
* @throws ConfigInvalidException Can't find account to notify.
* @throws NoSuchProjectException Can't find project state.
*/
- public Result cherryPick(
- BatchUpdate.Factory batchUpdateFactory,
- Change change,
- PatchSet patch,
- CherryPickInput input,
- BranchNameKey dest)
+ public Result cherryPick(Change change, PatchSet patch, CherryPickInput input, BranchNameKey dest)
throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
RestApiException, ConfigInvalidException, NoSuchProjectException {
return cherryPick(
- batchUpdateFactory,
change,
change.getProject(),
patch.commitId(),
input,
dest,
false,
+ TimeUtil.nowTs(),
null,
null,
null,
@@ -185,7 +182,6 @@
* This function is called directly to cherry pick a commit. Also, it is used to cherry pick a
* change as well as long as sourceChange is not null.
*
- * @param batchUpdateFactory Used for applying changes to the database.
* @param sourceChange Change to cherry pick. Can be null, and then the function will only cherry
* pick a commit.
* @param project Project name
@@ -203,7 +199,6 @@
* @throws NoSuchProjectException Can't find project state.
*/
public Result cherryPick(
- BatchUpdate.Factory batchUpdateFactory,
@Nullable Change sourceChange,
Project.NameKey project,
ObjectId sourceCommit,
@@ -212,13 +207,13 @@
throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
RestApiException, ConfigInvalidException, NoSuchProjectException {
return cherryPick(
- batchUpdateFactory,
sourceChange,
project,
sourceCommit,
input,
dest,
false,
+ TimeUtil.nowTs(),
null,
null,
null,
@@ -231,7 +226,6 @@
* null) with a few other parameters that are especially useful for cherry-picking a commit that
* is the revert-of another change.
*
- * @param batchUpdateFactory Used for applying changes to the database.
* @param sourceChange Change to cherry pick. Can be null, and then the function will only cherry
* pick a commit.
* @param project Project name
@@ -240,6 +234,7 @@
* @param dest Destination branch for the cherry pick.
* @param ignoreIdenticalTree When false, we throw an error when trying to cherry-pick creates an
* empty commit. When true, we allow creation of an empty commit.
+ * @param timestamp the current timestamp.
* @param topic Topic name for the change created.
* @param revertedChange The id of the change that is reverted. This is used for the "revertOf"
* field to mark the created cherry pick change as "revertOf" the original change that was
@@ -262,13 +257,13 @@
* @throws NoSuchProjectException Can't find project state.
*/
public Result cherryPick(
- BatchUpdate.Factory batchUpdateFactory,
@Nullable Change sourceChange,
Project.NameKey project,
ObjectId sourceCommit,
CherryPickInput input,
BranchNameKey dest,
boolean ignoreIdenticalTree,
+ Timestamp timestamp,
@Nullable String topic,
@Nullable Change.Id revertedChange,
@Nullable ObjectId changeIdForNewChange,
@@ -306,8 +301,7 @@
String message = Strings.nullToEmpty(input.message).trim();
message = message.isEmpty() ? commitToCherryPick.getFullMessage() : message;
- Timestamp now = TimeUtil.nowTs();
- PersonIdent committerIdent = identifiedUser.newCommitterIdent(now, serverTimeZone);
+ PersonIdent committerIdent = identifiedUser.newCommitterIdent(timestamp, serverTimeZone);
final ObjectId generatedChangeId =
changeIdForNewChange != null ? changeIdForNewChange : Change.generateChangeId();
@@ -359,7 +353,7 @@
+ " reside on the same branch. "
+ "Cannot create a new patch set.");
}
- try (BatchUpdate bu = batchUpdateFactory.create(project, identifiedUser, now)) {
+ try (BatchUpdate bu = batchUpdateFactory.create(project, identifiedUser, timestamp)) {
bu.setRepository(git, revWalk, oi);
bu.setNotify(resolveNotify(input));
Change.Id changeId;
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
index 729e32d..8b5b07c 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -35,7 +35,6 @@
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.submit.IntegrationException;
-import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -46,7 +45,6 @@
@Singleton
public class CherryPickCommit implements RestModifyView<CommitResource, CherryPickInput> {
private final PermissionBackend permissionBackend;
- private final BatchUpdate.Factory updateFactory;
private final Provider<CurrentUser> user;
private final CherryPickChange cherryPickChange;
private final ChangeJson.Factory json;
@@ -55,13 +53,11 @@
@Inject
CherryPickCommit(
PermissionBackend permissionBackend,
- BatchUpdate.Factory updateFactory,
Provider<CurrentUser> user,
CherryPickChange cherryPickChange,
ChangeJson.Factory json,
ContributorAgreementsChecker contributorAgreements) {
this.permissionBackend = permissionBackend;
- this.updateFactory = updateFactory;
this.user = user;
this.cherryPickChange = cherryPickChange;
this.json = json;
@@ -92,7 +88,6 @@
try {
CherryPickChange.Result cherryPickResult =
cherryPickChange.cherryPick(
- updateFactory,
null,
projectName,
rsrc.getCommit(),
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index 310f71b..c0b28d2 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -20,6 +20,7 @@
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.entities.BranchNameKey;
@@ -86,7 +87,6 @@
import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoMergeBaseException;
-import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
@@ -104,6 +104,8 @@
@Singleton
public class CreateChange
implements RestCollectionModifyView<TopLevelResource, ChangeResource, ChangeInput> {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final BatchUpdate.Factory updateFactory;
private final String anonymousCowardName;
private final GitRepositoryManager gitManager;
@@ -294,20 +296,30 @@
BatchUpdate.Factory updateFactory)
throws RestApiException, PermissionBackendException, IOException, ConfigInvalidException,
UpdateException {
+ logger.atFine().log(
+ "Creating new change for target branch %s in project %s"
+ + " (new branch = %s, base change = %s, base commit = %s)",
+ input.branch, projectState.getName(), input.newBranch, input.baseChange, input.baseCommit);
+
try (Repository git = gitManager.openRepository(projectState.getNameKey());
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader)) {
PatchSet basePatchSet = null;
List<String> groups = Collections.emptyList();
+
if (input.baseChange != null) {
ChangeNotes baseChange = getBaseChange(input.baseChange);
basePatchSet = psUtil.current(baseChange);
groups = basePatchSet.groups();
+ logger.atFine().log("base patch set = %s (groups = %s)", basePatchSet.id(), groups);
}
+
ObjectId parentCommit =
getParentCommit(
git, rw, input.branch, input.newBranch, basePatchSet, input.baseCommit, input.merge);
+ logger.atFine().log(
+ "parent commit = %s", parentCommit != null ? parentCommit.name() : "NULL");
RevCommit mergeTip = parentCommit == null ? null : rw.parseCommit(parentCommit);
@@ -464,6 +476,7 @@
RevCommit mergeTip,
String commitMessage)
throws IOException {
+ logger.atFine().log("Creating empty commit");
CommitBuilder commit = new CommitBuilder();
if (mergeTip == null) {
commit.setTreeId(emptyTreeId(oi));
@@ -487,6 +500,9 @@
PersonIdent authorIdent,
String commitMessage)
throws RestApiException, IOException {
+ logger.atFine().log(
+ "Creating merge commit: source = %s, strategy = %s", merge.source, merge.strategy);
+
if (Strings.isNullOrEmpty(merge.source)) {
throw new BadRequestException("merge.source must be non-empty");
}
@@ -500,6 +516,7 @@
// default merge strategy from project settings
String mergeStrategy =
firstNonNull(Strings.emptyToNull(merge.strategy), mergeUtil.mergeStrategyName());
+ logger.atFine().log("merge strategy = %s", mergeStrategy);
try {
return MergeUtil.createMergeCommit(
@@ -512,12 +529,8 @@
commitMessage,
rw);
} catch (NoMergeBaseException e) {
- if (MergeBaseFailureReason.TOO_MANY_MERGE_BASES == e.getReason()
- || MergeBaseFailureReason.CONFLICTS_DURING_MERGE_BASE_CALCULATION == e.getReason()) {
- throw new ResourceConflictException(
- String.format("Cannot create merge commit: %s", e.getMessage()), e);
- }
- throw e;
+ throw new ResourceConflictException(
+ String.format("Cannot create merge commit: %s", e.getMessage()), e);
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetDiff.java b/java/com/google/gerrit/server/restapi/change/GetDiff.java
index 82bdc34..c1d286a 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDiff.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDiff.java
@@ -29,6 +29,7 @@
import com.google.gerrit.extensions.common.DiffWebLinkInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -102,8 +103,8 @@
@Override
public Response<DiffInfo> apply(FileResource resource)
- throws ResourceConflictException, ResourceNotFoundException, AuthException,
- InvalidChangeOperationException, IOException, PermissionBackendException {
+ throws BadRequestException, ResourceConflictException, ResourceNotFoundException,
+ AuthException, InvalidChangeOperationException, IOException, PermissionBackendException {
DiffPreferencesInfo prefs = new DiffPreferencesInfo();
if (whitespace != null) {
prefs.ignoreWhitespace = whitespace;
@@ -130,6 +131,9 @@
RevisionResource baseResource =
revisions.parse(resource.getRevision().getChangeResource(), IdString.fromDecoded(base));
basePatchSet = baseResource.getPatchSet();
+ if (basePatchSet.id().get() == 0) {
+ throw new BadRequestException("edit not allowed as base");
+ }
psf = patchScriptFactoryFactory.create(notes, fileName, basePatchSet.id(), pId, prefs);
} else if (parentNum > 0) {
psf = patchScriptFactoryFactory.create(notes, fileName, parentNum - 1, pId, prefs);
diff --git a/java/com/google/gerrit/server/restapi/change/GetRelated.java b/java/com/google/gerrit/server/restapi/change/GetRelated.java
index 5f075f6..6eff9b0 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRelated.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRelated.java
@@ -19,6 +19,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSet;
@@ -52,6 +53,8 @@
@Singleton
public class GetRelated implements RestReadView<RevisionResource> {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final Provider<InternalChangeQuery> queryProvider;
private final PatchSetUtil psUtil;
private final RelatedChangesSorter sorter;
@@ -84,6 +87,7 @@
private List<RelatedChangeAndCommitInfo> getRelated(RevisionResource rsrc)
throws IOException, PermissionBackendException {
Set<String> groups = getAllGroups(rsrc.getNotes(), psUtil);
+ logger.atFine().log("groups = %s", groups);
if (groups.isEmpty()) {
return Collections.emptyList();
}
@@ -101,6 +105,7 @@
boolean isEdit = rsrc.getEdit().isPresent();
PatchSet basePs = isEdit ? rsrc.getEdit().get().getBasePatchSet() : rsrc.getPatchSet();
+ logger.atFine().log("isEdit = %s, basePs = %s", isEdit, basePs);
cds = reloadChangeIfStale(cds, rsrc.getChange(), basePs);
@@ -111,6 +116,9 @@
// Replace base of an edit with the edit itself.
ps = rsrc.getPatchSet();
commit = rsrc.getEdit().get().getEditCommit();
+ logger.atFine().log(
+ "Replaced base of edit (patch set %s, commit %s) with edit (patch set %s, commit %s)",
+ d.patchSet().id(), d.commit(), ps.id(), commit);
} else {
commit = d.commit();
}
diff --git a/java/com/google/gerrit/server/restapi/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java
index d0b2562..7ebb954 100644
--- a/java/com/google/gerrit/server/restapi/change/Revert.java
+++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -14,17 +14,12 @@
package com.google.gerrit.server.restapi.change;
-import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.PatchSet;
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.RevertInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -33,49 +28,25 @@
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.server.ApprovalsUtil;
-import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.NotifyResolver;
-import com.google.gerrit.server.extensions.events.ChangeReverted;
import com.google.gerrit.server.git.CommitUtil;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.mail.send.RevertedSender;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.update.BatchUpdate;
-import com.google.gerrit.server.update.BatchUpdateOp;
-import com.google.gerrit.server.update.ChangeContext;
-import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.sql.Timestamp;
-import java.util.HashSet;
-import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
@Singleton
public class Revert
@@ -83,52 +54,25 @@
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final PermissionBackend permissionBackend;
- private final BatchUpdate.Factory updateFactory;
- private final GitRepositoryManager repoManager;
- private final ChangeInserter.Factory changeInserterFactory;
- private final ChangeMessagesUtil cmUtil;
- private final Sequences seq;
private final PatchSetUtil psUtil;
- private final RevertedSender.Factory revertedSenderFactory;
private final ChangeJson.Factory json;
- private final ApprovalsUtil approvalsUtil;
- private final ChangeReverted changeReverted;
private final ContributorAgreementsChecker contributorAgreements;
private final ProjectCache projectCache;
- private final NotifyResolver notifyResolver;
private final CommitUtil commitUtil;
@Inject
Revert(
PermissionBackend permissionBackend,
- BatchUpdate.Factory updateFactory,
- GitRepositoryManager repoManager,
- ChangeInserter.Factory changeInserterFactory,
- ChangeMessagesUtil cmUtil,
- Sequences seq,
PatchSetUtil psUtil,
- RevertedSender.Factory revertedSenderFactory,
ChangeJson.Factory json,
- ApprovalsUtil approvalsUtil,
- ChangeReverted changeReverted,
ContributorAgreementsChecker contributorAgreements,
ProjectCache projectCache,
- NotifyResolver notifyResolver,
CommitUtil commitUtil) {
this.permissionBackend = permissionBackend;
- this.updateFactory = updateFactory;
- this.repoManager = repoManager;
- this.changeInserterFactory = changeInserterFactory;
- this.cmUtil = cmUtil;
- this.seq = seq;
this.psUtil = psUtil;
- this.revertedSenderFactory = revertedSenderFactory;
this.json = json;
- this.approvalsUtil = approvalsUtil;
- this.changeReverted = changeReverted;
this.contributorAgreements = contributorAgreements;
this.projectCache = projectCache;
- this.notifyResolver = notifyResolver;
this.commitUtil = commitUtil;
}
@@ -144,70 +88,19 @@
contributorAgreements.check(rsrc.getProject(), rsrc.getUser());
permissionBackend.user(rsrc.getUser()).ref(change.getDest()).check(CREATE_CHANGE);
projectCache.checkedGet(rsrc.getProject()).checkStatePermitsWrite();
-
- Change.Id revertId = revert(updateFactory, rsrc.getNotes(), rsrc.getUser(), input);
- return Response.ok(json.noOptions().format(rsrc.getProject(), revertId));
- }
-
- private Change.Id revert(
- BatchUpdate.Factory updateFactory, ChangeNotes notes, CurrentUser user, RevertInput input)
- throws IOException, RestApiException, UpdateException, ConfigInvalidException {
+ ChangeNotes notes = rsrc.getNotes();
Change.Id changeIdToRevert = notes.getChangeId();
PatchSet.Id patchSetId = notes.getChange().currentPatchSetId();
PatchSet patch = psUtil.get(notes, patchSetId);
if (patch == null) {
throw new ResourceNotFoundException(changeIdToRevert.toString());
}
-
- Project.NameKey project = notes.getProjectName();
- try (Repository git = repoManager.openRepository(project);
- ObjectInserter oi = git.newObjectInserter();
- ObjectReader reader = oi.newReader();
- RevWalk revWalk = new RevWalk(reader)) {
-
- Timestamp now = TimeUtil.nowTs();
- ObjectId generatedChangeId = Change.generateChangeId();
- Change changeToRevert = notes.getChange();
- ObjectId revertCommitId =
- commitUtil.createRevertCommit(
- input.message, notes, user, generatedChangeId, now, oi, revWalk);
-
- RevCommit revertCommit = revWalk.parseCommit(revertCommitId);
-
- Change.Id changeId = Change.id(seq.nextChangeId());
- NotifyResolver.Result notify =
- notifyResolver.resolve(
- firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails);
-
- ChangeInserter ins =
- changeInserterFactory
- .create(changeId, revertCommit, notes.getChange().getDest().branch())
- .setTopic(input.topic == null ? changeToRevert.getTopic() : input.topic.trim());
- ins.setMessage("Uploaded patch set 1.");
-
- ReviewerSet reviewerSet = approvalsUtil.getReviewers(notes);
-
- Set<Account.Id> reviewers = new HashSet<>();
- reviewers.add(changeToRevert.getOwner());
- reviewers.addAll(reviewerSet.byState(ReviewerStateInternal.REVIEWER));
- reviewers.remove(user.getAccountId());
- Set<Account.Id> ccs = new HashSet<>(reviewerSet.byState(ReviewerStateInternal.CC));
- ccs.remove(user.getAccountId());
- ins.setReviewersAndCcs(reviewers, ccs);
- ins.setRevertOf(changeIdToRevert);
-
- try (BatchUpdate bu = updateFactory.create(project, user, now)) {
- bu.setRepository(git, revWalk, oi);
- bu.setNotify(notify);
- bu.insertChange(ins);
- bu.addOp(changeId, new NotifyOp(changeToRevert, ins));
- bu.addOp(changeToRevert.getId(), new PostRevertedMessageOp(generatedChangeId));
- bu.execute();
- }
- return changeId;
- } catch (RepositoryNotFoundException e) {
- throw new ResourceNotFoundException(changeIdToRevert.toString(), e);
- }
+ Timestamp timestamp = TimeUtil.nowTs();
+ return Response.ok(
+ json.noOptions()
+ .format(
+ rsrc.getProject(),
+ commitUtil.createRevertChange(notes, rsrc.getUser(), input, timestamp)));
}
@Override
@@ -231,49 +124,4 @@
.ref(change.getDest())
.testCond(CREATE_CHANGE)));
}
-
- private class NotifyOp implements BatchUpdateOp {
- private final Change change;
- private final ChangeInserter ins;
-
- NotifyOp(Change change, ChangeInserter ins) {
- this.change = change;
- this.ins = ins;
- }
-
- @Override
- public void postUpdate(Context ctx) throws Exception {
- changeReverted.fire(change, ins.getChange(), ctx.getWhen());
- try {
- RevertedSender cm = revertedSenderFactory.create(ctx.getProject(), change.getId());
- cm.setFrom(ctx.getAccountId());
- cm.setNotify(ctx.getNotify(change.getId()));
- cm.send();
- } catch (Exception err) {
- logger.atSevere().withCause(err).log(
- "Cannot send email for revert change %s", change.getId());
- }
- }
- }
-
- private class PostRevertedMessageOp implements BatchUpdateOp {
- private final ObjectId computedChangeId;
-
- PostRevertedMessageOp(ObjectId computedChangeId) {
- this.computedChangeId = computedChangeId;
- }
-
- @Override
- public boolean updateChange(ChangeContext ctx) {
- Change change = ctx.getChange();
- PatchSet.Id patchSetId = change.currentPatchSetId();
- ChangeMessage changeMessage =
- ChangeMessagesUtil.newMessage(
- ctx,
- "Created a revert of this change as I" + computedChangeId.name(),
- ChangeMessagesUtil.TAG_REVERT);
- cmUtil.addChangeMessage(ctx.getUpdate(patchSetId), changeMessage);
- return true;
- }
- }
}
diff --git a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
index a7d19d1..8fc59a2 100644
--- a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
+++ b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
@@ -34,6 +34,7 @@
import com.google.gerrit.extensions.api.changes.RevertInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevertSubmissionInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -73,6 +74,7 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
+import java.sql.Timestamp;
import java.text.MessageFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -83,6 +85,8 @@
import java.util.List;
import java.util.Queue;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang.RandomStringUtils;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -110,16 +114,17 @@
private final ChangeMessagesUtil cmUtil;
private final CommitUtil commitUtil;
private final ChangeNotes.Factory changeNotesFactory;
- private final ChangeResource.Factory changeResourceFactory;
private final ChangeReverted changeReverted;
private final RevertedSender.Factory revertedSenderFactory;
private final Sequences seq;
private final NotifyResolver notifyResolver;
- private final Revert revert;
private final BatchUpdate.Factory updateFactory;
private CherryPickInput cherryPickInput;
private List<ChangeInfo> results;
+ private static final Pattern patternRevertSubject = Pattern.compile("Revert \"(.+)\"");
+ private static final Pattern patternRevertSubjectWithNum =
+ Pattern.compile("Revert\\^(\\d+) \"(.+)\"");
@Inject
RevertSubmission(
@@ -136,12 +141,10 @@
ChangeMessagesUtil cmUtil,
CommitUtil commitUtil,
ChangeNotes.Factory changeNotesFactory,
- ChangeResource.Factory changeResourceFactory,
ChangeReverted changeReverted,
RevertedSender.Factory revertedSenderFactory,
Sequences seq,
NotifyResolver notifyResolver,
- Revert revert,
BatchUpdate.Factory updateFactory) {
this.queryProvider = queryProvider;
this.user = user;
@@ -156,12 +159,10 @@
this.cmUtil = cmUtil;
this.commitUtil = commitUtil;
this.changeNotesFactory = changeNotesFactory;
- this.changeResourceFactory = changeResourceFactory;
this.changeReverted = changeReverted;
this.revertedSenderFactory = revertedSenderFactory;
this.seq = seq;
this.notifyResolver = notifyResolver;
- this.revert = revert;
this.updateFactory = updateFactory;
results = new ArrayList<>();
cherryPickInput = null;
@@ -185,6 +186,22 @@
}
List<ChangeData> changeDatas = queryProvider.get().bySubmissionId(submissionId);
+ checkPermissionsForAllChanges(changeResource, changeDatas);
+ input.topic = createTopic(input.topic, submissionId);
+ return Response.ok(revertSubmission(changeDatas, input));
+ }
+
+ private String createTopic(String topic, String submissionId) {
+ if (topic == null) {
+ return String.format(
+ "revert-%s-%s", submissionId, RandomStringUtils.randomAlphabetic(10).toUpperCase());
+ }
+ return topic;
+ }
+
+ private void checkPermissionsForAllChanges(
+ ChangeResource changeResource, List<ChangeData> changeDatas)
+ throws IOException, AuthException, PermissionBackendException, ResourceConflictException {
for (ChangeData changeData : changeDatas) {
Change change = changeData.change();
@@ -203,23 +220,130 @@
"current patch set %s of change %s not found",
change.currentPatchSetId(), change.currentPatchSetId()));
}
-
- if (input.topic == null) {
- input.topic =
- String.format(
- "revert-%s-%s", submissionId, RandomStringUtils.randomAlphabetic(10).toUpperCase());
- }
-
- return Response.ok(revertSubmission(changeDatas, input));
}
private RevertSubmissionInfo revertSubmission(
List<ChangeData> changeData, RevertInput revertInput)
- throws RestApiException, IOException, UpdateException, PermissionBackendException,
- NoSuchProjectException, ConfigInvalidException, StorageException {
+ throws RestApiException, IOException, UpdateException, ConfigInvalidException,
+ StorageException {
+
Multimap<BranchNameKey, ChangeData> changesPerProjectAndBranch = ArrayListMultimap.create();
changeData.stream().forEach(c -> changesPerProjectAndBranch.put(c.change().getDest(), c));
+ cherryPickInput = createCherryPickInput(revertInput);
+ Timestamp timestamp = TimeUtil.nowTs();
+ for (BranchNameKey projectAndBranch : changesPerProjectAndBranch.keySet()) {
+ cherryPickInput.base = null;
+ Project.NameKey project = projectAndBranch.project();
+ cherryPickInput.destination = projectAndBranch.branch();
+ Collection<ChangeData> changesInProjectAndBranch =
+ changesPerProjectAndBranch.get(projectAndBranch);
+
+ // Sort the changes topologically.
+ Iterator<PatchSetData> sortedChangesInProjectAndBranch =
+ sorter.sort(changesInProjectAndBranch).iterator();
+
+ Set<ObjectId> commitIdsInProjectAndBranch =
+ changesInProjectAndBranch.stream()
+ .map(c -> c.currentPatchSet().commitId())
+ .collect(Collectors.toSet());
+
+ revertAllChangesInProjectAndBranch(
+ revertInput,
+ project,
+ sortedChangesInProjectAndBranch,
+ commitIdsInProjectAndBranch,
+ timestamp);
+ }
+ results.sort(Comparator.comparing(c -> c.revertOf));
+ RevertSubmissionInfo revertSubmissionInfo = new RevertSubmissionInfo();
+ revertSubmissionInfo.revertChanges = results;
+ return revertSubmissionInfo;
+ }
+
+ private void revertAllChangesInProjectAndBranch(
+ RevertInput revertInput,
+ Project.NameKey project,
+ Iterator<PatchSetData> sortedChangesInProjectAndBranch,
+ Set<ObjectId> commitIdsInProjectAndBranch,
+ Timestamp timestamp)
+ throws IOException, RestApiException, UpdateException, ConfigInvalidException {
+
+ String groupName = null;
+ String initialMessage = revertInput.message;
+ while (sortedChangesInProjectAndBranch.hasNext()) {
+ ChangeNotes changeNotes = sortedChangesInProjectAndBranch.next().data().notes();
+ if (cherryPickInput.base == null) {
+ cherryPickInput.base = getBase(changeNotes, commitIdsInProjectAndBranch).name();
+ }
+
+ revertInput.message = getMessage(initialMessage, changeNotes);
+ if (cherryPickInput.base.equals(changeNotes.getCurrentPatchSet().commitId().getName())) {
+ // This is the code in case this is the first revert of this project + branch, and the
+ // revert would be on top of the change being reverted.
+ craeteNormalRevert(revertInput, changeNotes, timestamp);
+ groupName = cherryPickInput.base;
+ } else {
+ // This is the code in case this is the second revert (or more) of this project + branch.
+ if (groupName == null) {
+ groupName = cherryPickInput.base;
+ }
+ createCherryPickedRevert(revertInput, project, groupName, changeNotes, timestamp);
+ }
+ }
+ }
+
+ private void createCherryPickedRevert(
+ RevertInput revertInput,
+ Project.NameKey project,
+ String groupName,
+ ChangeNotes changeNotes,
+ Timestamp timestamp)
+ throws IOException, ConfigInvalidException, UpdateException, RestApiException {
+ ObjectId revCommitId =
+ commitUtil.createRevertCommit(revertInput.message, changeNotes, user.get(), timestamp);
+ // TODO (paiking): As a future change, the revert should just be done directly on the
+ // target rather than just creating a commit and then cherry-picking it.
+ cherryPickInput.message = revertInput.message;
+ ObjectId generatedChangeId = Change.generateChangeId();
+ Change.Id cherryPickRevertChangeId = Change.id(seq.nextChangeId());
+ // TODO (paiking): In the the future, the timestamp should be the same for all the revert
+ // changes.
+ try (BatchUpdate bu = updateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.setNotify(
+ notifyResolver.resolve(
+ firstNonNull(cherryPickInput.notify, NotifyHandling.ALL),
+ cherryPickInput.notifyDetails));
+ bu.addOp(
+ changeNotes.getChange().getId(),
+ new CreateCherryPickOp(
+ revCommitId,
+ revertInput.topic,
+ generatedChangeId,
+ cherryPickRevertChangeId,
+ groupName,
+ timestamp));
+ bu.addOp(changeNotes.getChange().getId(), new PostRevertedMessageOp(generatedChangeId));
+ bu.addOp(
+ cherryPickRevertChangeId,
+ new NotifyOp(changeNotes.getChange(), cherryPickRevertChangeId));
+
+ bu.execute();
+ }
+ }
+
+ private void craeteNormalRevert(
+ RevertInput revertInput, ChangeNotes changeNotes, Timestamp timestamp)
+ throws IOException, RestApiException, UpdateException, ConfigInvalidException {
+
+ Change.Id revertId =
+ commitUtil.createRevertChange(changeNotes, user.get(), revertInput, timestamp);
+ results.add(json.noOptions().format(changeNotes.getProjectName(), revertId));
+ cherryPickInput.base =
+ changeNotesFactory.createChecked(revertId).getCurrentPatchSet().commitId().getName();
+ }
+
+ private CherryPickInput createCherryPickInput(RevertInput revertInput) {
cherryPickInput = new CherryPickInput();
// To create a revert change, we create a revert commit that is then cherry-picked. The revert
// change is created for the cherry-picked commit. Notifications are sent only for this change,
@@ -228,102 +352,48 @@
cherryPickInput.notifyDetails = revertInput.notifyDetails;
cherryPickInput.parent = 1;
cherryPickInput.keepReviewers = true;
-
- for (BranchNameKey projectAndBranch : changesPerProjectAndBranch.keySet()) {
- String groupName = null;
- Project.NameKey project = projectAndBranch.project();
- cherryPickInput.destination = projectAndBranch.branch();
- Collection<ChangeData> changesInProjectAndBranch =
- changesPerProjectAndBranch.get(projectAndBranch);
-
- // Sort the changes topologically.
- Iterator<PatchSetData> sortedChangesInProject =
- sorter.sort(changesInProjectAndBranch).iterator();
-
- Set<ObjectId> commitIdsInProjectAndBranch =
- changesInProjectAndBranch.stream()
- .map(c -> c.currentPatchSet().commitId())
- .collect(Collectors.toSet());
-
- while (sortedChangesInProject.hasNext()) {
- ChangeNotes changeNotes = sortedChangesInProject.next().data().notes();
- if (cherryPickInput.base == null) {
- cherryPickInput.base = getBase(changeNotes, commitIdsInProjectAndBranch).name();
- }
-
- revertInput.message = getMessage(revertInput, changeNotes);
- // This is the code in case this is the first revert of this project + branch, and the
- // revert would be on top of the change being reverted.
- if (cherryPickInput.base.equals(changeNotes.getCurrentPatchSet().commitId().getName())) {
- ChangeInfo revertChangeInfo =
- revert
- .apply(changeResourceFactory.create(changeNotes, user.get()), revertInput)
- .value();
- results.add(revertChangeInfo);
- cherryPickInput.base =
- changeNotesFactory
- .createChecked(Change.id(revertChangeInfo._number))
- .getCurrentPatchSet()
- .commitId()
- .getName();
- groupName = cherryPickInput.base;
- } else {
- // This is the code in case this is the second revert (or more) of this project + branch.
- ObjectId revCommitId =
- commitUtil.createRevertCommit(revertInput.message, changeNotes, user.get());
- // TODO (paiking): As a future change, the revert should just be done directly on the
- // target rather than just creating a commit and then cherry-picking it.
- cherryPickInput.message = revertInput.message;
- ObjectId generatedChangeId = Change.generateChangeId();
- Change.Id cherryPickRevertChangeId = Change.id(seq.nextChangeId());
- if (groupName == null) {
- groupName = cherryPickInput.base;
- }
- // TODO (paiking): In the the future, the timestamp should be the same for all the revert
- // changes.
- try (BatchUpdate bu = updateFactory.create(project, user.get(), TimeUtil.nowTs())) {
- bu.setNotify(
- notifyResolver.resolve(
- firstNonNull(cherryPickInput.notify, NotifyHandling.ALL),
- cherryPickInput.notifyDetails));
- bu.addOp(
- changeNotes.getChange().getId(),
- new CreateCherryPickOp(
- revCommitId,
- revertInput.topic,
- generatedChangeId,
- cherryPickRevertChangeId,
- groupName));
- bu.addOp(changeNotes.getChange().getId(), new PostRevertedMessageOp(generatedChangeId));
- bu.addOp(
- cherryPickRevertChangeId,
- new NotifyOp(changeNotes.getChange(), cherryPickRevertChangeId));
-
- bu.execute();
- }
- }
- }
- cherryPickInput.base = null;
- }
- results.sort(Comparator.comparing(c -> c.revertOf));
- RevertSubmissionInfo revertSubmissionInfo = new RevertSubmissionInfo();
- revertSubmissionInfo.revertChanges = results;
- return revertSubmissionInfo;
+ return cherryPickInput;
}
- private String getMessage(RevertInput revertInput, ChangeNotes changeNotes) {
+ private String getMessage(String initialMessage, ChangeNotes changeNotes) {
String subject = changeNotes.getChange().getSubject();
- if (subject.length() > 63) {
- subject = subject.substring(0, 59) + "...";
+ if (subject.length() > 60) {
+ subject = subject.substring(0, 56) + "...";
}
- if (revertInput.message == null) {
+ if (initialMessage == null) {
+ initialMessage =
+ MessageFormat.format(
+ ChangeMessages.get().revertSubmissionDefaultMessage,
+ changeNotes.getCurrentPatchSet().commitId().name());
+ }
+
+ // For performance purposes: Almost all cases will end here.
+ if (!subject.startsWith("Revert")) {
return MessageFormat.format(
- ChangeMessages.get().revertChangeDefaultMessage,
- subject,
+ ChangeMessages.get().revertSubmissionUserMessage, subject, initialMessage);
+ }
+
+ Matcher matcher = patternRevertSubjectWithNum.matcher(subject);
+
+ if (matcher.matches()) {
+ return MessageFormat.format(
+ ChangeMessages.get().revertSubmissionOfRevertSubmissionUserMessage,
+ Integer.valueOf(matcher.group(1)) + 1,
+ matcher.group(2),
changeNotes.getCurrentPatchSet().commitId().name());
}
- return String.format("Revert \"%s\"\n\n%s", subject, revertInput.message);
+ matcher = patternRevertSubject.matcher(subject);
+ if (matcher.matches()) {
+ return MessageFormat.format(
+ ChangeMessages.get().revertSubmissionOfRevertSubmissionUserMessage,
+ 2,
+ matcher.group(1),
+ changeNotes.getCurrentPatchSet().commitId().name());
+ }
+
+ return MessageFormat.format(
+ ChangeMessages.get().revertSubmissionUserMessage, subject, initialMessage);
}
/**
@@ -499,18 +569,21 @@
private final ObjectId computedChangeId;
private final Change.Id cherryPickRevertChangeId;
private final String groupName;
+ private final Timestamp timestamp;
CreateCherryPickOp(
ObjectId revCommitId,
String topic,
ObjectId computedChangeId,
Change.Id cherryPickRevertChangeId,
- String groupName) {
+ String groupName,
+ Timestamp timestamp) {
this.revCommitId = revCommitId;
this.topic = topic;
this.computedChangeId = computedChangeId;
this.cherryPickRevertChangeId = cherryPickRevertChangeId;
this.groupName = groupName;
+ this.timestamp = timestamp;
}
@Override
@@ -518,7 +591,6 @@
Change change = ctx.getChange();
Result cherryPickResult =
cherryPickChange.cherryPick(
- updateFactory,
change,
change.getProject(),
revCommitId,
@@ -526,6 +598,7 @@
BranchNameKey.create(
change.getProject(), RefNames.fullName(cherryPickInput.destination)),
true,
+ timestamp,
topic,
change.getId(),
computedChangeId,
diff --git a/java/com/google/gerrit/server/restapi/change/Submit.java b/java/com/google/gerrit/server/restapi/change/Submit.java
index 287c2bc..53e96f7 100644
--- a/java/com/google/gerrit/server/restapi/change/Submit.java
+++ b/java/com/google/gerrit/server/restapi/change/Submit.java
@@ -212,12 +212,10 @@
return Response.ok(new Output(change));
}
- String msg =
+ throw new IllegalStateException(
String.format(
"change %s of project %s unexpectedly had status %s after submit attempt",
- updatedChange.getId(), updatedChange.getProject(), updatedChange.getStatus());
- logger.atWarning().log(msg);
- throw new RestApiException(msg);
+ updatedChange.getId(), updatedChange.getProject(), updatedChange.getStatus()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CreateDashboard.java b/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
index 9904b1f..e9a0d7f 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
@@ -17,7 +17,7 @@
import com.google.gerrit.extensions.api.projects.DashboardInfo;
import com.google.gerrit.extensions.api.projects.SetDashboardInput;
import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
@@ -48,7 +48,7 @@
throws RestApiException, IOException, PermissionBackendException {
parent.getProjectState().checkStatePermitsWrite();
if (!DashboardsCollection.isDefaultDashboard(id)) {
- throw new ResourceNotFoundException(id);
+ throw new MethodNotAllowedException("cannot create non-default dashboard");
}
SetDefaultDashboard set = setDefault.get();
set.inherited = inherited;
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java b/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
index 9d9e5f5..e507f05 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
@@ -43,6 +43,6 @@
}
// TODO: Implement delete of dashboards by API.
- throw new MethodNotAllowedException();
+ throw new MethodNotAllowedException("cannot delete non-default dashboard");
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/SetDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDashboard.java
index e8e0c0d..56c62dc 100644
--- a/java/com/google/gerrit/server/restapi/project/SetDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/SetDashboard.java
@@ -40,7 +40,7 @@
return defaultSetter.get().apply(resource, input);
}
- // TODO: Implement creation/update of dashboards by API.
- throw new MethodNotAllowedException();
+ // TODO: Implement update of dashboards by API.
+ throw new MethodNotAllowedException("cannot update non-default dashboard");
}
}
diff --git a/java/com/google/gerrit/server/schema/JdbcUtil.java b/java/com/google/gerrit/server/schema/JdbcUtil.java
deleted file mode 100644
index 9f6822c..0000000
--- a/java/com/google/gerrit/server/schema/JdbcUtil.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2012 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.schema;
-
-import com.google.gerrit.common.UsedAt;
-
-public class JdbcUtil {
-
- public static String hostname(String hostname) {
- if (hostname == null || hostname.isEmpty()) {
- hostname = "localhost";
-
- } else if (hostname.contains(":") && !hostname.startsWith("[")) {
- hostname = "[" + hostname + "]";
- }
- return hostname;
- }
-
- // TODO(dborowitz): Still used by plugins post-ReviewDb?
- @UsedAt(UsedAt.Project.PLUGINS_ALL)
- public static String port(String port) {
- if (port != null && !port.isEmpty()) {
- return ":" + port;
- }
- return "";
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_180.java b/java/com/google/gerrit/server/schema/Schema_180.java
index 6912b3e..7e13d0d 100644
--- a/java/com/google/gerrit/server/schema/Schema_180.java
+++ b/java/com/google/gerrit/server/schema/Schema_180.java
@@ -14,6 +14,15 @@
package com.google.gerrit.server.schema;
+/**
+ * Schema 180 for Gerrit metadata.
+ *
+ * <p>180 is the first schema version that is supported by NoteDb. All previous schema versions were
+ * for ReviewDb. Since ReviewDb no longer exists those schema versions have been deleted.
+ *
+ * <p>Upgrading to this schema version creates the {@code refs/meta/version} ref in NoteDb that
+ * stores the number of the current schema version.
+ */
public class Schema_180 implements NoteDbSchemaVersion {
@Override
public void upgrade(Arguments args, UpdateUI ui) {
diff --git a/java/com/google/gerrit/server/schema/Schema_181.java b/java/com/google/gerrit/server/schema/Schema_181.java
index 3054ad3..d357ae7 100644
--- a/java/com/google/gerrit/server/schema/Schema_181.java
+++ b/java/com/google/gerrit/server/schema/Schema_181.java
@@ -17,10 +17,16 @@
import com.google.gerrit.gpg.PublicKeyStore;
import org.eclipse.jgit.lib.Repository;
+/**
+ * Schema 181 for Gerrit metadata.
+ *
+ * <p>Upgrading to this schema version populates the GPG subkey to master key map (see {@link
+ * PublicKeyStore}.
+ */
public class Schema_181 implements NoteDbSchemaVersion {
@Override
public void upgrade(Arguments args, UpdateUI ui) throws Exception {
- ui.message("Rebuild GPGP note map to build subkey to master key map");
+ ui.message("Rebuild GPG note map to build subkey to master key map");
try (Repository repo = args.repoManager.openRepository(args.allUsers);
PublicKeyStore store = new PublicKeyStore(repo)) {
store.rebuildSubkeyMasterKeyMap();
diff --git a/java/com/google/gerrit/server/submit/MergeSuperSet.java b/java/com/google/gerrit/server/submit/MergeSuperSet.java
index b36eb7d..f8540fb 100644
--- a/java/com/google/gerrit/server/submit/MergeSuperSet.java
+++ b/java/com/google/gerrit/server/submit/MergeSuperSet.java
@@ -18,7 +18,6 @@
import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
-import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.registration.DynamicItem;
@@ -58,6 +57,7 @@
public class MergeSuperSet {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private final ChangeData.Factory changeDataFactory;
private final Provider<InternalChangeQuery> queryProvider;
private final Provider<MergeOpRepoManager> repoManagerProvider;
private final DynamicItem<MergeSuperSetComputation> mergeSuperSetComputation;
@@ -72,6 +72,7 @@
@Inject
MergeSuperSet(
@GerritServerConfig Config cfg,
+ ChangeData.Factory changeDataFactory,
Provider<InternalChangeQuery> queryProvider,
Provider<MergeOpRepoManager> repoManagerProvider,
DynamicItem<MergeSuperSetComputation> mergeSuperSetComputation,
@@ -79,6 +80,7 @@
ProjectCache projectCache,
ChangeNotes.Factory notesFactory) {
this.cfg = cfg;
+ this.changeDataFactory = changeDataFactory;
this.queryProvider = queryProvider;
this.repoManagerProvider = repoManagerProvider;
this.mergeSuperSetComputation = mergeSuperSetComputation;
@@ -105,14 +107,7 @@
orm = repoManagerProvider.get();
closeOrm = true;
}
- List<ChangeData> cds = queryProvider.get().byLegacyChangeId(change.getId());
- checkState(
- cds.size() == 1,
- "Expected exactly one ChangeData for change ID %s, got %s",
- change.getId(),
- cds.size());
- ChangeData cd = Iterables.getFirst(cds, null);
-
+ ChangeData cd = changeDataFactory.create(change.getProject(), change.getId());
boolean visible = false;
if (cd != null) {
ProjectState projectState = projectCache.checkedGet(cd.project());
diff --git a/java/gerrit/AbstractCommitUserIdentityPredicate.java b/java/gerrit/AbstractCommitUserIdentityPredicate.java
index bd8cf1a..51c4a3b 100644
--- a/java/gerrit/AbstractCommitUserIdentityPredicate.java
+++ b/java/gerrit/AbstractCommitUserIdentityPredicate.java
@@ -30,6 +30,20 @@
import java.io.IOException;
import org.eclipse.jgit.lib.PersonIdent;
+/**
+ * Abstract Prolog predicate for a Git person identity of a change.
+ *
+ * <p>Checks that the terms that are provided as input to this Prolog predicate match a Git person
+ * identity of the change (either author or committer).
+ *
+ * <p>The terms that are provided as input to this Prolog predicate are:
+ *
+ * <ul>
+ * <li>a user ID term that matches the account ID of the Git person identity
+ * <li>a string atom that matches the full name of the Git person identity
+ * <li>a string atom that matches the email of the Git person identity
+ * </ul>
+ */
abstract class AbstractCommitUserIdentityPredicate extends Predicate.P3 {
private static final SymbolTerm user = SymbolTerm.intern("user", 1);
private static final SymbolTerm anonymous = SymbolTerm.intern("anonymous");
diff --git a/java/gerrit/PRED_change_branch_1.java b/java/gerrit/PRED_change_branch_1.java
index 4501169..62744f7 100644
--- a/java/gerrit/PRED_change_branch_1.java
+++ b/java/gerrit/PRED_change_branch_1.java
@@ -23,6 +23,16 @@
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
+/**
+ * Prolog predicate for the destination branch of a change.
+ *
+ * <p>Checks that the term that is provided as input to this Prolog predicate is a string atom that
+ * matches the destination branch of the change.
+ *
+ * <pre>
+ * 'change_branch'(-Branch)
+ * </pre>
+ */
public class PRED_change_branch_1 extends Predicate.P1 {
public PRED_change_branch_1(Term a1, Operation n) {
arg1 = a1;
diff --git a/java/gerrit/PRED_change_owner_1.java b/java/gerrit/PRED_change_owner_1.java
index d42c0e1..f6fbb80 100644
--- a/java/gerrit/PRED_change_owner_1.java
+++ b/java/gerrit/PRED_change_owner_1.java
@@ -25,6 +25,16 @@
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
+/**
+ * Prolog predicate for the owner of a change.
+ *
+ * <p>Checks that the term that is provided as input to this Prolog predicate is a user ID term that
+ * matches the account ID of the change owner.
+ *
+ * <pre>
+ * 'change_owner'(user(-ID))
+ * </pre>
+ */
public class PRED_change_owner_1 extends Predicate.P1 {
private static final SymbolTerm user = SymbolTerm.intern("user", 1);
diff --git a/java/gerrit/PRED_change_project_1.java b/java/gerrit/PRED_change_project_1.java
index a973e1c..b2ef109 100644
--- a/java/gerrit/PRED_change_project_1.java
+++ b/java/gerrit/PRED_change_project_1.java
@@ -23,6 +23,16 @@
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
+/**
+ * Prolog predicate for the project of a change.
+ *
+ * <p>Checks that the term that is provided as input to this Prolog predicate is a string atom that
+ * matches the project of the change.
+ *
+ * <pre>
+ * 'change_project'(-Project)
+ * </pre>
+ */
public class PRED_change_project_1 extends Predicate.P1 {
public PRED_change_project_1(Term a1, Operation n) {
arg1 = a1;
diff --git a/java/gerrit/PRED_change_topic_1.java b/java/gerrit/PRED_change_topic_1.java
index 11d737a..f0175ef 100644
--- a/java/gerrit/PRED_change_topic_1.java
+++ b/java/gerrit/PRED_change_topic_1.java
@@ -23,6 +23,16 @@
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
+/**
+ * Prolog predicate for the topic of a change.
+ *
+ * <p>Checks that the term that is provided as input to this Prolog predicate is a string atom that
+ * matches the topic of the change.
+ *
+ * <pre>
+ * 'change_topic'(-Topic)
+ * </pre>
+ */
public class PRED_change_topic_1 extends Predicate.P1 {
public PRED_change_topic_1(Term a1, Operation n) {
arg1 = a1;
diff --git a/java/gerrit/PRED_commit_author_3.java b/java/gerrit/PRED_commit_author_3.java
index 998b30e..3381344 100644
--- a/java/gerrit/PRED_commit_author_3.java
+++ b/java/gerrit/PRED_commit_author_3.java
@@ -21,6 +21,27 @@
import com.googlecode.prolog_cafe.lang.Term;
import org.eclipse.jgit.revwalk.RevCommit;
+/**
+ * Prolog predicate for the Git author of the current patch set of a change.
+ *
+ * <p>Checks that the terms that are provided as input to this Prolog predicate match the Git author
+ * of the current patch set of the change.
+ *
+ * <p>The terms that are provided as input to this Prolog predicate are:
+ *
+ * <ul>
+ * <li>a user ID term that matches the account ID of the Git author of the current patch set of
+ * the change
+ * <li>a string atom that matches the full name of the Git author of the current patch set of the
+ * change
+ * <li>a string atom that matches the email of the Git author of the current patch set of the
+ * change
+ * </ul>
+ *
+ * <pre>
+ * 'commit_author'(user(-ID), -FullName, -Email)
+ * </pre>
+ */
public class PRED_commit_author_3 extends AbstractCommitUserIdentityPredicate {
public PRED_commit_author_3(Term a1, Term a2, Term a3, Operation n) {
super(a1, a2, a3, n);
diff --git a/java/gerrit/PRED_commit_committer_3.java b/java/gerrit/PRED_commit_committer_3.java
index 293d8ce..1757336 100644
--- a/java/gerrit/PRED_commit_committer_3.java
+++ b/java/gerrit/PRED_commit_committer_3.java
@@ -21,6 +21,27 @@
import com.googlecode.prolog_cafe.lang.Term;
import org.eclipse.jgit.revwalk.RevCommit;
+/**
+ * Prolog predicate for the Git committer of the current patch set of a change.
+ *
+ * <p>Checks that the terms that are provided as input to this Prolog predicate match the Git
+ * committer of the current patch set of the change.
+ *
+ * <p>The terms that are provided as input to this Prolog predicate are:
+ *
+ * <ul>
+ * <li>a user ID term that matches the account ID of the Git committer of the current patch set of
+ * the change
+ * <li>a string atom that matches the full name of the Git committer of the current patch set of
+ * the change
+ * <li>a string atom that matches the email of the Git committer of the current patch set of the
+ * change
+ * </ul>
+ *
+ * <pre>
+ * 'commit_committer'(user(-ID), -FullName, -Email)
+ * </pre>
+ */
public class PRED_commit_committer_3 extends AbstractCommitUserIdentityPredicate {
public PRED_commit_committer_3(Term a1, Term a2, Term a3, Operation n) {
super(a1, a2, a3, n);
diff --git a/java/gerrit/PRED_commit_message_1.java b/java/gerrit/PRED_commit_message_1.java
index eb996d6..3485af6 100644
--- a/java/gerrit/PRED_commit_message_1.java
+++ b/java/gerrit/PRED_commit_message_1.java
@@ -24,7 +24,10 @@
import org.eclipse.jgit.revwalk.RevCommit;
/**
- * Returns the commit message as a symbol
+ * Prolog predicate for the commit message of a change.
+ *
+ * <p>Checks that the term that is provided as input to this Prolog predicate is a string atom that
+ * matches the commit message of the change.
*
* <pre>
* 'commit_message'(-Msg)
diff --git a/java/gerrit/PRED_project_default_submit_type_1.java b/java/gerrit/PRED_project_default_submit_type_1.java
index d70a9e4..77a0261 100644
--- a/java/gerrit/PRED_project_default_submit_type_1.java
+++ b/java/gerrit/PRED_project_default_submit_type_1.java
@@ -24,6 +24,16 @@
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
+/**
+ * Prolog predicate for the default submit type of the project of a change.
+ *
+ * <p>Checks that the term that is provided as input to this Prolog predicate is a string atom that
+ * matches the default submit type of the change's project.
+ *
+ * <pre>
+ * 'project_default_submit_type'(-SubmitType)
+ * </pre>
+ */
public class PRED_project_default_submit_type_1 extends Predicate.P1 {
private static final SymbolTerm[] term;
diff --git a/java/gerrit/PRED_pure_revert_1.java b/java/gerrit/PRED_pure_revert_1.java
index 6300a668..19e7b68 100644
--- a/java/gerrit/PRED_pure_revert_1.java
+++ b/java/gerrit/PRED_pure_revert_1.java
@@ -22,7 +22,17 @@
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.Term;
-/** Checks if change is a pure revert of the change it references in 'revertOf'. */
+/**
+ * Prolog Predicate that checks if the change is a pure revert of the change it references in
+ * 'revertOf'.
+ *
+ * <p>The input is an integer atom where '1' represents a pure revert and '0' represents a non-pure
+ * revert.
+ *
+ * <pre>
+ * 'pure_revert'(-PureRevert)
+ * </pre>
+ */
public class PRED_pure_revert_1 extends Predicate.P1 {
public PRED_pure_revert_1(Term a1, Operation n) {
arg1 = a1;
diff --git a/java/gerrit/PRED_unresolved_comments_count_1.java b/java/gerrit/PRED_unresolved_comments_count_1.java
index d4abcc54..9a1fcca 100644
--- a/java/gerrit/PRED_unresolved_comments_count_1.java
+++ b/java/gerrit/PRED_unresolved_comments_count_1.java
@@ -22,6 +22,16 @@
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.Term;
+/**
+ * Prolog predicate for the number of unresolved comments of a change.
+ *
+ * <p>Checks that the term that is provided as input to this Prolog predicate is an integer atom
+ * that matches the number of unresolved comments of the change.
+ *
+ * <pre>
+ * 'unresolved_comments_count'(-NumberOfUnresolvedComments)
+ * </pre>
+ */
public class PRED_unresolved_comments_count_1 extends Predicate.P1 {
public PRED_unresolved_comments_count_1(Term a1, Operation n) {
arg1 = a1;
diff --git a/java/gerrit/PRED_uploader_1.java b/java/gerrit/PRED_uploader_1.java
index 681d86c..89e367e 100644
--- a/java/gerrit/PRED_uploader_1.java
+++ b/java/gerrit/PRED_uploader_1.java
@@ -27,6 +27,16 @@
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
+/**
+ * Prolog predicate for the uploader of the current patch set of a change.
+ *
+ * <p>Checks that the term that is provided as input to this Prolog predicate is a user ID term that
+ * matches the account ID of the uploader of the current patch set.
+ *
+ * <pre>
+ * 'uploader'(user(-ID))
+ * </pre>
+ */
public class PRED_uploader_1 extends Predicate.P1 {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
index d7e765b..a442ddd 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
@@ -21,7 +21,6 @@
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.extensions.client.Theme;
import org.junit.Test;
@NoHttpd
@@ -43,7 +42,6 @@
i.fontSize *= -1;
i.lineLength *= -1;
i.cursorBlinkRate = 500;
- i.theme = Theme.MIDNIGHT;
i.ignoreWhitespace = Whitespace.IGNORE_ALL;
i.expandAllComments ^= true;
i.intralineDifference ^= true;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
index 00d1733..f142c08 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
@@ -19,8 +19,6 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
-import com.google.gerrit.extensions.client.KeyMapType;
-import com.google.gerrit.extensions.client.Theme;
import org.junit.Test;
@NoHttpd
@@ -43,8 +41,6 @@
assertThat(out.indentWithTabs).isNull();
assertThat(out.autoCloseBrackets).isNull();
assertThat(out.showBase).isNull();
- assertThat(out.theme).isEqualTo(Theme.DEFAULT);
- assertThat(out.keyMapType).isEqualTo(KeyMapType.DEFAULT);
// change some default values
out.lineLength = 80;
@@ -61,8 +57,6 @@
out.indentWithTabs = true;
out.autoCloseBrackets = true;
out.showBase = true;
- out.theme = Theme.TWILIGHT;
- out.keyMapType = KeyMapType.EMACS;
EditPreferencesInfo info = gApi.accounts().id(admin.id().toString()).setEditPreferences(out);
@@ -94,7 +88,5 @@
assertThat(out.indentWithTabs).isEqualTo(in.indentWithTabs);
assertThat(out.autoCloseBrackets).isEqualTo(in.autoCloseBrackets);
assertThat(out.showBase).isEqualTo(in.showBase);
- assertThat(out.theme).isEqualTo(in.theme);
- assertThat(out.keyMapType).isEqualTo(in.keyMapType);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index 3b106a4..6717fb7 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -27,7 +27,6 @@
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DateFormat;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DefaultBase;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DownloadCommand;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.TimeFormat;
@@ -69,7 +68,6 @@
// change all default values
i.changesPerPage *= -1;
- i.downloadCommand = DownloadCommand.REPO_DOWNLOAD;
i.dateFormat = DateFormat.US;
i.timeFormat = TimeFormat.HHMM_24;
i.emailStrategy = EmailStrategy.DISABLED;
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
index 7703b1c..fdf3758 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
@@ -693,42 +693,60 @@
@Test
public void revertSubmissionWithSetMessage() throws Exception {
- String result = createChange("change", "a.txt", "message").getChangeId();
- gApi.changes().id(result).current().review(ReviewInput.approve());
- gApi.changes().id(result).current().submit();
+ String firstResult = createChange("first change", "a.txt", "message").getChangeId();
+ String secondResult = createChange("second change", "b.txt", "message").getChangeId();
+ approve(firstResult);
+ approve(secondResult);
+ gApi.changes().id(secondResult).current().submit();
RevertInput revertInput = new RevertInput();
String commitMessage = "Message from input";
revertInput.message = commitMessage;
- ChangeInfo revertChange =
- gApi.changes().id(result).revertSubmission(revertInput).revertChanges.get(0);
- assertThat(revertChange.subject).isEqualTo("Revert \"change\"");
- assertThat(gApi.changes().id(revertChange.id).current().commit(false).message)
+ List<ChangeInfo> revertChanges =
+ gApi.changes().id(firstResult).revertSubmission(revertInput).revertChanges;
+ assertThat(revertChanges.get(0).subject).isEqualTo("Revert \"first change\"");
+ assertThat(gApi.changes().id(revertChanges.get(0).id).current().commit(false).message)
.isEqualTo(
String.format(
- "Revert \"change\"\n\n%s\n\nChange-Id: %s\n",
- commitMessage, revertChange.changeId));
+ "Revert \"first change\"\n\n%s\n\nChange-Id: %s\n",
+ commitMessage, revertChanges.get(0).changeId));
+ assertThat(revertChanges.get(1).subject).isEqualTo("Revert \"second change\"");
+ assertThat(gApi.changes().id(revertChanges.get(1).id).current().commit(false).message)
+ .isEqualTo(
+ String.format(
+ "Revert \"second change\"\n\n%s\n\nChange-Id: %s\n",
+ commitMessage, revertChanges.get(1).changeId));
}
@Test
public void revertSubmissionWithoutMessage() throws Exception {
- String result = createChange("change", "a.txt", "message").getChangeId();
- gApi.changes().id(result).current().review(ReviewInput.approve());
- gApi.changes().id(result).current().submit();
+ String firstResult = createChange("first change", "a.txt", "message").getChangeId();
+ String secondResult = createChange("second change", "b.txt", "message").getChangeId();
+ approve(firstResult);
+ approve(secondResult);
+ gApi.changes().id(secondResult).current().submit();
RevertInput revertInput = new RevertInput();
- ChangeInfo revertChange =
- gApi.changes().id(result).revertSubmission(revertInput).revertChanges.get(0);
- assertThat(revertChange.subject).isEqualTo("Revert \"change\"");
- assertThat(gApi.changes().id(revertChange.id).current().commit(false).message)
+ List<ChangeInfo> revertChanges =
+ gApi.changes().id(firstResult).revertSubmission(revertInput).revertChanges;
+ assertThat(revertChanges.get(0).subject).isEqualTo("Revert \"first change\"");
+ assertThat(gApi.changes().id(revertChanges.get(0).id).current().commit(false).message)
.isEqualTo(
String.format(
- "Revert \"change\"\n\nThis reverts commit %s.\n\nChange-Id: %s\n",
- gApi.changes().id(result).get().currentRevision, revertChange.changeId));
+ "Revert \"first change\"\n\nThis reverts commit %s.\n\nChange-Id: %s\n",
+ gApi.changes().id(firstResult).get().currentRevision,
+ revertChanges.get(0).changeId));
+ assertThat(revertChanges.get(1).subject).isEqualTo("Revert \"second change\"");
+ assertThat(gApi.changes().id(revertChanges.get(1).id).current().commit(false).message)
+ .isEqualTo(
+ String.format(
+ "Revert \"second change\"\n\nThis reverts commit %s.\n\nChange-Id: %s\n",
+ gApi.changes().id(secondResult).get().currentRevision,
+ revertChanges.get(1).changeId));
}
@Test
public void revertSubmissionRevertsChangeWithLongSubject() throws Exception {
String changeTitle =
- "This change has a very long title and therefore it will be cut to 50 characters when the"
+ "This change has a very long title and therefore it will be cut to 56 characters when the"
+ " revert change will revert this change";
String result = createChange(changeTitle, "a.txt", "message").getChangeId();
gApi.changes().id(result).current().review(ReviewInput.approve());
@@ -737,12 +755,12 @@
ChangeInfo revertChange =
gApi.changes().id(result).revertSubmission(revertInput).revertChanges.get(0);
assertThat(revertChange.subject)
- .isEqualTo(String.format("Revert \"%s...\"", changeTitle.substring(0, 59)));
+ .isEqualTo(String.format("Revert \"%s...\"", changeTitle.substring(0, 56)));
assertThat(gApi.changes().id(revertChange.id).current().commit(false).message)
.isEqualTo(
String.format(
"Revert \"%s...\"\n\nThis reverts commit %s.\n\nChange-Id: %s\n",
- changeTitle.substring(0, 59),
+ changeTitle.substring(0, 56),
gApi.changes().id(result).get().currentRevision,
revertChange.changeId));
}
@@ -766,8 +784,16 @@
}
// submit all changes
gApi.changes().id(resultCommits.get(1).getChangeId()).current().submit();
- List<ChangeApi> revertChanges =
- getChangeApis(gApi.changes().id(resultCommits.get(1).getChangeId()).revertSubmission());
+ RevertSubmissionInfo revertSubmissionInfo =
+ gApi.changes().id(resultCommits.get(1).getChangeId()).revertSubmission();
+ assertThat(
+ revertSubmissionInfo.revertChanges.stream()
+ .map(change -> change.created)
+ .distinct()
+ .count())
+ .isEqualTo(1);
+
+ List<ChangeApi> revertChanges = getChangeApis(revertSubmissionInfo);
assertThat(revertChanges).hasSize(3);
@@ -835,8 +861,16 @@
approve(secondResult.getChangeId());
approve(firstResult.getChangeId());
gApi.changes().id(secondResult.getChangeId()).current().submit();
- List<ChangeApi> revertChanges =
- getChangeApis(gApi.changes().id(firstResult.getChangeId()).revertSubmission());
+ RevertSubmissionInfo revertSubmissionInfo =
+ gApi.changes().id(firstResult.getChangeId()).revertSubmission();
+ assertThat(
+ revertSubmissionInfo.revertChanges.stream()
+ .map(change -> change.created)
+ .distinct()
+ .count())
+ .isEqualTo(1);
+
+ List<ChangeApi> revertChanges = getChangeApis(revertSubmissionInfo);
Collections.reverse(revertChanges);
String sha1SecondChange = secondResult.getCommit().getName();
String sha1FirstRevert = revertChanges.get(0).current().commit(false).commit;
@@ -900,8 +934,16 @@
approve(firstResult.getChangeId());
// submit both changes
gApi.changes().id(secondResult.getChangeId()).current().submit();
- List<ChangeApi> revertChanges =
- getChangeApis(gApi.changes().id(secondResult.getChangeId()).revertSubmission());
+ RevertSubmissionInfo revertSubmissionInfo =
+ gApi.changes().id(secondResult.getChangeId()).revertSubmission();
+ assertThat(
+ revertSubmissionInfo.revertChanges.stream()
+ .map(change -> change.created)
+ .distinct()
+ .count())
+ .isEqualTo(1);
+
+ List<ChangeApi> revertChanges = getChangeApis(revertSubmissionInfo);
// has size 2 because of the same topic, and submitWholeTopic is true.
assertThat(gApi.changes().id(revertChanges.get(0).get()._number).submittedTogether())
.hasSize(2);
@@ -937,8 +979,15 @@
}
// submit all changes
gApi.changes().id(resultCommits.get(1).getChangeId()).current().submit();
- List<ChangeApi> revertChanges =
- getChangeApis(gApi.changes().id(resultCommits.get(1).getChangeId()).revertSubmission());
+ RevertSubmissionInfo revertSubmissionInfo =
+ gApi.changes().id(resultCommits.get(1).getChangeId()).revertSubmission();
+ assertThat(
+ revertSubmissionInfo.revertChanges.stream()
+ .map(change -> change.created)
+ .distinct()
+ .count())
+ .isEqualTo(1);
+ List<ChangeApi> revertChanges = getChangeApis(revertSubmissionInfo);
assertThat(revertChanges.get(0).current().files().get("c.txt").linesDeleted).isEqualTo(1);
assertThat(revertChanges.get(1).current().files().get("a.txt").linesDeleted).isEqualTo(1);
assertThat(revertChanges.get(2).current().files().get("b.txt").linesDeleted).isEqualTo(1);
@@ -984,8 +1033,15 @@
approve(fourthResult.getChangeId());
gApi.changes().id(fourthResult.getChangeId()).current().submit();
- List<ChangeApi> revertChanges =
- getChangeApis(gApi.changes().id(secondResult.getChangeId()).revertSubmission());
+ RevertSubmissionInfo revertSubmissionInfo =
+ gApi.changes().id(secondResult.getChangeId()).revertSubmission();
+ assertThat(
+ revertSubmissionInfo.revertChanges.stream()
+ .map(change -> change.created)
+ .distinct()
+ .count())
+ .isEqualTo(1);
+ List<ChangeApi> revertChanges = getChangeApis(revertSubmissionInfo);
Collections.reverse(revertChanges);
assertThat(revertChanges.get(0).current().files().get("c.txt").linesDeleted).isEqualTo(1);
assertThat(revertChanges.get(1).current().files().get("b.txt").linesDeleted).isEqualTo(1);
@@ -1034,8 +1090,15 @@
approve(fourthResult.getChangeId());
gApi.changes().id(fourthResult.getChangeId()).current().submit();
- List<ChangeApi> revertChanges =
- getChangeApis(gApi.changes().id(secondResult.getChangeId()).revertSubmission());
+ RevertSubmissionInfo revertSubmissionInfo =
+ gApi.changes().id(secondResult.getChangeId()).revertSubmission();
+ assertThat(
+ revertSubmissionInfo.revertChanges.stream()
+ .map(change -> change.created)
+ .distinct()
+ .count())
+ .isEqualTo(1);
+ List<ChangeApi> revertChanges = getChangeApis(revertSubmissionInfo);
Collections.reverse(revertChanges);
assertThat(revertChanges.get(0).current().files().get("c.txt").linesDeleted).isEqualTo(1);
assertThat(revertChanges.get(1).current().files().get("b.txt").linesDeleted).isEqualTo(1);
@@ -1055,6 +1118,56 @@
assertThat(gApi.changes().id(revertChanges.get(1).id()).current().related().changes).hasSize(3);
}
+ @Test
+ public void revertSubmissionSubjects() throws Exception {
+ String firstResult = createChange("first change", "a.txt", "message").getChangeId();
+ String secondResult = createChange("second change", "b.txt", "other").getChangeId();
+ approve(firstResult);
+ approve(secondResult);
+ gApi.changes().id(secondResult).current().submit();
+
+ List<ChangeApi> firstRevertChanges =
+ getChangeApis(gApi.changes().id(firstResult).revertSubmission());
+ assertThat(firstRevertChanges.get(0).get().subject).isEqualTo("Revert \"first change\"");
+ assertThat(firstRevertChanges.get(1).get().subject).isEqualTo("Revert \"second change\"");
+ approve(firstRevertChanges.get(0).id());
+ approve(firstRevertChanges.get(1).id());
+ gApi.changes().id(firstRevertChanges.get(0).id()).current().submit();
+
+ List<ChangeApi> secondRevertChanges =
+ getChangeApis(gApi.changes().id(firstRevertChanges.get(0).id()).revertSubmission());
+ assertThat(secondRevertChanges.get(0).get().subject).isEqualTo("Revert^2 \"second change\"");
+ assertThat(secondRevertChanges.get(1).get().subject).isEqualTo("Revert^2 \"first change\"");
+ approve(secondRevertChanges.get(0).id());
+ approve(secondRevertChanges.get(1).id());
+ gApi.changes().id(secondRevertChanges.get(0).id()).current().submit();
+
+ List<ChangeApi> thirdRevertChanges =
+ getChangeApis(gApi.changes().id(secondRevertChanges.get(0).id()).revertSubmission());
+ assertThat(thirdRevertChanges.get(0).get().subject).isEqualTo("Revert^3 \"first change\"");
+ assertThat(thirdRevertChanges.get(1).get().subject).isEqualTo("Revert^3 \"second change\"");
+ }
+
+ @Test
+ public void revertSubmissionWithUserChangedSubjects() throws Exception {
+ String firstResult = createChange("Revert^aa", "a.txt", "message").getChangeId();
+ String secondResult = createChange("Revert", "b.txt", "other").getChangeId();
+ String thirdResult = createChange("Revert^934 \"change x\"", "c.txt", "another").getChangeId();
+ String fourthResult = createChange("Revert^934", "d.txt", "last").getChangeId();
+ approve(firstResult);
+ approve(secondResult);
+ approve(thirdResult);
+ approve(fourthResult);
+ gApi.changes().id(fourthResult).current().submit();
+
+ List<ChangeApi> firstRevertChanges =
+ getChangeApis(gApi.changes().id(firstResult).revertSubmission());
+ assertThat(firstRevertChanges.get(0).get().subject).isEqualTo("Revert \"Revert^aa\"");
+ assertThat(firstRevertChanges.get(1).get().subject).isEqualTo("Revert \"Revert\"");
+ assertThat(firstRevertChanges.get(2).get().subject).isEqualTo("Revert^935 \"change x\"");
+ assertThat(firstRevertChanges.get(3).get().subject).isEqualTo("Revert \"Revert^934\"");
+ }
+
@Override
protected PushOneCommit.Result createChange() throws Exception {
return createChange("refs/for/master");
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index d4a4c45..717d3cc 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -21,6 +21,7 @@
import static com.google.gerrit.extensions.common.testing.DiffInfoSubject.assertThat;
import static com.google.gerrit.extensions.common.testing.FileInfoSubject.assertThat;
import static com.google.gerrit.git.ObjectIds.abbreviateName;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toMap;
@@ -41,6 +42,7 @@
import com.google.gerrit.extensions.common.ChangeType;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.FileInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.testing.ConfigSuite;
@@ -2718,6 +2720,23 @@
assertThat(diffInfo).content().element(0).numberOfSkippedLines().isGreaterThan(0);
}
+ @Test
+ public void editNotAllowedAsBase() throws Exception {
+ gApi.changes().id(changeId).edit().create();
+
+ BadRequestException e =
+ assertThrows(
+ BadRequestException.class,
+ () -> getDiffRequest(changeId, CURRENT, FILE_NAME).withBase("edit").get());
+ assertThat(e).hasMessageThat().isEqualTo("edit not allowed as base");
+
+ e =
+ assertThrows(
+ BadRequestException.class,
+ () -> getDiffRequest(changeId, CURRENT, FILE_NAME).withBase("0").get());
+ assertThat(e).hasMessageThat().isEqualTo("edit not allowed as base");
+ }
+
private static CommentInput createCommentInput(
int startLine, int startCharacter, int endLine, int endCharacter, String message) {
CommentInput comment = new CommentInput();
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index bf93d58..b0f183e 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -520,6 +520,14 @@
}
@Test
+ public void changeEditNoContentProvidedRest() throws Exception {
+ createEmptyEditFor(changeId);
+ adminRestSession
+ .put(urlEditFile(changeId, FILE_NAME), new FileContentInput())
+ .assertBadRequest();
+ }
+
+ @Test
public void emptyPutRequest() throws Exception {
createEmptyEditFor(changeId);
adminRestSession.put(urlEditFile(changeId, FILE_NAME)).assertNoContent();
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
index c692a3b..c67a842 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -51,7 +51,7 @@
case V6_7:
return "blacktop/elasticsearch:6.7.2";
case V6_8:
- return "blacktop/elasticsearch:6.8.5";
+ return "blacktop/elasticsearch:6.8.6";
case V7_0:
return "blacktop/elasticsearch:7.0.1";
case V7_1:
@@ -63,7 +63,7 @@
case V7_4:
return "blacktop/elasticsearch:7.4.2";
case V7_5:
- return "blacktop/elasticsearch:7.5.0";
+ return "blacktop/elasticsearch:7.5.1";
}
throw new IllegalStateException("No tests for version: " + version.name());
}
diff --git a/javatests/com/google/gerrit/server/config/ConfigUtilTest.java b/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
index 865bda6..035878c 100644
--- a/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
+++ b/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
@@ -22,7 +22,6 @@
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
-import com.google.gerrit.extensions.client.Theme;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -48,8 +47,6 @@
public String s;
public String sd;
public String nd;
- public Theme t;
- public Theme td;
public List<String> list;
public Map<String, String> map;
@@ -67,8 +64,6 @@
i.s = "foo";
i.sd = "bar";
// i.nd = null; // Don't need to explicitly set it; it's null by default
- i.t = Theme.DEFAULT;
- i.td = Theme.DEFAULT;
return i;
}
}
@@ -86,7 +81,6 @@
in.bb = true;
in.bd = false;
in.s = "baz";
- in.t = Theme.MIDNIGHT;
Config cfg = new Config();
ConfigUtil.storeSection(cfg, SECT, SUB, in, d);
@@ -117,8 +111,6 @@
assertThat(out.s).isEqualTo(in.s);
assertThat(out.sd).isEqualTo(d.sd);
assertThat(out.nd).isNull();
- assertThat(out.t).isEqualTo(in.t);
- assertThat(out.td).isEqualTo(d.td);
}
@Test
diff --git a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
index c7ca887..083493d 100644
--- a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
+++ b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
@@ -35,10 +36,13 @@
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.server.patch.DiffSummary;
+import com.google.gerrit.server.patch.DiffSummaryKey;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.InMemoryTestEnvironment;
import com.google.inject.Inject;
import com.google.inject.Provider;
+import com.google.inject.name.Named;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
@@ -57,8 +61,9 @@
new InMemoryTestEnvironment(
() -> {
Config cfg = new Config();
- cfg.setInt("change", null, "maxUpdates", MAX_UPDATES);
+ cfg.setInt("change", null, "maxFiles", 2);
cfg.setInt("change", null, "maxPatchSets", MAX_PATCH_SETS);
+ cfg.setInt("change", null, "maxUpdates", MAX_UPDATES);
return cfg;
});
@@ -70,6 +75,9 @@
@Inject private Provider<CurrentUser> user;
@Inject private Sequences sequences;
+ @Inject
+ private @Named("diff_summary") Cache<DiffSummaryKey, DiffSummary> diffSummaryCache;
+
private Project.NameKey project;
private TestRepository<Repository> repo;
@@ -243,6 +251,59 @@
assertThat(getMetaId(changeId)).isEqualTo(oldMetaId);
}
+ @Test
+ public void limitFileCount_exceed() throws Exception {
+ Change.Id changeId = createChangeWithUpdates(1);
+ ChangeNotes notes = changeNotesFactory.create(project, changeId);
+
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ ObjectId commitId =
+ repo.amend(notes.getCurrentPatchSet().commitId())
+ .add("bar.txt", "bar")
+ .add("baz.txt", "baz")
+ .add("boom.txt", "boom")
+ .message("blah")
+ .create();
+ bu.addOp(
+ changeId,
+ patchSetInserterFactory
+ .create(notes, PatchSet.id(changeId, 2), commitId)
+ .setMessage("blah"));
+ ResourceConflictException thrown = assertThrows(ResourceConflictException.class, bu::execute);
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Exceeding maximum number of files per change (3 > 2)");
+ }
+ }
+
+ @Test
+ public void limitFileCount_cacheKeyMatches() throws Exception {
+ Change.Id changeId = createChangeWithUpdates(1);
+ ChangeNotes notes = changeNotesFactory.create(project, changeId);
+
+ int cacheSizeBefore = diffSummaryCache.asMap().size();
+
+ // We don't want to depend on the test helper used above so we perform an explicit commit here.
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ ObjectId commitId =
+ repo.amend(notes.getCurrentPatchSet().commitId())
+ .add("bar.txt", "bar")
+ .add("baz.txt", "baz")
+ .message("blah")
+ .create();
+ bu.addOp(
+ changeId,
+ patchSetInserterFactory
+ .create(notes, PatchSet.id(changeId, 3), commitId)
+ .setMessage("blah"));
+ bu.execute();
+ }
+
+ // Assert that we only performed the diff computation once. This would e.g. catch
+ // bugs/deviations in the computation of the cache key.
+ assertThat(diffSummaryCache.asMap()).hasSize(cacheSizeBefore + 1);
+ }
+
private Change.Id createChangeWithUpdates(int totalUpdates) throws Exception {
checkArgument(totalUpdates > 0);
checkArgument(totalUpdates <= MAX_UPDATES);
diff --git a/plugins/plugin-manager b/plugins/plugin-manager
index 828d666..2933add 160000
--- a/plugins/plugin-manager
+++ b/plugins/plugin-manager
@@ -1 +1 @@
-Subproject commit 828d666bbb4aae1a2c348a12d7855ec5db3be46f
+Subproject commit 2933add62ecf2cbfc28cfe2cff81ff0e0eecc913
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index b596de8..e21dbe2 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -61,17 +61,6 @@
),
)
-filegroup(
- name = "bower_components",
- srcs = glob(
- [
- "bower_components/**/*.html",
- "bower_components/**/*.js",
- "bower_components/**/*.js.map",
- ],
- ),
-)
-
genrule2(
name = "pg_code_zip",
srcs = [":pg_code"],
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
index f11e5ed..dfcacb4 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
@@ -76,9 +76,9 @@
}
/**
- * @param {?boolean} value - fallback to false if undefined
+ * @param {string} value - fallback to 'false' if undefined
*/
- _computeChecked(value = false) {
+ _computeChecked(value = 'false') {
return JSON.parse(value);
}
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
index 05a2bb2..9e7857c4 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
@@ -45,14 +45,7 @@
font-family: var(--monospace-font-family);
font-size: var(--font-size-mono);
line-height: var(--line-height-mono);
- padding: 0;
width: 73ch; /* Add a char to account for the border. */
-
- --iron-autogrow-textarea {
- border: 1px solid var(--border-color);
- box-sizing: border-box;
- font-family: var(--monospace-font-family);
- }
}
</style>
<gr-dialog
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
index 8ddcd83..cab9fd6 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
@@ -47,18 +47,11 @@
display: block;
width: 100%;
}
- .main .message {
- width: 100%;
- }
iron-autogrow-textarea {
font-family: var(--monospace-font-family);
font-size: var(--font-size-mono);
line-height: var(--line-height-mono);
- padding: 0;
- --iron-autogrow-textarea: {
- font-family: var(--monospace-font-family);
- width: 72ch;
- };
+ width: 73ch; /* Add a char to account for the border. */
}
</style>
<gr-dialog
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
index 24c1132..f65ec03 100644
--- 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
@@ -37,9 +37,6 @@
label {
cursor: pointer;
}
- iron-autogrow-textarea {
- padding: 0;
- }
.main {
display: flex;
flex-direction: column;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
index 2e1e6ae..fc3a8c1 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
@@ -41,14 +41,7 @@
font-family: var(--monospace-font-family);
font-size: var(--font-size-mono);
line-height: var(--line-height-mono);
- padding: 0;
width: 73ch; /* Add a char to account for the border. */
-
- --iron-autogrow-textarea {
- border: 1px solid var(--border-color);
- box-sizing: border-box;
- font-family: var(--monospace-font-family);
- }
}
</style>
<gr-dialog
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog.html
index 0107c33..f2cfef8 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog.html
@@ -40,14 +40,9 @@
}
iron-autogrow-textarea {
font-family: var(--monospace-font-family);
- padding: 0;
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
width: 73ch; /* Add a char to account for the border. */
-
- --iron-autogrow-textarea {
- border: 1px solid var(--border-color);
- box-sizing: border-box;
- font-family: var(--monospace-font-family);
- }
}
</style>
<gr-dialog
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
index 46e7d32..403d9d5 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
@@ -398,4 +398,4 @@
assert.equal(element._registerText, 'Sign up');
});
});
- </script>
\ No newline at end of file
+ </script>
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 5c98281..889f1f3 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -436,8 +436,13 @@
// Finalize the interval. Either from a registered start mark or
// the navigation start time (if baseTime is 0).
- const startMark = baseTime === 0 ? undefined : `${name}-start`;
- window.performance.measure(name, startMark);
+ if (baseTime !== 0) {
+ window.performance.measure(name, `${name}-start`);
+ } else {
+ // Microsft Edge does not handle the 2nd param correctly
+ // (if undefined).
+ window.performance.measure(name);
+ }
},
/**
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
index a96cf44..afd0e59 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
@@ -43,10 +43,6 @@
display: block;
font-family: var(--font-family);
padding: var(--spacing-m);
- --iron-autogrow-textarea: {
- box-sizing: border-box;
- padding: 2px;
- };
}
:host([disabled]) {
pointer-events: none;
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
index 62ab307..e986ad0 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
@@ -45,14 +45,7 @@
font-family: var(--monospace-font-family);
font-size: var(--font-size-mono);
line-height: var(--line-height-mono);
- padding: 0;
width: 73ch; /* Add a char to account for the border. */
-
- --iron-autogrow-textarea {
- border: 1px solid var(--border-color);
- box-sizing: border-box;
- font-family: var(--monospace-font-family);
- }
}
</style>
<gr-dialog
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
index 45ddfc8..950be2a 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
@@ -41,9 +41,11 @@
background-color: var(--view-background-color);
width: 100%;
+ /* You have to also repeat everything from shared-styles here, because
+ you can only *replace* --iron-autogrow-textarea vars as a whole. */
--iron-autogrow-textarea: {
- padding: var(--spacing-m);
box-sizing: border-box;
+ padding: var(--spacing-m);
overflow-y: hidden;
white-space: pre;
};
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
index 6803eb9..42a4f3b 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
@@ -59,11 +59,7 @@
visibility: visible;
white-space: normal;
}
- /*This is needed to not add a scroll bar on the side of gr-textarea
- since there is 2px of padding in iron-autogrow-textarea for the
- native textarea*/
iron-autogrow-textarea {
- padding: 2px;
position: relative;
/** This is needed for firefox */
diff --git a/polygerrit-ui/app/rules.bzl b/polygerrit-ui/app/rules.bzl
index e1304e6..93c662a 100644
--- a/polygerrit-ui/app/rules.bzl
+++ b/polygerrit-ui/app/rules.bzl
@@ -19,10 +19,9 @@
],
language = "ECMASCRIPT_2017",
deps = [name + "_closure_lib"],
+ dependency_mode = "PRUNE_LEGACY",
)
- # TODO(davido): Remove JSC_REFERENCE_BEFORE_DECLARE when this is fixed upstream:
- # https://github.com/Polymer/polymer-resin/issues/7
closure_js_library(
name = name + "_closure_lib",
srcs = [appName + ".js"],
@@ -31,9 +30,7 @@
# and remove this supression
suppress = [
"JSC_JSDOC_MISSING_TYPE_WARNING",
- "JSC_REFERENCE_BEFORE_DECLARE",
"JSC_UNNECESSARY_ESCAPE",
- "JSC_UNUSED_LOCAL_ASSIGNMENT",
],
deps = [
"//lib/polymer_externs:polymer_closure",
diff --git a/polygerrit-ui/app/styles/gr-form-styles.html b/polygerrit-ui/app/styles/gr-form-styles.html
index 7c9ae0d..5133051 100644
--- a/polygerrit-ui/app/styles/gr-form-styles.html
+++ b/polygerrit-ui/app/styles/gr-form-styles.html
@@ -93,15 +93,8 @@
width: 100%;
}
.gr-form-styles iron-autogrow-textarea {
- border: none;
height: auto;
min-height: 4em;
- --iron-autogrow-textarea: {
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
- box-sizing: border-box;
- padding: var(--spacing-s);
- }
}
.gr-form-styles gr-autocomplete {
width: 14em;
diff --git a/polygerrit-ui/app/styles/shared-styles.html b/polygerrit-ui/app/styles/shared-styles.html
index 51b92e1..ce6bead 100644
--- a/polygerrit-ui/app/styles/shared-styles.html
+++ b/polygerrit-ui/app/styles/shared-styles.html
@@ -53,13 +53,15 @@
color: var(--primary-text-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
+ padding: 0;
box-sizing: border-box;
/* iron-autogrow-textarea has a "-webkit-appearance: textarea" :host
css rule, which prevents overriding the border color. Clear that. */
-webkit-appearance: none;
--iron-autogrow-textarea: {
- padding: 4px;
+ box-sizing: border-box;
+ padding: var(--spacing-s);
};
}
a {
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html
index cfc1f62..80243dc 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.html
@@ -98,8 +98,8 @@
--light-rebased-add-highlight-color: #487165;
--light-remove-add-highlight-color: #2f3f2f;
--light-remove-highlight-color: #320404;
- --coverage-covered: #e0f2f1;
- --coverage-not-covered: #ffd1a4;
+ --coverage-covered: #112826;
+ --coverage-not-covered: #6b3600;
/* syntax colors */
--syntax-attr-color: #80cbbf;
diff --git a/resources/com/google/gerrit/server/change/ChangeMessages.properties b/resources/com/google/gerrit/server/change/ChangeMessages.properties
index ec20677..3763d8e 100644
--- a/resources/com/google/gerrit/server/change/ChangeMessages.properties
+++ b/resources/com/google/gerrit/server/change/ChangeMessages.properties
@@ -1,4 +1,7 @@
revertChangeDefaultMessage = Revert \"{0}\"\n\nThis reverts commit {1}.
+revertSubmissionDefaultMessage = This reverts commit {0}.
+revertSubmissionUserMessage = Revert \"{0}\"\n\n{1}
+revertSubmissionOfRevertSubmissionUserMessage = Revert^{0} \"{1}\"\n\n{2}
reviewerCantSeeChange = {0} does not have permission to see this change
reviewerInvalid = {0} is not a valid user identifier
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index 8805614e..b14a761 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -494,6 +494,7 @@
deps = [
name + "_closure_lib",
],
+ dependency_mode = "PRUNE_LEGACY",
)
if html_plugin:
diff --git a/tools/bzl/maven_jar.bzl b/tools/bzl/maven_jar.bzl
index 0b4a52a..2f5447b 100644
--- a/tools/bzl/maven_jar.bzl
+++ b/tools/bzl/maven_jar.bzl
@@ -94,6 +94,7 @@
srcjar_attr = 'srcjar = "%s",' % srcjar
contents = """
{header}
+load("@rules_java//java:defs.bzl", "java_import")
package(default_visibility = ['//visibility:public'])
java_import(
name = 'jar',
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index e16c0a0..fa8008c 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -102,8 +102,8 @@
# and httpasyncclient as necessary.
maven_jar(
name = "elasticsearch-rest-client",
- artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.5.0",
- sha1 = "62535b6fc3a4e943e88e7640eac22e29f03a696d",
+ artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.5.1",
+ sha1 = "094c155906dc94146fc5adc344ea2c676d487cf2",
)
maven_jar(