Merge "Stop query from executing if predicate is empty"
diff --git a/.gitignore b/.gitignore
index 75cdfb5..93c1339 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,6 @@
/.apt_generated
/.apt_generated_tests
/.bazel_path
-/.buckd
/.classpath
/.factorypath
/.idea
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 00350d1..37bf3e1 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1565,12 +1565,16 @@
Execute `java -jar gerrit.war daemon --help` to see all possible
options.
-[[container.slave]]container.slave::
+[[container.replica]]container.replica::
+
Used on Gerrit replica installations. If set to true the Gerrit JVM is
called with the '--replica' switch, enabling replica mode. If no value is
set (or any other value), Gerrit defaults to master mode.
+[[container.slave]]container.slave::
++
+Backward compatibility for 'container.slave' config setting.
+
[[container.startupTimeout]]container.startupTimeout::
+
The maximum time (in seconds) to wait for a gerrit.sh start command
@@ -3640,7 +3644,10 @@
[[plugins.mandatory]]plugins.mandatory::
+
List of mandatory plugins. If a plugin from this list does not load,
-Gerrit start will fail.
+Gerrit will fail to start.
++
+Disabling and restarting of a mandatory plugin is rejected, but reloading
+of a mandatory plugin is still possible.
[[plugins.jsLoadTimeout]]plugins.jsLoadTimeout::
+
diff --git a/Documentation/dev-build-plugins.txt b/Documentation/dev-build-plugins.txt
index 8139743..219861f 100644
--- a/Documentation/dev-build-plugins.txt
+++ b/Documentation/dev-build-plugins.txt
@@ -69,7 +69,7 @@
The output can be normally found in the following directory:
----
-bazel-genfiles/plugins/<plugin-name>/<plugin-name>.jar
+bazel-bin/plugins/<plugin-name>/<plugin-name>.jar
----
Some plugins describe their build process in `src/main/resources/Documentation/build.md`
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index 48bafdf..9e1744c 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -98,7 +98,7 @@
Tag the plugins:
----
- git submodule foreach git tag -s -m "v$version" "v$version"
+ git submodule foreach '[ "$path" == "modules/jgit" ] || git tag -s -m "v$version" "v$version"'
----
[[build-gerrit]]
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 6b8281a..10fb741 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1501,6 +1501,90 @@
change is new
----
+[[revert-submission]]
+=== Revert Submission
+--
+'POST /changes/link:#change-id[\{change-id\}]/revert_submission'
+--
+
+Creates open revert changes for all of the changes of a certain submission.
+
+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
+`revert-{submission_id}-{random_string_of_size_10}`.
+
+The changes will not be rebased on onto the destination branch so the users may still
+have to manually rebase them to resolve conflicts and make them submittable.
+
+.Request
+----
+ POST /changes/myProject~master~I1ffe09a505e25f15ce1521bcfb222e51e62c2a14/revert_submission HTTP/1.0
+----
+
+As response link:#revert-submission-info[RevertSubmissionInfo] entity
+is returned. That entity describes the revert changes.
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ "revert_changes":
+ [
+ {
+ "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940",
+ "project": "myProject",
+ "branch": "master",
+ "topic": "revert--1571043962462-3640749-ABCEEZGHIJ",
+ "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9940",
+ "subject": "Revert \"Implementing Feature X\"",
+ "status": "NEW",
+ "created": "2013-02-01 09:59:32.126000000",
+ "updated": "2013-02-21 11:16:36.775000000",
+ "mergeable": true,
+ "insertions": 6,
+ "deletions": 4,
+ "_number": 3965,
+ "owner": {
+ "name": "John Doe"
+ }
+ },
+ {
+ "id": "anyProject~master~1eee2c9d8f352483781e772f35dc586a69ff5646",
+ "project": "anyProject",
+ "branch": "master",
+ "topic": "revert--1571043962462-3640749-ABCEEZGHIJ",
+ "change_id": "I1eee2c9d8f352483781e772f35dc586a69ff5646",
+ "subject": "Revert \"Implementing Feature Y\"",
+ "status": "NEW",
+ "created": "2013-02-04 09:59:33.126000000",
+ "updated": "2013-02-21 11:16:37.775000000",
+ "mergeable": true,
+ "insertions": 62,
+ "deletions": 11,
+ "_number": 3966,
+ "owner": {
+ "name": "Jane Doe"
+ }
+ }
+ ]
+----
+
+If any of the changes cannot be reverted because the change state doesn't
+allow reverting the change, the response is "`409 Conflict`" and
+the error message is contained in the response body.
+
+.Response
+----
+ HTTP/1.1 409 Conflict
+ Content-Disposition: attachment
+ Content-Type: text/plain; charset=UTF-8
+
+ change is new
+----
+
[[submit-change]]
=== Submit Change
--
@@ -6931,8 +7015,22 @@
Additional information about whom to notify about the revert as a map
of recipient type to link:#notify-info[NotifyInfo] entity.
|`topic` |optional|
-Name of the topic for the revert change. If not set, the default is the topic
-of the change being reverted.
+Name of the topic for the revert change. If not set, the default for Revert
+endpoint is the topic of the change being reverted, and the default for the
+RevertSubmission endpoint is `revert-{submission_id}-{timestamp.now}`.
+|===========================
+
+[[revert-submission-info]]
+=== RevertSubmissionInfo
+The `RevertSubmissionInfo` describes the revert changes.
+
+[options="header",cols="1,6"]
+|===========================
+|Field Name | Description
+|`revert_changes` |
+A list of #change-info[ChangeInfo] that describes the revert
+changes. Each entity in that list is a revert change that was created in that
+revert submission.
|=============================
[[review-info]]
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index c1349aa..b70dfea 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1252,6 +1252,57 @@
}
----
+[[create-change]]
+=== Create Change for review.
+
+This endpoint is functionally equivalent to
+link:rest-api-changes.html#create-change[create change in the change
+API], but it has the project name in the URL, which is easier to route
+in sharded deployments.
+
+.Request
+----
+ POST /projects/myProject/create.change HTTP/1.0
+ Content-Type: application/json; charset=UTF-8
+
+ {
+ "subject" : "Let's support 100% Gerrit workflow direct in browser",
+ "branch" : "master",
+ "topic" : "create-change-in-browser",
+ "status" : "NEW"
+ }
+----
+
+As response a link:#change-info[ChangeInfo] entity is returned that describes
+the resulting change.
+
+.Response
+----
+ HTTP/1.1 201 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9941",
+ "project": "myProject",
+ "branch": "master",
+ "topic": "create-change-in-browser",
+ "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9941",
+ "subject": "Let's support 100% Gerrit workflow direct in browser",
+ "status": "NEW",
+ "created": "2014-05-05 07:15:44.639000000",
+ "updated": "2014-05-05 07:15:44.639000000",
+ "mergeable": true,
+ "insertions": 0,
+ "deletions": 0,
+ "_number": 4711,
+ "owner": {
+ "name": "John Doe"
+ }
+ }
+----
+
[[create-access-change]]
=== Create Access Rights Change for review.
--
diff --git a/WORKSPACE b/WORKSPACE
index 00c905c..e623e42 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -760,7 +760,7 @@
sha1 = "f7be08ec23c21485b9b5a1cf1654c2ec8c58168d",
)
-GITILES_VERS = "0.3-4"
+GITILES_VERS = "0.3-5"
GITILES_REPO = GERRIT
@@ -769,14 +769,14 @@
artifact = "com.google.gitiles:blame-cache:" + GITILES_VERS,
attach_source = False,
repository = GITILES_REPO,
- sha1 = "d1d62c9905b0cc9e066d337b33480599f430eb87",
+ sha1 = "22d5e48827bd48b9e7b049bb9726ef017fda9eca",
)
maven_jar(
name = "gitiles-servlet",
artifact = "com.google.gitiles:gitiles-servlet:" + GITILES_VERS,
repository = GITILES_REPO,
- sha1 = "7360cb90576a813cd1288cf853c59824fe92467e",
+ sha1 = "061de6d5ef22be870300cc01a6fb205bb7782eae",
)
# prettify must match the version used in Gitiles
diff --git a/contrib/hooks/post-receive-move-tmp-refs b/contrib/hooks/post-receive-move-tmp-refs
index c4a53db..c99a3e5 100755
--- a/contrib/hooks/post-receive-move-tmp-refs
+++ b/contrib/hooks/post-receive-move-tmp-refs
@@ -23,6 +23,8 @@
# remote.NAME.push = +refs/changes/*:refs/tmp/changes/*
# remote.NAME.push = +refs/heads/*:refs/heads/*
# remote.NAME.push = +refs/tags/*:refs/tags/*
+# And if it's a Gerrit mirror:
+# remote.NAME.push = +refs/meta/*:refs/meta/*
#
# In the replicated repository in the gerrit slave configure
# receive.hideRefs = refs/changes/
@@ -47,18 +49,15 @@
# automatically have the "tmp-refs" commit hook installed.
# See https://git-scm.com/docs/git-init#_template_directory for details.
-readonly NULL_SHA1=0000000000000000000000000000000000000000
-
# move new changes arriving under refs/tmp/changes/ to refs/changes/
mv_tmp_refs()
{
oldrev=$1
newrev=$2
refname=$3
- case "$refname","$oldrev" in
- refs/tmp/changes/*,$NULL_SHA1)
+ case "$refname" in refs/tmp/changes/*)
short_refname=${refname##refs/tmp/changes/}
- $(git update-ref refs/changes/$short_refname $newrev $NULL_SHA1 2>/dev/null)
+ $(git update-ref refs/changes/$short_refname $newrev 2>/dev/null)
$(git update-ref -d $refname $newrev 2>/dev/null)
echo "moved \"$refname\" to \"refs/changes/$short_refname\""
;;
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index a5066b7..678bc31 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -44,6 +44,7 @@
import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
import com.google.gerrit.server.ssh.NoSshModule;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.gerrit.server.util.SocketUtil;
import com.google.gerrit.server.util.SystemLog;
import com.google.gerrit.testing.FakeEmailSender;
@@ -450,7 +451,7 @@
@Nullable InMemoryRepositoryManager inMemoryRepoManager)
throws Exception {
Config cfg = desc.buildConfig(baseConfig);
- daemon.setReplica(isReplica(baseConfig) || isReplica(cfg));
+ daemon.setReplica(ReplicaUtil.isReplica(baseConfig) || ReplicaUtil.isReplica(cfg));
mergeTestConfig(cfg);
// Set the log4j configuration to an invalid one to prevent system logs
// from getting configured and creating log files.
@@ -463,7 +464,8 @@
cfg.setString(
"accountPatchReviewDb", null, "url", JdbcAccountPatchReviewStore.TEST_IN_MEMORY_URL);
daemon.setEnableHttpd(desc.httpd());
- daemon.setLuceneModule(LuceneIndexModule.singleVersionAllLatest(0, isReplica(baseConfig)));
+ daemon.setLuceneModule(
+ LuceneIndexModule.singleVersionAllLatest(0, ReplicaUtil.isReplica(baseConfig)));
daemon.setDatabaseForTesting(
ImmutableList.of(
new InMemoryTestingDatabaseModule(cfg, site, inMemoryRepoManager),
@@ -479,10 +481,6 @@
return new GerritServer(desc, null, createTestInjector(daemon), daemon, null);
}
- private static boolean isReplica(Config baseConfig) {
- return baseConfig.getBoolean("container", "slave", false);
- }
-
private static GerritServer startOnDisk(
Description desc,
Path site,
@@ -662,7 +660,7 @@
Path site = server.testInjector.getInstance(Key.get(Path.class, SitePath.class));
Config cfg = server.testInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
- cfg.setBoolean("container", null, "slave", true);
+ cfg.setBoolean("container", null, "replica", true);
InMemoryRepositoryManager inMemoryRepoManager = null;
if (hasBinding(server.testInjector, InMemoryRepositoryManager.class)) {
diff --git a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
index bd8a926..b985e40 100644
--- a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
@@ -20,6 +20,7 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.index.group.GroupIndexer;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.inject.Inject;
import com.google.inject.Scopes;
import java.io.IOException;
@@ -50,8 +51,8 @@
@Override
public void start() {
- // Gerrit slaves without a reindex
- if (cfg.getBoolean("container", "slave", false)
+ // Gerrit replicas without a reindex
+ if (ReplicaUtil.isReplica(cfg)
&& !cfg.getBoolean("index", "scheduledIndexer", "runOnStartup", true)) {
return;
}
diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
index 58a9e1c..a06f90f 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
@@ -14,8 +14,6 @@
package com.google.gerrit.elasticsearch;
-import static com.google.gerrit.server.index.account.AccountField.ID;
-
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
import com.google.gerrit.elasticsearch.bulk.BulkRequest;
import com.google.gerrit.elasticsearch.bulk.IndexRequest;
@@ -92,8 +90,16 @@
@Override
public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts)
throws QueryParseException {
- JsonArray sortArray = getSortArray(AccountField.ID.getName());
- return new ElasticQuerySource(p, opts.filterFields(IndexUtils::accountFields), type, sortArray);
+ JsonArray sortArray =
+ getSortArray(
+ schema.useLegacyNumericFields()
+ ? AccountField.ID.getName()
+ : AccountField.ID_STR.getName());
+ return new ElasticQuerySource(
+ p,
+ opts.filterFields(o -> IndexUtils.accountFields(o, schema.useLegacyNumericFields())),
+ type,
+ sortArray);
}
@Override
@@ -118,7 +124,15 @@
source = json.getAsJsonObject().get("fields");
}
- Account.Id id = Account.id(source.getAsJsonObject().get(ID.getName()).getAsInt());
+ Account.Id id =
+ Account.id(
+ source
+ .getAsJsonObject()
+ .get(
+ schema.useLegacyNumericFields()
+ ? AccountField.ID.getName()
+ : AccountField.ID_STR.getName())
+ .getAsInt());
// Use the AccountCache rather than depending on any stored fields in the document (of which
// there shouldn't be any). The most expensive part to compute anyway is the effective group
// IDs, and we don't have a good way to reindex when those change.
diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index 1e35191..6151de2 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -40,6 +40,7 @@
import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
import com.google.gerrit.entities.converter.PatchSetProtoConverter;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
@@ -91,6 +92,7 @@
private final ChangeMapping mapping;
private final ChangeData.Factory changeDataFactory;
private final Schema<ChangeData> schema;
+ private final FieldDef<ChangeData, ?> idField;
@Inject
ElasticChangeIndex(
@@ -102,7 +104,9 @@
super(cfg, sitePaths, schema, clientBuilder, CHANGES);
this.changeDataFactory = changeDataFactory;
this.schema = schema;
- mapping = new ChangeMapping(schema, client.adapter());
+ this.mapping = new ChangeMapping(schema, client.adapter());
+ this.idField =
+ this.schema.useLegacyNumericFields() ? ChangeField.LEGACY_ID : ChangeField.LEGACY_ID_STR;
}
@Override
@@ -157,7 +161,8 @@
}
}
- QueryOptions filteredOpts = opts.filterFields(IndexUtils::changeFields);
+ QueryOptions filteredOpts =
+ opts.filterFields(o -> IndexUtils.changeFields(o, schema.useLegacyNumericFields()));
return new ElasticQuerySource(p, filteredOpts, getURI(indexes), getSortArray());
}
@@ -167,7 +172,7 @@
JsonArray sortArray = new JsonArray();
addNamedElement(ChangeField.UPDATED.getName(), properties, sortArray);
- addNamedElement(ChangeField.LEGACY_ID.getName(), properties, sortArray);
+ addNamedElement(idField.getName(), properties, sortArray);
return sortArray;
}
@@ -206,7 +211,7 @@
JsonElement c = source.get(ChangeField.CHANGE.getName());
if (c == null) {
- int id = source.get(ChangeField.LEGACY_ID.getName()).getAsInt();
+ int id = source.get(idField.getName()).getAsInt();
// IndexUtils#changeFields ensures either CHANGE or PROJECT is always present.
String projectName = requireNonNull(source.get(ChangeField.PROJECT.getName()).getAsString());
return changeDataFactory.create(Project.nameKey(projectName), Change.id(id));
diff --git a/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java b/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
index 394158d..d05e91c 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
@@ -145,7 +145,7 @@
String name = p.getField().getName();
String value = p.getValue();
- if (value.isEmpty()) {
+ if (!p.getField().isRepeatable() && value.isEmpty()) {
return new BoolQueryBuilder().mustNot(QueryBuilders.existsQuery(name));
} else if (p instanceof RegexPredicate) {
if (value.startsWith("^")) {
diff --git a/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java b/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
index a693f6d..2f0bd01 100644
--- a/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
+++ b/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
@@ -40,11 +40,7 @@
for (Values<V> values : schema.buildFields(v)) {
String name = values.getField().getName();
if (values.getField().isRepeatable()) {
- builder.field(
- name,
- Streams.stream(values.getValues())
- .filter(e -> shouldAddElement(e))
- .collect(toList()));
+ builder.field(name, Streams.stream(values.getValues()).collect(toList()));
} else {
Object element = Iterables.getOnlyElement(values.getValues(), "");
if (shouldAddElement(element)) {
diff --git a/java/com/google/gerrit/entities/Change.java b/java/com/google/gerrit/entities/Change.java
index ced3823..739bd38 100644
--- a/java/com/google/gerrit/entities/Change.java
+++ b/java/com/google/gerrit/entities/Change.java
@@ -44,7 +44,7 @@
* |
* +- {@link PatchSetApproval}: a +/- vote on the change's current state.
* |
- * +- {@link PatchLineComment}: comment about a specific line
+ * +- {@link Comment}: comment about a specific line
* </pre>
*
* <p>
@@ -60,8 +60,8 @@
* commit can contain zero patches, if the merge has no conflicts, or has no impact other than to
* cut off a line of development.
*
- * <p>Each PatchLineComment is a draft or a published comment about a single line of the associated
- * file. These are the inline comment entities created by users as they perform a review.
+ * <p>Each Comment is a draft or a published comment about a single line of the associated file.
+ * These are the inline comment entities created by users as they perform a review.
*
* <p>When additional PatchSets appear under a change, these PatchSets reference <i>replacement</i>
* commits; alternative commits that could be made to the project instead of the original commit
@@ -76,10 +76,10 @@
* <h5>ChangeMessage</h5>
*
* <p>The ChangeMessage entity is a general free-form comment about the whole change, rather than
- * PatchLineComment's file and line specific context. The ChangeMessage appears at the start of any
- * email generated by Gerrit, and is shown on the change overview page, rather than in a
- * file-specific context. Users often use this entity to describe general remarks about the overall
- * concept proposed by the change.
+ * Comment's file and line specific context. The ChangeMessage appears at the start of any email
+ * generated by Gerrit, and is shown on the change overview page, rather than in a file-specific
+ * context. Users often use this entity to describe general remarks about the overall concept
+ * proposed by the change.
*
* <p>
*
diff --git a/java/com/google/gerrit/entities/Comment.java b/java/com/google/gerrit/entities/Comment.java
index db62beea..65c642c 100644
--- a/java/com/google/gerrit/entities/Comment.java
+++ b/java/com/google/gerrit/entities/Comment.java
@@ -29,30 +29,33 @@
*
* <p>Changing fields in this class changes the storage format of inline comments in NoteDb and may
* require a corresponding data migration (adding new optional fields is generally okay).
- *
- * <p>{@link PatchLineComment} historically represented comments in ReviewDb. There are a few
- * notable differences:
- *
- * <ul>
- * <li>PatchLineComment knows the comment status (published or draft). For comments in NoteDb the
- * status is determined by the branch in which they are stored (published comments are stored
- * in the change meta ref; draft comments are store in refs/draft-comments branches in
- * All-Users). Hence Comment doesn't need to contain the status, but the status is implicitly
- * known by where the comments are read from.
- * <li>PatchLineComment knows the change ID. For comments in NoteDb, the change ID is determined
- * by the branch in which they are stored (the ref name contains the change ID). Hence Comment
- * doesn't need to contain the change ID, but the change ID is implicitly known by where the
- * comments are read from.
- * </ul>
- *
- * <p>For all utility classes and middle layer functionality using Comment over PatchLineComment is
- * preferred, as ReviewDb is gone so PatchLineComment is slated for deletion as well. This means
- * Comment should be used everywhere and only for storing inline comment in ReviewDb a conversion to
- * PatchLineComment is done. Converting Comments to PatchLineComments and vice verse is done by
- * CommentsUtil#toPatchLineComments(Change.Id, PatchLineComment.Status, Iterable) and
- * CommentsUtil#toComments(String, Iterable).
*/
public class Comment {
+ public enum Status {
+ DRAFT('d'),
+
+ PUBLISHED('P');
+
+ private final char code;
+
+ Status(char c) {
+ code = c;
+ }
+
+ public char getCode() {
+ return code;
+ }
+
+ public static Status forCode(char c) {
+ for (Status s : Status.values()) {
+ if (s.code == c) {
+ return s;
+ }
+ }
+ return null;
+ }
+ }
+
public static class Key {
public String uuid;
public String filename;
diff --git a/java/com/google/gerrit/entities/PatchLineComment.java b/java/com/google/gerrit/entities/PatchLineComment.java
deleted file mode 100644
index 2182589..0000000
--- a/java/com/google/gerrit/entities/PatchLineComment.java
+++ /dev/null
@@ -1,327 +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.entities;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.client.Comment.Range;
-import java.sql.Timestamp;
-import java.util.Objects;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
-
-/**
- * A comment left by a user on a specific line of a {@link Patch}.
- *
- * <p>New APIs should not expose this class.
- *
- * @see Comment
- */
-public final class PatchLineComment {
- public static final char STATUS_DRAFT = 'd';
- public static final char STATUS_PUBLISHED = 'P';
-
- public enum Status {
- DRAFT(STATUS_DRAFT),
-
- PUBLISHED(STATUS_PUBLISHED);
-
- private final char code;
-
- Status(char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
-
- public static Status forCode(char c) {
- for (Status s : Status.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
- }
-
- public static PatchLineComment from(
- Change.Id changeId, PatchLineComment.Status status, Comment c) {
- Patch.Key patchKey = Patch.key(PatchSet.id(changeId, c.key.patchSetId), c.key.filename);
- PatchLineComment plc =
- new PatchLineComment(
- patchKey, c.key.uuid, c.lineNbr, c.author.getId(), c.parentUuid, c.writtenOn);
- plc.setSide(c.side);
- plc.setMessage(c.message);
- if (c.range != null) {
- Comment.Range r = c.range;
- plc.setRange(new CommentRange(r.startLine, r.startChar, r.endLine, r.endChar));
- }
- plc.setTag(c.tag);
- plc.setCommitId(c.getCommitId());
- plc.setStatus(status);
- plc.setRealAuthor(c.getRealAuthor().getId());
- plc.setUnresolved(c.unresolved);
- return plc;
- }
-
- protected Patch.Key patchKey;
-
- protected String uuid;
-
- /** Line number this comment applies to; it should display after the line. */
- protected int lineNbr;
-
- /** Who wrote this comment. */
- protected Account.Id author;
-
- /** When this comment was drafted. */
- protected Timestamp writtenOn;
-
- /** Current publication state of the comment; see {@link Status}. */
- protected char status;
-
- /** Which file is this comment; 0 is ancestor, 1 is new version. */
- protected short side;
-
- /** The text left by the user. */
- @Nullable protected String message;
-
- /** The parent of this comment, or null if this is the first comment on this line */
- @Nullable protected String parentUuid;
-
- @Nullable protected CommentRange range;
-
- @Nullable protected String tag;
-
- /** Real user that added this comment on behalf of the user recorded in {@link #author}. */
- @Nullable protected Account.Id realAuthor;
-
- /** True if this comment requires further action. */
- protected boolean unresolved;
-
- /** The ID of the commit to which this comment is referring. */
- protected ObjectId commitId;
-
- protected PatchLineComment() {}
-
- public PatchLineComment(
- Patch.Key patchKey, String uuid, int line, Account.Id a, String parentUuid, Timestamp when) {
- this.patchKey = patchKey;
- this.uuid = uuid;
- lineNbr = line;
- author = a;
- setParentUuid(parentUuid);
- setStatus(Status.DRAFT);
- setWrittenOn(when);
- }
-
- public PatchLineComment(PatchLineComment o) {
- patchKey = o.patchKey;
- uuid = o.uuid;
- lineNbr = o.lineNbr;
- author = o.author;
- realAuthor = o.realAuthor;
- writtenOn = o.writtenOn;
- status = o.status;
- side = o.side;
- message = o.message;
- parentUuid = o.parentUuid;
- commitId = o.commitId;
- if (o.range != null) {
- range =
- new CommentRange(
- o.range.getStartLine(),
- o.range.getStartCharacter(),
- o.range.getEndLine(),
- o.range.getEndCharacter());
- }
- }
-
- public PatchSet.Id getPatchSetId() {
- return patchKey.patchSetId();
- }
-
- public int getLine() {
- return lineNbr;
- }
-
- public void setLine(int line) {
- lineNbr = line;
- }
-
- public Account.Id getAuthor() {
- return author;
- }
-
- public Account.Id getRealAuthor() {
- return realAuthor != null ? realAuthor : getAuthor();
- }
-
- public void setRealAuthor(Account.Id id) {
- // Use null for same real author, as before the column was added.
- realAuthor = Objects.equals(getAuthor(), id) ? null : id;
- }
-
- public Timestamp getWrittenOn() {
- return writtenOn;
- }
-
- public Status getStatus() {
- return Status.forCode(status);
- }
-
- public void setStatus(Status s) {
- status = s.getCode();
- }
-
- public short getSide() {
- return side;
- }
-
- public void setSide(short s) {
- side = s;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String s) {
- message = s;
- }
-
- public void setWrittenOn(Timestamp ts) {
- writtenOn = ts;
- }
-
- public String getParentUuid() {
- return parentUuid;
- }
-
- public void setParentUuid(String inReplyTo) {
- parentUuid = inReplyTo;
- }
-
- public void setRange(Range r) {
- if (r != null) {
- range =
- new CommentRange(
- r.startLine, r.startCharacter,
- r.endLine, r.endCharacter);
- } else {
- range = null;
- }
- }
-
- public void setRange(CommentRange r) {
- range = r;
- }
-
- public CommentRange getRange() {
- return range;
- }
-
- public void setCommitId(AnyObjectId commitId) {
- this.commitId = commitId.copy();
- }
-
- public ObjectId getCommitId() {
- return commitId;
- }
-
- public void setTag(String tag) {
- this.tag = tag;
- }
-
- public String getTag() {
- return tag;
- }
-
- public void setUnresolved(Boolean unresolved) {
- this.unresolved = unresolved;
- }
-
- public Boolean getUnresolved() {
- return unresolved;
- }
-
- public Comment asComment(String serverId) {
- Comment c =
- new Comment(
- new Comment.Key(uuid, patchKey.fileName(), patchKey.patchSetId().get()),
- author,
- writtenOn,
- side,
- message,
- serverId,
- unresolved);
- c.setCommitId(commitId);
- c.setRange(range);
- c.lineNbr = lineNbr;
- c.parentUuid = parentUuid;
- c.tag = tag;
- c.setRealAuthor(getRealAuthor());
- return c;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof PatchLineComment) {
- PatchLineComment c = (PatchLineComment) o;
- return Objects.equals(patchKey, c.patchKey)
- && Objects.equals(uuid, c.uuid)
- && Objects.equals(lineNbr, c.getLine())
- && Objects.equals(author, c.getAuthor())
- && Objects.equals(writtenOn, c.getWrittenOn())
- && Objects.equals(status, c.getStatus().getCode())
- && Objects.equals(side, c.getSide())
- && Objects.equals(message, c.getMessage())
- && Objects.equals(parentUuid, c.getParentUuid())
- && Objects.equals(range, c.getRange())
- && Objects.equals(commitId, c.getCommitId())
- && Objects.equals(tag, c.getTag())
- && Objects.equals(unresolved, c.getUnresolved());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(patchKey, uuid);
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("PatchLineComment{");
- builder.append("patchKey=").append(patchKey).append(',');
- builder.append("uuid=").append(uuid).append(',');
- builder.append("lineNbr=").append(lineNbr).append(',');
- builder.append("author=").append(author.get()).append(',');
- builder.append("realAuthor=").append(realAuthor != null ? realAuthor.get() : "").append(',');
- builder.append("writtenOn=").append(writtenOn.toString()).append(',');
- builder.append("status=").append(status).append(',');
- builder.append("side=").append(side).append(',');
- builder.append("message=").append(Objects.toString(message, "")).append(',');
- builder.append("parentUuid=").append(Objects.toString(parentUuid, "")).append(',');
- builder.append("range=").append(Objects.toString(range, "")).append(',');
- builder.append("revId=").append(commitId != null ? commitId.name() : "").append(',');
- builder.append("tag=").append(Objects.toString(tag, "")).append(',');
- builder.append("unresolved=").append(unresolved);
- builder.append('}');
- return builder.toString();
- }
-}
diff --git a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
index 26a1a27..119d941 100644
--- a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
+++ b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -27,6 +27,7 @@
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.common.MergePatchSetInput;
import com.google.gerrit.extensions.common.PureRevertInfo;
+import com.google.gerrit.extensions.common.RevertSubmissionInfo;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException;
@@ -161,6 +162,12 @@
*/
ChangeApi revert(RevertInput in) throws RestApiException;
+ default RevertSubmissionInfo revertSubmission() throws RestApiException {
+ return revertSubmission(new RevertInput());
+ }
+
+ RevertSubmissionInfo revertSubmission(RevertInput in) throws RestApiException;
+
/** Create a merge patch set for the change. */
ChangeInfo createMergePatchSet(MergePatchSetInput in) throws RestApiException;
@@ -502,6 +509,11 @@
}
@Override
+ public RevertSubmissionInfo revertSubmission(RevertInput in) throws RestApiException {
+ throw new NotImplementedException();
+ }
+
+ @Override
public void rebase(RebaseInput in) throws RestApiException {
throw new NotImplementedException();
}
diff --git a/java/com/google/gerrit/extensions/common/RevertSubmissionInfo.java b/java/com/google/gerrit/extensions/common/RevertSubmissionInfo.java
new file mode 100644
index 0000000..dabd035
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/RevertSubmissionInfo.java
@@ -0,0 +1,21 @@
+// Copyright (C) 2019 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.common;
+
+import java.util.List;
+
+public class RevertSubmissionInfo {
+ public List<ChangeInfo> revertChanges;
+}
diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
index 40939b3..b9b66bc 100644
--- a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
+++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
@@ -112,9 +112,6 @@
SanitizedContent sanitizedStaticPath = urlInScriptTagOrdainer.apply(staticPath);
ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
- // TODO(taoalpha): Remove once p2 fully rolled out
- data.put("polymer2", "true");
-
if (canonicalPath != null) {
data.put("canonicalPath", canonicalPath);
}
diff --git a/java/com/google/gerrit/index/IndexType.java b/java/com/google/gerrit/index/IndexType.java
index a92b734..ee44deb 100644
--- a/java/com/google/gerrit/index/IndexType.java
+++ b/java/com/google/gerrit/index/IndexType.java
@@ -52,10 +52,6 @@
return type.equals(ELASTICSEARCH);
}
- public static boolean isElasticsearch(String type) {
- return type.toLowerCase().equals(ELASTICSEARCH);
- }
-
@Override
public String toString() {
return type;
diff --git a/java/com/google/gerrit/index/Schema.java b/java/com/google/gerrit/index/Schema.java
index ed7ae30..f9f8c48 100644
--- a/java/com/google/gerrit/index/Schema.java
+++ b/java/com/google/gerrit/index/Schema.java
@@ -34,6 +34,7 @@
public static class Builder<T> {
private final List<FieldDef<T, ?>> fields = new ArrayList<>();
+ private boolean useLegacyNumericFields;
public Builder<T> add(Schema<T> schema) {
this.fields.addAll(schema.getFields().values());
@@ -52,8 +53,13 @@
return this;
}
+ public Builder<T> legacyNumericFields(boolean useLegacyNumericFields) {
+ this.useLegacyNumericFields = useLegacyNumericFields;
+ return this;
+ }
+
public Schema<T> build() {
- return new Schema<>(ImmutableList.copyOf(fields));
+ return new Schema<>(useLegacyNumericFields, ImmutableList.copyOf(fields));
}
}
@@ -82,14 +88,15 @@
private final ImmutableMap<String, FieldDef<T, ?>> fields;
private final ImmutableMap<String, FieldDef<T, ?>> storedFields;
+ private final boolean useLegacyNumericFields;
private int version;
- public Schema(Iterable<FieldDef<T, ?>> fields) {
- this(0, fields);
+ public Schema(boolean useLegacyNumericFields, Iterable<FieldDef<T, ?>> fields) {
+ this(0, useLegacyNumericFields, fields);
}
- public Schema(int version, Iterable<FieldDef<T, ?>> fields) {
+ public Schema(int version, boolean useLegacyNumericFields, Iterable<FieldDef<T, ?>> fields) {
this.version = version;
ImmutableMap.Builder<String, FieldDef<T, ?>> b = ImmutableMap.builder();
ImmutableMap.Builder<String, FieldDef<T, ?>> sb = ImmutableMap.builder();
@@ -101,12 +108,17 @@
}
this.fields = b.build();
this.storedFields = sb.build();
+ this.useLegacyNumericFields = useLegacyNumericFields;
}
public final int getVersion() {
return version;
}
+ public final boolean useLegacyNumericFields() {
+ return useLegacyNumericFields;
+ }
+
/**
* Get all fields in this schema.
*
diff --git a/java/com/google/gerrit/index/SchemaUtil.java b/java/com/google/gerrit/index/SchemaUtil.java
index c59f251..9599d6a 100644
--- a/java/com/google/gerrit/index/SchemaUtil.java
+++ b/java/com/google/gerrit/index/SchemaUtil.java
@@ -67,12 +67,19 @@
}
public static <V> Schema<V> schema(Collection<FieldDef<V, ?>> fields) {
- return new Schema<>(ImmutableList.copyOf(fields));
+ return new Schema<>(true, ImmutableList.copyOf(fields));
+ }
+
+ public static <V> Schema<V> schema(Schema<V> schema, boolean useLegacyNumericFields) {
+ return new Schema<>(
+ useLegacyNumericFields,
+ new ImmutableList.Builder<FieldDef<V, ?>>().addAll(schema.getFields().values()).build());
}
@SafeVarargs
public static <V> Schema<V> schema(Schema<V> schema, FieldDef<V, ?>... moreFields) {
return new Schema<>(
+ true,
new ImmutableList.Builder<FieldDef<V, ?>>()
.addAll(schema.getFields().values())
.addAll(ImmutableList.copyOf(moreFields))
@@ -81,7 +88,7 @@
@SafeVarargs
public static <V> Schema<V> schema(FieldDef<V, ?>... fields) {
- return schema(ImmutableList.copyOf(fields));
+ return new Schema<>(true, ImmutableList.copyOf(fields));
}
public static Set<String> getPersonParts(PersonIdent person) {
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index 7a0430c..deb3203 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -63,8 +63,10 @@
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LegacyIntField;
import org.apache.lucene.document.LegacyLongField;
+import org.apache.lucene.document.LongPoint;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
@@ -334,15 +336,23 @@
if (type == FieldType.INTEGER || type == FieldType.INTEGER_RANGE) {
for (Object value : values.getValues()) {
- doc.add(new LegacyIntField(name, (Integer) value, store));
+ Integer intValue = (Integer) value;
+ if (schema.useLegacyNumericFields()) {
+ doc.add(new LegacyIntField(name, intValue, store));
+ } else {
+ doc.add(new IntPoint(name, intValue));
+ if (store == Store.YES) {
+ doc.add(new StoredField(name, intValue));
+ }
+ }
}
} else if (type == FieldType.LONG) {
for (Object value : values.getValues()) {
- doc.add(new LegacyLongField(name, (Long) value, store));
+ addLongField(doc, name, store, (Long) value);
}
} else if (type == FieldType.TIMESTAMP) {
for (Object value : values.getValues()) {
- doc.add(new LegacyLongField(name, ((Timestamp) value).getTime(), store));
+ addLongField(doc, name, store, ((Timestamp) value).getTime());
}
} else if (type == FieldType.EXACT || type == FieldType.PREFIX) {
for (Object value : values.getValues()) {
@@ -361,6 +371,17 @@
}
}
+ private void addLongField(Document doc, String name, Store store, Long longValue) {
+ if (schema.useLegacyNumericFields()) {
+ doc.add(new LegacyLongField(name, longValue, store));
+ } else {
+ doc.add(new LongPoint(name, longValue));
+ if (store == Store.YES) {
+ doc.add(new StoredField(name, longValue));
+ }
+ }
+ }
+
protected FieldBundle toFieldBundle(Document doc) {
Map<String, FieldDef<V, ?>> allFields = getSchema().getFields();
ListMultimap<String, Object> rawFields = ArrayListMultimap.create();
diff --git a/java/com/google/gerrit/lucene/ChangeSubIndex.java b/java/com/google/gerrit/lucene/ChangeSubIndex.java
index 778c0e5..fd439f1 100644
--- a/java/com/google/gerrit/lucene/ChangeSubIndex.java
+++ b/java/com/google/gerrit/lucene/ChangeSubIndex.java
@@ -15,6 +15,7 @@
package com.google.gerrit.lucene;
import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.gerrit.lucene.LuceneChangeIndex.ID2_SORT_FIELD;
import static com.google.gerrit.lucene.LuceneChangeIndex.ID_SORT_FIELD;
import static com.google.gerrit.lucene.LuceneChangeIndex.UPDATED_SORT_FIELD;
import static com.google.gerrit.server.index.change.ChangeSchemaDefinitions.NAME;
@@ -99,6 +100,9 @@
if (f == ChangeField.LEGACY_ID) {
int v = (Integer) getOnlyElement(values.getValues());
doc.add(new NumericDocValuesField(ID_SORT_FIELD, v));
+ } else if (f == ChangeField.LEGACY_ID_STR) {
+ String v = (String) getOnlyElement(values.getValues());
+ doc.add(new NumericDocValuesField(ID2_SORT_FIELD, Integer.valueOf(v)));
} else if (f == ChangeField.UPDATED) {
long t = ((Timestamp) getOnlyElement(values.getValues())).getTime();
doc.add(new NumericDocValuesField(UPDATED_SORT_FIELD, t));
diff --git a/java/com/google/gerrit/lucene/LuceneAccountIndex.java b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
index ccbe8a3..efd7ea3 100644
--- a/java/com/google/gerrit/lucene/LuceneAccountIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
@@ -17,6 +17,7 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.gerrit.server.index.account.AccountField.FULL_NAME;
import static com.google.gerrit.server.index.account.AccountField.ID;
+import static com.google.gerrit.server.index.account.AccountField.ID_STR;
import static com.google.gerrit.server.index.account.AccountField.PREFERRED_EMAIL_EXACT;
import com.google.gerrit.entities.Account;
@@ -60,13 +61,18 @@
private static final String FULL_NAME_SORT_FIELD = sortFieldName(FULL_NAME);
private static final String EMAIL_SORT_FIELD = sortFieldName(PREFERRED_EMAIL_EXACT);
private static final String ID_SORT_FIELD = sortFieldName(ID);
+ private static final String ID2_SORT_FIELD = sortFieldName(ID_STR);
- private static Term idTerm(AccountState as) {
- return idTerm(as.account().id());
+ private static Term idTerm(boolean useLegacyNumericFields, AccountState as) {
+ return idTerm(useLegacyNumericFields, as.account().id());
}
- private static Term idTerm(Account.Id id) {
- return QueryBuilder.intTerm(ID.getName(), id.get());
+ private static Term idTerm(boolean useLegacyNumericFields, Account.Id id) {
+ FieldDef<AccountState, ?> idField = useLegacyNumericFields ? ID : ID_STR;
+ if (useLegacyNumericFields) {
+ return QueryBuilder.intTerm(idField.getName(), id.get());
+ }
+ return QueryBuilder.stringTerm(idField.getName(), Integer.toString(id.get()));
}
private final GerritIndexWriterConfig indexWriterConfig;
@@ -110,6 +116,9 @@
if (f == ID) {
int v = (Integer) getOnlyElement(values.getValues());
doc.add(new NumericDocValuesField(ID_SORT_FIELD, v));
+ } else if (f == ID_STR) {
+ String v = (String) getOnlyElement(values.getValues());
+ doc.add(new NumericDocValuesField(ID2_SORT_FIELD, Integer.valueOf(v)));
} else if (f == FULL_NAME) {
String value = (String) getOnlyElement(values.getValues());
doc.add(new SortedDocValuesField(FULL_NAME_SORT_FIELD, new BytesRef(value)));
@@ -123,7 +132,7 @@
@Override
public void replace(AccountState as) {
try {
- replace(idTerm(as), toDocument(as)).get();
+ replace(idTerm(getSchema().useLegacyNumericFields(), as), toDocument(as)).get();
} catch (ExecutionException | InterruptedException e) {
throw new StorageException(e);
}
@@ -132,7 +141,7 @@
@Override
public void delete(Account.Id key) {
try {
- delete(idTerm(key)).get();
+ delete(idTerm(getSchema().useLegacyNumericFields(), key)).get();
} catch (ExecutionException | InterruptedException e) {
throw new StorageException(e);
}
@@ -141,20 +150,29 @@
@Override
public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts)
throws QueryParseException {
+ queryBuilder.getSchema().useLegacyNumericFields();
return new LuceneQuerySource(
- opts.filterFields(IndexUtils::accountFields), queryBuilder.toQuery(p), getSort());
+ opts.filterFields(o -> IndexUtils.accountFields(o, getSchema().useLegacyNumericFields())),
+ queryBuilder.toQuery(p),
+ getSort());
}
private Sort getSort() {
+ String idSortField = getSchema().useLegacyNumericFields() ? ID_SORT_FIELD : ID2_SORT_FIELD;
return new Sort(
new SortField(FULL_NAME_SORT_FIELD, SortField.Type.STRING, false),
new SortField(EMAIL_SORT_FIELD, SortField.Type.STRING, false),
- new SortField(ID_SORT_FIELD, SortField.Type.LONG, false));
+ new SortField(idSortField, SortField.Type.LONG, false));
}
@Override
protected AccountState fromDocument(Document doc) {
- Account.Id id = Account.id(doc.getField(ID.getName()).numericValue().intValue());
+ FieldDef<AccountState, ?> idField = getSchema().useLegacyNumericFields() ? ID : ID_STR;
+ Account.Id id =
+ Account.id(
+ getSchema().useLegacyNumericFields()
+ ? doc.getField(idField.getName()).numericValue().intValue()
+ : Integer.valueOf(doc.getField(idField.getName()).stringValue()));
// Use the AccountCache rather than depending on any stored fields in the document (of which
// there shouldn't be any). The most expensive part to compute anyway is the effective group
// IDs, and we don't have a good way to reindex when those change.
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 80adaf1..16d66b6 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -18,6 +18,7 @@
import static com.google.gerrit.lucene.AbstractLuceneIndex.sortFieldName;
import static com.google.gerrit.server.git.QueueProvider.QueueType.INTERACTIVE;
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
+import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID_STR;
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
import static com.google.gerrit.server.index.change.ChangeIndexRewriter.CLOSED_STATUSES;
import static com.google.gerrit.server.index.change.ChangeIndexRewriter.OPEN_STATUSES;
@@ -44,6 +45,7 @@
import com.google.gerrit.entities.converter.PatchSetProtoConverter;
import com.google.gerrit.entities.converter.ProtoConverter;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.FieldBundle;
@@ -106,6 +108,7 @@
static final String UPDATED_SORT_FIELD = sortFieldName(ChangeField.UPDATED);
static final String ID_SORT_FIELD = sortFieldName(ChangeField.LEGACY_ID);
+ static final String ID2_SORT_FIELD = sortFieldName(ChangeField.LEGACY_ID_STR);
private static final String CHANGES = "changes";
private static final String CHANGES_OPEN = "open";
@@ -134,12 +137,22 @@
private static final String UNRESOLVED_COMMENT_COUNT_FIELD =
ChangeField.UNRESOLVED_COMMENT_COUNT.getName();
- static Term idTerm(ChangeData cd) {
- return QueryBuilder.intTerm(LEGACY_ID.getName(), cd.getId().get());
+ @FunctionalInterface
+ static interface IdTerm {
+ Term get(String name, int id);
}
- static Term idTerm(Change.Id id) {
- return QueryBuilder.intTerm(LEGACY_ID.getName(), id.get());
+ static Term idTerm(IdTerm idTerm, FieldDef<ChangeData, ?> idField, ChangeData cd) {
+ return idTerm(idTerm, idField, cd.getId());
+ }
+
+ static Term idTerm(IdTerm idTerm, FieldDef<ChangeData, ?> idField, Change.Id id) {
+ return idTerm.get(idField.getName(), id.get());
+ }
+
+ @FunctionalInterface
+ static interface ChangeIdExtractor {
+ Change.Id extract(IndexableField f);
}
private final ListeningExecutorService executor;
@@ -149,6 +162,12 @@
private final ChangeSubIndex openIndex;
private final ChangeSubIndex closedIndex;
+ // TODO(davido): Remove the below fields when support for legacy numeric fields is removed.
+ private final FieldDef<ChangeData, ?> idField;
+ private final String idSortFieldName;
+ private final IdTerm idTerm;
+ private final ChangeIdExtractor extractor;
+
@Inject
LuceneChangeIndex(
@GerritServerConfig Config cfg,
@@ -183,6 +202,20 @@
new ChangeSubIndex(
schema, sitePaths, dir.resolve(CHANGES_CLOSED), closedConfig, searcherFactory);
}
+
+ idField = this.schema.useLegacyNumericFields() ? LEGACY_ID : LEGACY_ID_STR;
+ idSortFieldName = schema.useLegacyNumericFields() ? ID_SORT_FIELD : ID2_SORT_FIELD;
+ idTerm =
+ (name, id) ->
+ this.schema.useLegacyNumericFields()
+ ? QueryBuilder.intTerm(name, id)
+ : QueryBuilder.stringTerm(name, Integer.toString(id));
+ extractor =
+ (f) ->
+ Change.id(
+ this.schema.useLegacyNumericFields()
+ ? f.numericValue().intValue()
+ : Integer.valueOf(f.stringValue()));
}
@Override
@@ -201,7 +234,7 @@
@Override
public void replace(ChangeData cd) {
- Term id = LuceneChangeIndex.idTerm(cd);
+ Term id = LuceneChangeIndex.idTerm(idTerm, idField, cd);
// toDocument is essentially static and doesn't depend on the specific
// sub-index, so just pick one.
Document doc = openIndex.toDocument(cd);
@@ -217,10 +250,10 @@
}
@Override
- public void delete(Change.Id id) {
- Term idTerm = LuceneChangeIndex.idTerm(id);
+ public void delete(Change.Id changeId) {
+ Term id = LuceneChangeIndex.idTerm(idTerm, idField, changeId);
try {
- Futures.allAsList(openIndex.delete(idTerm), closedIndex.delete(idTerm)).get();
+ Futures.allAsList(openIndex.delete(id), closedIndex.delete(id)).get();
} catch (ExecutionException | InterruptedException e) {
throw new StorageException(e);
}
@@ -256,7 +289,7 @@
private Sort getSort() {
return new Sort(
new SortField(UPDATED_SORT_FIELD, SortField.Type.LONG, true),
- new SortField(ID_SORT_FIELD, SortField.Type.LONG, true));
+ new SortField(idSortFieldName, SortField.Type.LONG, true));
}
private class QuerySource implements ChangeDataSource {
@@ -304,7 +337,7 @@
throw new StorageException("interrupted");
}
- final Set<String> fields = IndexUtils.changeFields(opts);
+ final Set<String> fields = IndexUtils.changeFields(opts, schema.useLegacyNumericFields());
return new ChangeDataResults(
executor.submit(
new Callable<List<Document>>() {
@@ -325,7 +358,7 @@
public ResultSet<FieldBundle> readRaw() {
List<Document> documents;
try {
- documents = doRead(IndexUtils.changeFields(opts));
+ documents = doRead(IndexUtils.changeFields(opts, schema.useLegacyNumericFields()));
} catch (IOException e) {
throw new StorageException(e);
}
@@ -403,9 +436,8 @@
List<Document> docs = future.get();
ImmutableList.Builder<ChangeData> result =
ImmutableList.builderWithExpectedSize(docs.size());
- String idFieldName = LEGACY_ID.getName();
for (Document doc : docs) {
- result.add(toChangeData(fields(doc, fields), fields, idFieldName));
+ result.add(toChangeData(fields(doc, fields), fields, idField.getName()));
}
return result.build();
} catch (InterruptedException e) {
@@ -446,10 +478,10 @@
cd = changeDataFactory.create(parseProtoFrom(proto, ChangeProtoConverter.INSTANCE));
} else {
IndexableField f = Iterables.getFirst(doc.get(idFieldName), null);
- Change.Id id = Change.id(f.numericValue().intValue());
+
// IndexUtils#changeFields ensures either CHANGE or PROJECT is always present.
IndexableField project = doc.get(PROJECT.getName()).iterator().next();
- cd = changeDataFactory.create(Project.nameKey(project.stringValue()), id);
+ cd = changeDataFactory.create(Project.nameKey(project.stringValue()), extractor.extract(f));
}
// Any decoding that is done here must also be done in {@link ElasticChangeIndex}.
diff --git a/java/com/google/gerrit/lucene/QueryBuilder.java b/java/com/google/gerrit/lucene/QueryBuilder.java
index ce5ba98..e8ef95f 100644
--- a/java/com/google/gerrit/lucene/QueryBuilder.java
+++ b/java/com/google/gerrit/lucene/QueryBuilder.java
@@ -36,6 +36,8 @@
import java.util.Date;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.document.IntPoint;
+import org.apache.lucene.document.LongPoint;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.LegacyNumericRangeQuery;
@@ -49,6 +51,21 @@
@SuppressWarnings("deprecation")
public class QueryBuilder<V> {
+ @FunctionalInterface
+ static interface IntTermQuery {
+ Query get(String name, int value);
+ }
+
+ @FunctionalInterface
+ static interface IntRangeQuery {
+ Query get(String name, int min, int max);
+ }
+
+ @FunctionalInterface
+ static interface LongRangeQuery {
+ Query get(String name, long min, long max);
+ }
+
static Term intTerm(String name, int value) {
BytesRefBuilder builder = new BytesRefBuilder();
LegacyNumericUtils.intToPrefixCoded(value, 0, builder);
@@ -61,12 +78,36 @@
return new Term(name, builder.get());
}
+ static Query intPoint(String name, int value) {
+ return IntPoint.newExactQuery(name, value);
+ }
+
private final Schema<V> schema;
private final org.apache.lucene.util.QueryBuilder queryBuilder;
+ // TODO(davido): Remove the below fields when support for legacy numeric fields is removed.
+ private final IntTermQuery intTermQuery;
+ private final IntRangeQuery intRangeTermQuery;
+ private final LongRangeQuery longRangeQuery;
+
public QueryBuilder(Schema<V> schema, Analyzer analyzer) {
this.schema = schema;
queryBuilder = new org.apache.lucene.util.QueryBuilder(analyzer);
+ intTermQuery =
+ (name, value) ->
+ this.schema.useLegacyNumericFields()
+ ? new TermQuery(intTerm(name, value))
+ : intPoint(name, value);
+ intRangeTermQuery =
+ (name, min, max) ->
+ this.schema.useLegacyNumericFields()
+ ? LegacyNumericRangeQuery.newIntRange(name, min, max, true, true)
+ : IntPoint.newRangeQuery(name, min, max);
+ longRangeQuery =
+ (name, min, max) ->
+ this.schema.useLegacyNumericFields()
+ ? LegacyNumericRangeQuery.newLongRange(name, min, max, true, true)
+ : LongPoint.newRangeQuery(name, min, max);
}
public Query toQuery(Predicate<V> p) throws QueryParseException {
@@ -169,20 +210,20 @@
} catch (NumberFormatException e) {
throw new QueryParseException("not an integer: " + p.getValue());
}
- return new TermQuery(intTerm(p.getField().getName(), value));
+ return intTermQuery.get(p.getField().getName(), value);
}
private Query intRangeQuery(IndexPredicate<V> p) throws QueryParseException {
if (p instanceof IntegerRangePredicate) {
IntegerRangePredicate<V> r = (IntegerRangePredicate<V>) p;
+ String name = r.getField().getName();
int minimum = r.getMinimumValue();
int maximum = r.getMaximumValue();
if (minimum == maximum) {
// Just fall back to a standard integer query.
- return new TermQuery(intTerm(p.getField().getName(), minimum));
+ return intTermQuery.get(name, minimum);
}
- return LegacyNumericRangeQuery.newIntRange(
- r.getField().getName(), minimum, maximum, true, true);
+ return intRangeTermQuery.get(name, minimum, maximum);
}
throw new QueryParseException("not an integer range: " + p);
}
@@ -190,20 +231,16 @@
private Query timestampQuery(IndexPredicate<V> p) throws QueryParseException {
if (p instanceof TimestampRangePredicate) {
TimestampRangePredicate<V> r = (TimestampRangePredicate<V>) p;
- return LegacyNumericRangeQuery.newLongRange(
- r.getField().getName(),
- r.getMinTimestamp().getTime(),
- r.getMaxTimestamp().getTime(),
- true,
- true);
+ return longRangeQuery.get(
+ r.getField().getName(), r.getMinTimestamp().getTime(), r.getMaxTimestamp().getTime());
}
throw new QueryParseException("not a timestamp: " + p);
}
private Query notTimestamp(TimestampRangePredicate<V> r) throws QueryParseException {
if (r.getMinTimestamp().getTime() == 0) {
- return LegacyNumericRangeQuery.newLongRange(
- r.getField().getName(), r.getMaxTimestamp().getTime(), null, true, true);
+ return longRangeQuery.get(
+ r.getField().getName(), r.getMaxTimestamp().getTime(), Long.MAX_VALUE);
}
throw new QueryParseException("cannot negate: " + r);
}
@@ -245,4 +282,8 @@
public int toIndexTimeInMinutes(Date ts) {
return (int) (ts.getTime() / 60000);
}
+
+ public Schema<V> getSchema() {
+ return schema;
+ }
}
diff --git a/java/com/google/gerrit/pgm/Init.java b/java/com/google/gerrit/pgm/Init.java
index 0537fe9..799377c 100644
--- a/java/com/google/gerrit/pgm/Init.java
+++ b/java/com/google/gerrit/pgm/Init.java
@@ -32,6 +32,7 @@
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.ioutil.HostPlatform;
import com.google.gerrit.server.securestore.SecureStoreClassName;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -144,7 +145,7 @@
});
modules.add(new GerritServerConfigModule());
Guice.createInjector(modules).injectMembers(this);
- if (!run.flags.cfg.getBoolean("container", "slave", false)) {
+ if (!ReplicaUtil.isReplica(run.flags.cfg)) {
reindexProjects();
}
start(run);
diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java
index 3cc9315..2e526bb 100644
--- a/java/com/google/gerrit/pgm/Reindex.java
+++ b/java/com/google/gerrit/pgm/Reindex.java
@@ -34,6 +34,7 @@
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
@@ -144,14 +145,15 @@
if (changesVersion != null) {
versions.put(ChangeSchemaDefinitions.INSTANCE.getName(), changesVersion);
}
- boolean slave = globalConfig.getBoolean("container", "slave", false);
+ boolean replica = ReplicaUtil.isReplica(globalConfig);
List<Module> modules = new ArrayList<>();
Module indexModule;
IndexType indexType = IndexModule.getIndexType(dbInjector);
if (indexType.isLucene()) {
- indexModule = LuceneIndexModule.singleVersionWithExplicitVersions(versions, threads, slave);
+ indexModule = LuceneIndexModule.singleVersionWithExplicitVersions(versions, threads, replica);
} else if (indexType.isElasticsearch()) {
- indexModule = ElasticIndexModule.singleVersionWithExplicitVersions(versions, threads, slave);
+ indexModule =
+ ElasticIndexModule.singleVersionWithExplicitVersions(versions, threads, replica);
} else {
throw new IllegalStateException("unsupported index.type = " + indexType);
}
diff --git a/java/com/google/gerrit/server/AssigneeStatusUpdate.java b/java/com/google/gerrit/server/AssigneeStatusUpdate.java
new file mode 100644
index 0000000..0da5edf
--- /dev/null
+++ b/java/com/google/gerrit/server/AssigneeStatusUpdate.java
@@ -0,0 +1,21 @@
+package com.google.gerrit.server;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.entities.Account;
+import java.sql.Timestamp;
+import java.util.Optional;
+
+/** Change to an assignee's status. */
+@AutoValue
+public abstract class AssigneeStatusUpdate {
+ public static AssigneeStatusUpdate create(
+ Timestamp ts, Account.Id updatedBy, Optional<Account.Id> currentAssignee) {
+ return new AutoValue_AssigneeStatusUpdate(ts, updatedBy, currentAssignee);
+ }
+
+ public abstract Timestamp date();
+
+ public abstract Account.Id updatedBy();
+
+ public abstract Optional<Account.Id> currentAssignee();
+}
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index c858e1a..ae4ba4b 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -19,7 +19,6 @@
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.gerrit.common.Nullable;
@@ -27,7 +26,6 @@
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
import com.google.gerrit.entities.Patch;
-import com.google.gerrit.entities.PatchLineComment;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.RobotComment;
@@ -259,8 +257,7 @@
return sort(comments);
}
- public void putComments(
- ChangeUpdate update, PatchLineComment.Status status, Iterable<Comment> comments) {
+ public void putComments(ChangeUpdate update, Comment.Status status, Iterable<Comment> comments) {
for (Comment c : comments) {
update.putComment(status, c);
}
@@ -353,15 +350,4 @@
comments.sort(COMMENT_ORDER);
return comments;
}
-
- public static Iterable<PatchLineComment> toPatchLineComments(
- Change.Id changeId, PatchLineComment.Status status, Iterable<Comment> comments) {
- return FluentIterable.from(comments).transform(c -> PatchLineComment.from(changeId, status, c));
- }
-
- public static List<Comment> toComments(
- final String serverId, Iterable<PatchLineComment> comments) {
- return COMMENT_ORDER.sortedCopy(
- FluentIterable.from(comments).transform(plc -> plc.asComment(serverId)));
- }
}
diff --git a/java/com/google/gerrit/server/PublishCommentUtil.java b/java/com/google/gerrit/server/PublishCommentUtil.java
index 6ebebcf..c446c92 100644
--- a/java/com/google/gerrit/server/PublishCommentUtil.java
+++ b/java/com/google/gerrit/server/PublishCommentUtil.java
@@ -15,12 +15,13 @@
package com.google.gerrit.server;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.entities.PatchLineComment.Status.PUBLISHED;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableList;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Comment.Status;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.validators.CommentForValidation;
@@ -34,10 +35,14 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
@Singleton
public class PublishCommentUtil {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final PatchListCache patchListCache;
private final PatchSetUtil psUtil;
private final CommentsUtil commentsUtil;
@@ -51,32 +56,58 @@
}
public void publish(
- ChangeContext ctx, PatchSet.Id psId, Collection<Comment> drafts, @Nullable String tag) {
+ ChangeContext ctx,
+ PatchSet.Id psId,
+ Collection<Comment> draftComments,
+ @Nullable String tag) {
ChangeNotes notes = ctx.getNotes();
checkArgument(notes != null);
- if (drafts.isEmpty()) {
+ if (draftComments.isEmpty()) {
return;
}
Map<PatchSet.Id, PatchSet> patchSets =
- psUtil.getAsMap(notes, drafts.stream().map(d -> psId(notes, d)).collect(toSet()));
- for (Comment d : drafts) {
- PatchSet ps = patchSets.get(psId(notes, d));
+ psUtil.getAsMap(notes, draftComments.stream().map(d -> psId(notes, d)).collect(toSet()));
+ Set<Comment> commentsToPublish = new HashSet<>();
+ for (Comment draftComment : draftComments) {
+ PatchSet.Id psIdOfDraftComment = psId(notes, draftComment);
+ PatchSet ps = patchSets.get(psIdOfDraftComment);
if (ps == null) {
- throw new StorageException("patch set " + ps + " not found");
+ // This can happen if changes with the same numeric ID exist:
+ // - change 12345 has 3 patch sets in repo X
+ // - another change 12345 has 7 patch sets in repo Y
+ // - the user saves a draft comment on patch set 6 of the change in repo Y
+ // - this draft comment gets stored in:
+ // AllUsers -> refs/draft-comments/45/12345/<account-id>
+ // - when posting a review with draft handling PUBLISH_ALL_REVISIONS on the change in
+ // repo X, the draft comments are loaded from
+ // AllUsers -> refs/draft-comments/45/12345/<account-id>, including the draft
+ // comment that was saved for patch set 6 of the change in repo Y
+ // - patch set 6 does not exist for the change in repo x, hence we get null for the patch
+ // set here
+ // Instead of failing hard (and returning an Internal Server Error) to the caller,
+ // just ignore that comment.
+ // Gerrit ensures that numeric change IDs are unique, but you can get duplicates if
+ // change refs of one repo are copied/pushed to another repo on the same host (this
+ // should never be done, but we know it happens).
+ logger.atWarning().log(
+ "Ignoring draft comment %s on non existing patch set %s (repo = %s)",
+ draftComment, psIdOfDraftComment, notes.getProjectName());
+ continue;
}
- d.writtenOn = ctx.getWhen();
- d.tag = tag;
+ draftComment.writtenOn = ctx.getWhen();
+ draftComment.tag = tag;
// Draft may have been created by a different real user; copy the current real user. (Only
// applies to X-Gerrit-RunAs, since modifying drafts via on_behalf_of is not allowed.)
- ctx.getUser().updateRealAccountId(d::setRealAuthor);
+ ctx.getUser().updateRealAccountId(draftComment::setRealAuthor);
try {
- CommentsUtil.setCommentCommitId(d, patchListCache, notes.getChange(), ps);
+ CommentsUtil.setCommentCommitId(draftComment, patchListCache, notes.getChange(), ps);
} catch (PatchListNotAvailableException e) {
throw new StorageException(e);
}
+ commentsToPublish.add(draftComment);
}
- commentsUtil.putComments(ctx.getUpdate(psId), PUBLISHED, drafts);
+ commentsUtil.putComments(ctx.getUpdate(psId), Status.PUBLISHED, commentsToPublish);
}
private static PatchSet.Id psId(ChangeNotes notes, Comment c) {
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index a04be30..d0af686 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -50,6 +50,7 @@
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.MergePatchSetInput;
import com.google.gerrit.extensions.common.PureRevertInfo;
+import com.google.gerrit.extensions.common.RevertSubmissionInfo;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
@@ -96,6 +97,7 @@
import com.google.gerrit.server.restapi.change.Rebase;
import com.google.gerrit.server.restapi.change.Restore;
import com.google.gerrit.server.restapi.change.Revert;
+import com.google.gerrit.server.restapi.change.RevertSubmission;
import com.google.gerrit.server.restapi.change.Reviewers;
import com.google.gerrit.server.restapi.change.Revisions;
import com.google.gerrit.server.restapi.change.SetReadyForReview;
@@ -132,6 +134,7 @@
private final ChangeResource change;
private final Abandon abandon;
private final Revert revert;
+ private final RevertSubmission revertSubmission;
private final Restore restore;
private final CreateMergePatchSet updateByMerge;
private final Provider<SubmittedTogether> submittedTogether;
@@ -181,6 +184,7 @@
ListReviewers listReviewers,
Abandon abandon,
Revert revert,
+ RevertSubmission revertSubmission,
Restore restore,
CreateMergePatchSet updateByMerge,
Provider<SubmittedTogether> submittedTogether,
@@ -219,6 +223,7 @@
@Assisted ChangeResource change) {
this.changeApi = changeApi;
this.revert = revert;
+ this.revertSubmission = revertSubmission;
this.reviewers = reviewers;
this.revisions = revisions;
this.reviewerApi = reviewerApi;
@@ -358,6 +363,15 @@
}
@Override
+ public RevertSubmissionInfo revertSubmission(RevertInput in) throws RestApiException {
+ try {
+ return revertSubmission.apply(change, in).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot revert a change submission", e);
+ }
+ }
+
+ @Override
public ChangeInfo createMergePatchSet(MergePatchSetInput in) throws RestApiException {
try {
return updateByMerge.apply(change, in).value();
diff --git a/java/com/google/gerrit/server/change/AbandonUtil.java b/java/com/google/gerrit/server/change/AbandonUtil.java
index cb89440..1bc1fad 100644
--- a/java/com/google/gerrit/server/change/AbandonUtil.java
+++ b/java/com/google/gerrit/server/change/AbandonUtil.java
@@ -40,7 +40,10 @@
private final ChangeCleanupConfig cfg;
private final Provider<ChangeQueryProcessor> queryProvider;
- private final ChangeQueryBuilder queryBuilder;
+ // Provider is needed, because AbandonUtil is singleton, but ChangeQueryBuilder accesses
+ // index collection, that is only provided when multiversion index module is started.
+ // TODO(davido); Remove provider again, when support for legacy numeric fields is removed.
+ private final Provider<ChangeQueryBuilder> queryBuilderProvider;
private final BatchAbandon batchAbandon;
private final InternalUser internalUser;
@@ -49,11 +52,11 @@
ChangeCleanupConfig cfg,
InternalUser.Factory internalUserFactory,
Provider<ChangeQueryProcessor> queryProvider,
- ChangeQueryBuilder queryBuilder,
+ Provider<ChangeQueryBuilder> queryBuilderProvider,
BatchAbandon batchAbandon) {
this.cfg = cfg;
this.queryProvider = queryProvider;
- this.queryBuilder = queryBuilder;
+ this.queryBuilderProvider = queryBuilderProvider;
this.batchAbandon = batchAbandon;
internalUser = internalUserFactory.create();
}
@@ -71,7 +74,11 @@
}
List<ChangeData> changesToAbandon =
- queryProvider.get().enforceVisibility(false).query(queryBuilder.parse(query)).entities();
+ queryProvider
+ .get()
+ .enforceVisibility(false)
+ .query(queryBuilderProvider.get().parse(query))
+ .entities();
ImmutableListMultimap.Builder<Project.NameKey, ChangeData> builder =
ImmutableListMultimap.builder();
for (ChangeData cd : changesToAbandon) {
@@ -111,7 +118,7 @@
queryProvider
.get()
.enforceVisibility(false)
- .query(queryBuilder.parse(newQuery))
+ .query(queryBuilderProvider.get().parse(newQuery))
.entities();
if (!changesToAbandon.isEmpty()) {
validChanges.add(cd);
diff --git a/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index afaf695..c05a47d 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -17,12 +17,14 @@
import com.google.common.collect.ImmutableList;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -228,7 +230,12 @@
createCommit(repository, basePatchSetCommit, baseTree, newCommitMessage, nowTimestamp);
if (optionalChangeEdit.isPresent()) {
- updateEdit(repository, optionalChangeEdit.get(), newEditCommit, nowTimestamp);
+ updateEdit(
+ notes.getProjectName(),
+ repository,
+ optionalChangeEdit.get(),
+ newEditCommit,
+ nowTimestamp);
} else {
createEdit(repository, notes, basePatchSet, newEditCommit, nowTimestamp);
}
@@ -331,7 +338,12 @@
createCommit(repository, basePatchSetCommit, newTreeId, commitMessage, nowTimestamp);
if (optionalChangeEdit.isPresent()) {
- updateEdit(repository, optionalChangeEdit.get(), newEditCommit, nowTimestamp);
+ updateEdit(
+ notes.getProjectName(),
+ repository,
+ optionalChangeEdit.get(),
+ newEditCommit,
+ nowTimestamp);
} else {
createEdit(repository, notes, basePatchSet, newEditCommit, nowTimestamp);
}
@@ -384,7 +396,12 @@
createCommit(repository, patchSetCommit, newTreeId, commitMessage, nowTimestamp);
if (optionalChangeEdit.isPresent()) {
- return updateEdit(repository, optionalChangeEdit.get(), newEditCommit, nowTimestamp);
+ return updateEdit(
+ notes.getProjectName(),
+ repository,
+ optionalChangeEdit.get(),
+ newEditCommit,
+ nowTimestamp);
}
return createEdit(repository, notes, patchSet, newEditCommit, nowTimestamp);
}
@@ -531,7 +548,13 @@
throws IOException {
Change change = notes.getChange();
String editRefName = getEditRefName(change, basePatchSet);
- updateReference(repository, editRefName, ObjectId.zeroId(), newEditCommitId, timestamp);
+ updateReference(
+ notes.getProjectName(),
+ repository,
+ editRefName,
+ ObjectId.zeroId(),
+ newEditCommitId,
+ timestamp);
reindex(change);
RevCommit newEditCommit = lookupCommit(repository, newEditCommitId);
@@ -544,11 +567,16 @@
}
private ChangeEdit updateEdit(
- Repository repository, ChangeEdit changeEdit, ObjectId newEditCommitId, Timestamp timestamp)
+ Project.NameKey projectName,
+ Repository repository,
+ ChangeEdit changeEdit,
+ ObjectId newEditCommitId,
+ Timestamp timestamp)
throws IOException {
String editRefName = changeEdit.getRefName();
RevCommit currentEditCommit = changeEdit.getEditCommit();
- updateReference(repository, editRefName, currentEditCommit, newEditCommitId, timestamp);
+ updateReference(
+ projectName, repository, editRefName, currentEditCommit, newEditCommitId, timestamp);
reindex(changeEdit.getChange());
RevCommit newEditCommit = lookupCommit(repository, newEditCommitId);
@@ -557,6 +585,7 @@
}
private void updateReference(
+ Project.NameKey projectName,
Repository repository,
String refName,
ObjectId currentObjectId,
@@ -571,14 +600,12 @@
ru.setForceUpdate(true);
try (RevWalk revWalk = new RevWalk(repository)) {
RefUpdate.Result res = ru.update(revWalk);
+ String message = "cannot update " + ru.getName() + " in " + projectName + ": " + res;
+ if (res == RefUpdate.Result.LOCK_FAILURE) {
+ throw new LockFailureException(message, ru);
+ }
if (res != RefUpdate.Result.NEW && res != RefUpdate.Result.FORCED) {
- throw new IOException(
- "cannot update "
- + ru.getName()
- + " in "
- + repository.getDirectory()
- + ": "
- + ru.getResult());
+ throw new IOException(message);
}
}
}
diff --git a/java/com/google/gerrit/server/index/IndexUtils.java b/java/com/google/gerrit/server/index/IndexUtils.java
index 4b5cd49..a45777e 100644
--- a/java/com/google/gerrit/server/index/IndexUtils.java
+++ b/java/com/google/gerrit/server/index/IndexUtils.java
@@ -16,18 +16,21 @@
import static com.google.gerrit.server.index.change.ChangeField.CHANGE;
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
+import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID_STR;
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.project.ProjectField;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.account.AccountField;
import com.google.gerrit.server.index.group.GroupField;
+import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.SingleGroupUser;
import java.io.IOException;
import java.util.Set;
@@ -56,17 +59,18 @@
}
}
- public static Set<String> accountFields(QueryOptions opts) {
- return accountFields(opts.fields());
+ public static Set<String> accountFields(QueryOptions opts, boolean useLegacyNumericFields) {
+ return accountFields(opts.fields(), useLegacyNumericFields);
}
- public static Set<String> accountFields(Set<String> fields) {
- return fields.contains(AccountField.ID.getName())
- ? fields
- : Sets.union(fields, ImmutableSet.of(AccountField.ID.getName()));
+ public static Set<String> accountFields(Set<String> fields, boolean useLegacyNumericFields) {
+ String idFieldName =
+ useLegacyNumericFields ? AccountField.ID.getName() : AccountField.ID_STR.getName();
+ return fields.contains(idFieldName) ? fields : Sets.union(fields, ImmutableSet.of(idFieldName));
}
- public static Set<String> changeFields(QueryOptions opts) {
+ public static Set<String> changeFields(QueryOptions opts, boolean useLegacyNumericFields) {
+ FieldDef<ChangeData, ?> idField = useLegacyNumericFields ? LEGACY_ID : LEGACY_ID_STR;
// Ensure we request enough fields to construct a ChangeData. We need both
// change ID and project, which can either come via the Change field or
// separate fields.
@@ -75,10 +79,10 @@
// A Change is always sufficient.
return fs;
}
- if (fs.contains(PROJECT.getName()) && fs.contains(LEGACY_ID.getName())) {
+ if (fs.contains(PROJECT.getName()) && fs.contains(idField.getName())) {
return fs;
}
- return Sets.union(fs, ImmutableSet.of(LEGACY_ID.getName(), PROJECT.getName()));
+ return Sets.union(fs, ImmutableSet.of(idField.getName(), PROJECT.getName()));
}
public static Set<String> groupFields(QueryOptions opts) {
diff --git a/java/com/google/gerrit/server/index/account/AccountField.java b/java/com/google/gerrit/server/index/account/AccountField.java
index ae94283..0dd22ce 100644
--- a/java/com/google/gerrit/server/index/account/AccountField.java
+++ b/java/com/google/gerrit/server/index/account/AccountField.java
@@ -46,6 +46,9 @@
public static final FieldDef<AccountState, Integer> ID =
integer("id").stored().build(a -> a.account().id().get());
+ public static final FieldDef<AccountState, String> ID_STR =
+ exact("id_str").stored().build(a -> String.valueOf(a.account().id().get()));
+
/**
* External IDs.
*
diff --git a/java/com/google/gerrit/server/index/account/AccountIndex.java b/java/com/google/gerrit/server/index/account/AccountIndex.java
index 153bf17..1838edf 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndex.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndex.java
@@ -27,6 +27,6 @@
@Override
default Predicate<AccountState> keyPredicate(Account.Id id) {
- return AccountPredicates.id(id);
+ return AccountPredicates.id(getSchema(), id);
}
}
diff --git a/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java b/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java
index c41814f..157e290 100644
--- a/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java
@@ -49,7 +49,18 @@
@Deprecated static final Schema<AccountState> V9 = schema(V8);
// Lucene index was changed to add additional fields for sorting.
- static final Schema<AccountState> V10 = schema(V9);
+ @Deprecated static final Schema<AccountState> V10 = schema(V9);
+
+ // New numeric types: use dimensional points using the k-d tree geo-spatial data structure
+ // to offer fast single- and multi-dimensional numeric range. As the consequense, integer
+ // document id type is replaced with string document id type.
+ static final Schema<AccountState> V11 =
+ new Schema.Builder<AccountState>()
+ .add(V10)
+ .remove(AccountField.ID)
+ .add(AccountField.ID_STR)
+ .legacyNumericFields(false)
+ .build();
public static final String NAME = "accounts";
public static final AccountSchemaDefinitions INSTANCE = new AccountSchemaDefinitions();
diff --git a/java/com/google/gerrit/server/index/account/StalenessChecker.java b/java/com/google/gerrit/server/index/account/StalenessChecker.java
index fc86c8d..aad9527 100644
--- a/java/com/google/gerrit/server/index/account/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/account/StalenessChecker.java
@@ -61,6 +61,12 @@
AccountField.REF_STATE.getName(),
AccountField.EXTERNAL_ID_STATE.getName());
+ public static final ImmutableSet<String> FIELDS2 =
+ ImmutableSet.of(
+ AccountField.ID_STR.getName(),
+ AccountField.REF_STATE.getName(),
+ AccountField.EXTERNAL_ID_STATE.getName());
+
private final AccountIndexCollection indexes;
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
@@ -93,8 +99,13 @@
return false;
}
+ boolean useLegacyNumericFields = i.getSchema().useLegacyNumericFields();
+ ImmutableSet<String> fields = useLegacyNumericFields ? FIELDS : FIELDS2;
Optional<FieldBundle> result =
- i.getRaw(id, QueryOptions.create(indexConfig, 0, 1, IndexUtils.accountFields(FIELDS)));
+ i.getRaw(
+ id,
+ QueryOptions.create(
+ indexConfig, 0, 1, IndexUtils.accountFields(fields, useLegacyNumericFields)));
if (!result.isPresent()) {
// The document is missing in the index.
try (Repository repo = repoManager.openRepository(allUsersName)) {
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index c62b4df..07e9b9e4 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -107,6 +107,9 @@
public static final FieldDef<ChangeData, Integer> LEGACY_ID =
integer("legacy_id").stored().build(cd -> cd.getId().get());
+ public static final FieldDef<ChangeData, String> LEGACY_ID_STR =
+ exact("legacy_id_str").stored().build(cd -> String.valueOf(cd.getId().get()));
+
/** Newer style Change-Id key. */
public static final FieldDef<ChangeData, String> ID =
prefix(ChangeQueryBuilder.FIELD_CHANGE_ID).build(changeGetter(c -> c.getKey().get()));
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndex.java b/java/com/google/gerrit/server/index/change/ChangeIndex.java
index 1a01417..29bff0a 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndex.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndex.java
@@ -20,6 +20,7 @@
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.LegacyChangeIdPredicate;
+import com.google.gerrit.server.query.change.LegacyChangeIdStrPredicate;
public interface ChangeIndex extends Index<Change.Id, ChangeData> {
public interface Factory
@@ -27,6 +28,8 @@
@Override
default Predicate<ChangeData> keyPredicate(Change.Id id) {
- return new LegacyChangeIdPredicate(id);
+ return getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(id)
+ : new LegacyChangeIdStrPredicate(id);
}
}
diff --git a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
index bf93edb..72153c4 100644
--- a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
@@ -85,7 +85,18 @@
ChangeField.WIP);
// The computation of the 'extension' field is changed, hence reindexing is required.
- static final Schema<ChangeData> V56 = schema(V55);
+ @Deprecated static final Schema<ChangeData> V56 = schema(V55);
+
+ // New numeric types: use dimensional points using the k-d tree geo-spatial data structure
+ // to offer fast single- and multi-dimensional numeric range. As the consequense, integer
+ // document id type is replaced with string document id type.
+ static final Schema<ChangeData> V57 =
+ new Schema.Builder<ChangeData>()
+ .add(V56)
+ .remove(ChangeField.LEGACY_ID)
+ .add(ChangeField.LEGACY_ID_STR)
+ .legacyNumericFields(false)
+ .build();
public static final String NAME = "changes";
public static final ChangeSchemaDefinitions INSTANCE = new ChangeSchemaDefinitions();
diff --git a/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java b/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java
index 6d0f3b6..e64e2eb 100644
--- a/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java
@@ -43,7 +43,11 @@
@Deprecated static final Schema<InternalGroup> V6 = schema(V5);
// Lucene index was changed to add an additional field for sorting.
- static final Schema<InternalGroup> V7 = schema(V6);
+ @Deprecated static final Schema<InternalGroup> V7 = schema(V6);
+
+ // New numeric types: use dimensional points using the k-d tree geo-spatial data structure
+ // to offer fast single- and multi-dimensional numeric range.
+ static final Schema<InternalGroup> V8 = schema(V7, false);
public static final GroupSchemaDefinitions INSTANCE = new GroupSchemaDefinitions();
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 81d5381..158db1c 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -24,7 +24,6 @@
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment.Status;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
@@ -335,7 +334,7 @@
persistentCommentFromMailComment(ctx, c, targetPatchSetForComment(ctx, c, patchSet)));
}
commentsUtil.putComments(
- ctx.getUpdate(ctx.getChange().currentPatchSetId()), Status.PUBLISHED, comments);
+ ctx.getUpdate(ctx.getChange().currentPatchSetId()), Comment.Status.PUBLISHED, comments);
return true;
}
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index dbd1abb..0acf20e 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -50,7 +50,6 @@
public final ChangeNoteJson changeNoteJson;
public final GitRepositoryManager repoManager;
public final AllUsersName allUsers;
- public final LegacyChangeNoteRead legacyChangeNoteRead;
public final NoteDbMetrics metrics;
public final String serverId;
@@ -64,14 +63,12 @@
GitRepositoryManager repoManager,
AllUsersName allUsers,
ChangeNoteJson changeNoteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
NoteDbMetrics metrics,
Provider<ChangeNotesCache> cache,
@GerritServerId String serverId) {
this.failOnLoadForTest = new AtomicBoolean();
this.repoManager = repoManager;
this.allUsers = allUsers;
- this.legacyChangeNoteRead = legacyChangeNoteRead;
this.changeNoteJson = changeNoteJson;
this.metrics = metrics;
this.cache = cache;
diff --git a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
index d0819d7..877022e 100644
--- a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
@@ -23,7 +23,6 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
@@ -265,12 +264,7 @@
// Even though reading from changes might not be enabled, we need to
// parse any existing revision notes so we can merge them.
return RevisionNoteMap.parse(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- getId(),
- rw.getObjectReader(),
- noteMap,
- PatchLineComment.Status.DRAFT);
+ noteUtil.getChangeNoteJson(), rw.getObjectReader(), noteMap, Comment.Status.DRAFT);
}
@Override
diff --git a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
index 7aa3bc8..b221ef5 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
@@ -63,22 +63,13 @@
static final String UNRESOLVED = "Unresolved";
static final String TAG = FOOTER_TAG.getName();
- private final LegacyChangeNoteRead legacyChangeNoteRead;
private final ChangeNoteJson changeNoteJson;
private final String serverId;
@Inject
- public ChangeNoteUtil(
- ChangeNoteJson changeNoteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
- @GerritServerId String serverId) {
+ public ChangeNoteUtil(ChangeNoteJson changeNoteJson, @GerritServerId String serverId) {
this.serverId = serverId;
this.changeNoteJson = changeNoteJson;
- this.legacyChangeNoteRead = legacyChangeNoteRead;
- }
-
- public LegacyChangeNoteRead getLegacyChangeNoteRead() {
- return legacyChangeNoteRead;
}
public ChangeNoteJson getChangeNoteJson() {
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index 384eebc..8cf0046 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -16,6 +16,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.gerrit.entities.RefNames.changeMetaRef;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
@@ -29,6 +30,7 @@
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
@@ -48,6 +50,7 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
@@ -366,9 +369,24 @@
return state.reviewerUpdates();
}
- /** @return an ImmutableSet of Account.Ids of all users that have been assigned to this change. */
+ /**
+ * @return an ImmutableSet of Account.Ids of all users that have been assigned to this change. The
+ * order of the set is the order in which they were assigned.
+ */
public ImmutableSet<Account.Id> getPastAssignees() {
- return state.pastAssignees();
+ return Lists.reverse(state.assigneeUpdates()).stream()
+ .map(AssigneeStatusUpdate::currentAssignee)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * @return an ImmutableList of AssigneeStatusUpdate of all the updates to the assignee field to
+ * this change. The order of the list is from most recent updates to least recent.
+ */
+ public ImmutableList<AssigneeStatusUpdate> getAssigneeUpdates() {
+ return state.assigneeUpdates();
}
/** @return a ImmutableSet of all hashtags for this change sorted in alphabetical order. */
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesCache.java b/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
index 03073d2..7fde297 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
@@ -61,7 +61,7 @@
.weigher(Weigher.class)
.maximumWeight(10 << 20)
.diskLimit(-1)
- .version(1)
+ .version(2)
.keySerializer(Key.Serializer.INSTANCE)
.valueSerializer(ChangeNotesState.Serializer.INSTANCE);
}
@@ -148,11 +148,8 @@
+ str(state.columns().originalSubject())
+ P
+ str(state.columns().submissionId())
- + ptr(state.columns().assignee(), K) // assignee
+ P // status
+ P
- + set(state.pastAssignees(), K)
- + P
+ set(state.hashtags(), str(10))
+ P
+ list(state.patchSets(), patchSet())
@@ -169,6 +166,8 @@
+ P
+ list(state.reviewerUpdates(), 4 * O + K + K + P)
+ P
+ + list(state.assigneeUpdates(), 4 * O + K + K)
+ + P
+ list(state.submitRecords(), P + list(2, str(4) + P + K) + P)
+ P
+ list(state.changeMessages(), changeMessage())
@@ -180,10 +179,6 @@
+ I; // updateCount
}
- private static int ptr(Object o, int size) {
- return o != null ? P + size : P;
- }
-
private static int str(String s) {
if (s == null) {
return P;
@@ -355,12 +350,7 @@
"Load change notes for change %s of project %s", key.changeId(), key.project());
ChangeNotesParser parser =
new ChangeNotesParser(
- key.changeId(),
- key.id(),
- walkSupplier.get(),
- args.changeNoteJson,
- args.legacyChangeNoteRead,
- args.metrics);
+ key.changeId(), key.id(), walkSupplier.get(), args.changeNoteJson, args.metrics);
ChangeNotesState result = parser.parseAll();
// This assignment only happens if call() was actually called, which only
// happens when Cache#get(K, Callable<V>) incurs a cache miss.
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index c4c227e..9c45aaf 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -61,12 +61,12 @@
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.Comment;
import com.google.gerrit.entities.LabelId;
-import com.google.gerrit.entities.PatchLineComment;
import com.google.gerrit.entities.PatchSet;
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.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
@@ -102,7 +102,6 @@
// Private final members initialized in the constructor.
private final ChangeNoteJson changeNoteJson;
- private final LegacyChangeNoteRead legacyChangeNoteRead;
private final NoteDbMetrics metrics;
private final Change.Id id;
@@ -115,6 +114,7 @@
private final Table<Address, ReviewerStateInternal, Timestamp> reviewersByEmail;
private final List<Account.Id> allPastReviewers;
private final List<ReviewerStatusUpdate> reviewerUpdates;
+ private final List<AssigneeStatusUpdate> assigneeUpdates;
private final List<SubmitRecord> submitRecords;
private final ListMultimap<ObjectId, Comment> comments;
private final Map<PatchSet.Id, PatchSet.Builder> patchSets;
@@ -129,8 +129,6 @@
private String branch;
private Change.Status status;
private String topic;
- private Optional<Account.Id> assignee;
- private List<Account.Id> pastAssignees;
private Set<String> hashtags;
private Timestamp createdOn;
private Timestamp lastUpdatedOn;
@@ -156,13 +154,11 @@
ObjectId tip,
ChangeNotesRevWalk walk,
ChangeNoteJson changeNoteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
NoteDbMetrics metrics) {
this.id = changeId;
this.tip = tip;
this.walk = walk;
this.changeNoteJson = changeNoteJson;
- this.legacyChangeNoteRead = legacyChangeNoteRead;
this.metrics = metrics;
approvals = new LinkedHashMap<>();
bufferedApprovals = new ArrayList<>();
@@ -172,6 +168,7 @@
pendingReviewersByEmail = ReviewerByEmailSet.empty();
allPastReviewers = new ArrayList<>();
reviewerUpdates = new ArrayList<>();
+ assigneeUpdates = new ArrayList<>();
submitRecords = Lists.newArrayListWithExpectedSize(1);
allChangeMessages = new ArrayList<>();
comments = MultimapBuilder.hashKeys().arrayListValues().build();
@@ -231,9 +228,7 @@
topic,
originalSubject,
submissionId,
- assignee != null ? assignee.orElse(null) : null,
status,
- Sets.newLinkedHashSet(Lists.reverse(pastAssignees)),
firstNonNull(hashtags, ImmutableSet.of()),
buildPatchSets(),
buildApprovals(),
@@ -243,6 +238,7 @@
pendingReviewersByEmail,
allPastReviewers,
buildReviewerUpdates(),
+ assigneeUpdates,
submitRecords,
buildAllMessages(),
comments,
@@ -361,7 +357,7 @@
}
parseHashtags(commit);
- parseAssignee(commit);
+ parseAssigneeUpdates(ts, commit);
if (submissionId == null) {
submissionId = parseSubmissionId(commit);
@@ -444,7 +440,7 @@
return effectiveAccountId;
}
PersonIdent ident = RawParseUtils.parsePersonIdent(realUser);
- return legacyChangeNoteRead.parseIdent(ident, id);
+ return parseIdent(ident);
}
private String parseTopic(ChangeNotesCommit commit) throws ConfigInvalidException {
@@ -566,10 +562,8 @@
}
}
- private void parseAssignee(ChangeNotesCommit commit) throws ConfigInvalidException {
- if (pastAssignees == null) {
- pastAssignees = Lists.newArrayList();
- }
+ private void parseAssigneeUpdates(Timestamp ts, ChangeNotesCommit commit)
+ throws ConfigInvalidException {
String assigneeValue = parseOneFooter(commit, FOOTER_ASSIGNEE);
if (assigneeValue != null) {
Optional<Account.Id> parsedAssignee;
@@ -578,14 +572,9 @@
parsedAssignee = Optional.empty();
} else {
PersonIdent ident = RawParseUtils.parsePersonIdent(assigneeValue);
- parsedAssignee = Optional.ofNullable(legacyChangeNoteRead.parseIdent(ident, id));
+ parsedAssignee = Optional.ofNullable(parseIdent(ident));
}
- if (assignee == null) {
- assignee = parsedAssignee;
- }
- if (parsedAssignee.isPresent()) {
- pastAssignees.add(parsedAssignee.get());
- }
+ assigneeUpdates.add(AssigneeStatusUpdate.create(ts, ownerId, parsedAssignee));
}
}
@@ -716,12 +705,7 @@
ChangeNotesCommit tipCommit = walk.parseCommit(tip);
revisionNoteMap =
RevisionNoteMap.parse(
- changeNoteJson,
- legacyChangeNoteRead,
- id,
- reader,
- NoteMap.read(reader, tipCommit),
- PatchLineComment.Status.PUBLISHED);
+ changeNoteJson, reader, NoteMap.read(reader, tipCommit), Comment.Status.PUBLISHED);
Map<ObjectId, ChangeRevisionNote> rns = revisionNoteMap.revisionNotes;
for (Map.Entry<ObjectId, ChangeRevisionNote> e : rns.entrySet()) {
@@ -780,7 +764,7 @@
labelVoteStr = line.substring(0, s);
PersonIdent ident = RawParseUtils.parsePersonIdent(line.substring(s + 1));
checkFooter(ident != null, FOOTER_LABEL, line);
- effectiveAccountId = legacyChangeNoteRead.parseIdent(ident, id);
+ effectiveAccountId = parseIdent(ident);
} else {
labelVoteStr = line;
effectiveAccountId = committerId;
@@ -819,7 +803,7 @@
label = line.substring(1, s);
PersonIdent ident = RawParseUtils.parsePersonIdent(line.substring(s + 1));
checkFooter(ident != null, FOOTER_LABEL, line);
- effectiveAccountId = legacyChangeNoteRead.parseIdent(ident, id);
+ effectiveAccountId = parseIdent(ident);
} else {
label = line.substring(1);
effectiveAccountId = committerId;
@@ -878,7 +862,7 @@
label.label = line.substring(c + 2, c2);
PersonIdent ident = RawParseUtils.parsePersonIdent(line.substring(c2 + 2));
checkFooter(ident != null, FOOTER_SUBMITTED_WITH, line);
- label.appliedBy = legacyChangeNoteRead.parseIdent(ident, id);
+ label.appliedBy = parseIdent(ident);
} else {
label.label = line.substring(c + 2);
}
@@ -894,7 +878,7 @@
if (a.getName().equals(c.getName()) && a.getEmailAddress().equals(c.getEmailAddress())) {
return null;
}
- return legacyChangeNoteRead.parseIdent(commit.getAuthorIdent(), id);
+ return parseIdent(commit.getAuthorIdent());
}
private void parseReviewer(Timestamp ts, ReviewerStateInternal state, String line)
@@ -903,7 +887,7 @@
if (ident == null) {
throw invalidFooter(state.getFooterKey(), line);
}
- Account.Id accountId = legacyChangeNoteRead.parseIdent(ident, id);
+ Account.Id accountId = parseIdent(ident);
reviewerUpdates.add(ReviewerStatusUpdate.create(ts, ownerId, accountId, state));
if (!reviewers.containsRow(accountId)) {
reviewers.put(accountId, state, ts);
@@ -1103,4 +1087,10 @@
private ConfigInvalidException parseException(String fmt, Object... args) {
return ChangeNotes.parseException(id, fmt, args);
}
+
+ private Account.Id parseIdent(PersonIdent ident) throws ConfigInvalidException {
+ return NoteDbUtil.parseIdent(ident)
+ .orElseThrow(
+ () -> parseException("cannot retrieve account id: %s", ident.getEmailAddress()));
+ }
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index 5396508..896cca3 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -18,7 +18,6 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
@@ -50,10 +49,12 @@
import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.mail.Address;
import com.google.gerrit.proto.Protos;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
+import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.AssigneeStatusUpdateProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ChangeColumnsProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
@@ -67,6 +68,7 @@
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
@@ -103,9 +105,7 @@
@Nullable String topic,
@Nullable String originalSubject,
@Nullable String submissionId,
- @Nullable Account.Id assignee,
@Nullable Change.Status status,
- Set<Account.Id> pastAssignees,
Set<String> hashtags,
Map<PatchSet.Id, PatchSet> patchSets,
ListMultimap<PatchSet.Id, PatchSetApproval> approvals,
@@ -115,6 +115,7 @@
ReviewerByEmailSet pendingReviewersByEmail,
List<Account.Id> allPastReviewers,
List<ReviewerStatusUpdate> reviewerUpdates,
+ List<AssigneeStatusUpdate> assigneeUpdates,
List<SubmitRecord> submitRecords,
List<ChangeMessage> changeMessages,
ListMultimap<ObjectId, Comment> publishedComments,
@@ -147,13 +148,11 @@
.topic(topic)
.originalSubject(originalSubject)
.submissionId(submissionId)
- .assignee(assignee)
.isPrivate(isPrivate)
.workInProgress(workInProgress)
.reviewStarted(reviewStarted)
.revertOf(revertOf)
.build())
- .pastAssignees(pastAssignees)
.hashtags(hashtags)
.serverId(serverId)
.patchSets(patchSets.entrySet())
@@ -164,6 +163,7 @@
.pendingReviewersByEmail(pendingReviewersByEmail)
.allPastReviewers(allPastReviewers)
.reviewerUpdates(reviewerUpdates)
+ .assigneeUpdates(assigneeUpdates)
.submitRecords(submitRecords)
.changeMessages(changeMessages)
.publishedComments(publishedComments)
@@ -211,9 +211,6 @@
@Nullable
abstract String submissionId();
- @Nullable
- abstract Account.Id assignee();
-
abstract boolean isPrivate();
abstract boolean workInProgress();
@@ -247,8 +244,6 @@
abstract Builder submissionId(@Nullable String submissionId);
- abstract Builder assignee(@Nullable Account.Id assignee);
-
abstract Builder status(@Nullable Change.Status status);
abstract Builder isPrivate(boolean isPrivate);
@@ -274,8 +269,6 @@
abstract ChangeColumns columns();
// Other related to this Change.
- abstract ImmutableSet<Account.Id> pastAssignees();
-
abstract ImmutableSet<String> hashtags();
@Nullable
@@ -297,6 +290,8 @@
abstract ImmutableList<ReviewerStatusUpdate> reviewerUpdates();
+ abstract ImmutableList<AssigneeStatusUpdate> assigneeUpdates();
+
abstract ImmutableList<SubmitRecord> submitRecords();
abstract ImmutableList<ChangeMessage> changeMessages();
@@ -339,7 +334,9 @@
change.setTopic(Strings.emptyToNull(c.topic()));
change.setLastUpdatedOn(c.lastUpdatedOn());
change.setSubmissionId(c.submissionId());
- change.setAssignee(c.assignee());
+ if (!assigneeUpdates().isEmpty()) {
+ change.setAssignee(assigneeUpdates().get(0).currentAssignee().orElse(null));
+ }
change.setPrivate(c.isPrivate());
change.setWorkInProgress(c.workInProgress());
change.setReviewStarted(c.reviewStarted());
@@ -359,7 +356,6 @@
static Builder empty(Change.Id changeId) {
return new AutoValue_ChangeNotesState.Builder()
.changeId(changeId)
- .pastAssignees(ImmutableSet.of())
.hashtags(ImmutableSet.of())
.patchSets(ImmutableList.of())
.approvals(ImmutableList.of())
@@ -369,6 +365,7 @@
.pendingReviewersByEmail(ReviewerByEmailSet.empty())
.allPastReviewers(ImmutableList.of())
.reviewerUpdates(ImmutableList.of())
+ .assigneeUpdates(ImmutableList.of())
.submitRecords(ImmutableList.of())
.changeMessages(ImmutableList.of())
.publishedComments(ImmutableListMultimap.of())
@@ -383,8 +380,6 @@
abstract Builder serverId(String serverId);
- abstract Builder pastAssignees(Set<Account.Id> pastAssignees);
-
abstract Builder hashtags(Iterable<String> hashtags);
abstract Builder patchSets(Iterable<Map.Entry<PatchSet.Id, PatchSet>> patchSets);
@@ -403,6 +398,8 @@
abstract Builder reviewerUpdates(List<ReviewerStatusUpdate> reviewerUpdates);
+ abstract Builder assigneeUpdates(List<AssigneeStatusUpdate> assigneeUpdates);
+
abstract Builder submitRecords(List<SubmitRecord> submitRecords);
abstract Builder changeMessages(List<ChangeMessage> changeMessages);
@@ -438,7 +435,6 @@
b.setServerId(object.serverId());
b.setHasServerId(true);
}
- object.pastAssignees().forEach(a -> b.addPastAssignee(a.get()));
object.hashtags().forEach(b::addHashtag);
object
.patchSets()
@@ -469,6 +465,7 @@
object.allPastReviewers().forEach(a -> b.addPastReviewer(a.get()));
object.reviewerUpdates().forEach(u -> b.addReviewerUpdate(toReviewerStatusUpdateProto(u)));
+ object.assigneeUpdates().forEach(u -> b.addAssigneeUpdate(toAssigneeStatusUpdateProto(u)));
object
.submitRecords()
.forEach(r -> b.addSubmitRecord(GSON.toJson(new StoredSubmitRecord(r))));
@@ -508,9 +505,6 @@
if (cols.submissionId() != null) {
b.setSubmissionId(cols.submissionId()).setHasSubmissionId(true);
}
- if (cols.assignee() != null) {
- b.setAssignee(cols.assignee().get()).setHasAssignee(true);
- }
if (cols.status() != null) {
b.setStatus(STATUS_CONVERTER.reverse().convert(cols.status())).setHasStatus(true);
}
@@ -550,6 +544,17 @@
.build();
}
+ private static AssigneeStatusUpdateProto toAssigneeStatusUpdateProto(AssigneeStatusUpdate u) {
+ AssigneeStatusUpdateProto.Builder builder =
+ AssigneeStatusUpdateProto.newBuilder()
+ .setDate(u.date().getTime())
+ .setUpdatedBy(u.updatedBy().get())
+ .setHasCurrentAssignee(u.currentAssignee().isPresent());
+
+ u.currentAssignee().ifPresent(assignee -> builder.setCurrentAssignee(assignee.get()));
+ return builder.build();
+ }
+
@Override
public ChangeNotesState deserialize(byte[] in) {
ChangeNotesStateProto proto = Protos.parseUnchecked(ChangeNotesStateProto.parser(), in);
@@ -561,8 +566,6 @@
.changeId(changeId)
.columns(toChangeColumns(changeId, proto.getColumns()))
.serverId(proto.getHasServerId() ? proto.getServerId() : null)
- .pastAssignees(
- proto.getPastAssigneeList().stream().map(Account::id).collect(toImmutableSet()))
.hashtags(proto.getHashtagList())
.patchSets(
proto.getPatchSetList().stream()
@@ -581,6 +584,7 @@
.allPastReviewers(
proto.getPastReviewerList().stream().map(Account::id).collect(toImmutableList()))
.reviewerUpdates(toReviewerStatusUpdateList(proto.getReviewerUpdateList()))
+ .assigneeUpdates(toAssigneeStatusUpdateList(proto.getAssigneeUpdateList()))
.submitRecords(
proto.getSubmitRecordList().stream()
.map(r -> GSON.fromJson(r, StoredSubmitRecord.class).toSubmitRecord())
@@ -624,9 +628,6 @@
if (proto.getHasSubmissionId()) {
b.submissionId(proto.getSubmissionId());
}
- if (proto.getHasAssignee()) {
- b.assignee(Account.id(proto.getAssignee()));
- }
if (proto.getHasStatus()) {
b.status(STATUS_CONVERTER.convert(proto.getStatus()));
}
@@ -677,5 +678,20 @@
}
return b.build();
}
+
+ private static ImmutableList<AssigneeStatusUpdate> toAssigneeStatusUpdateList(
+ List<AssigneeStatusUpdateProto> protos) {
+ ImmutableList.Builder<AssigneeStatusUpdate> b = ImmutableList.builder();
+ for (AssigneeStatusUpdateProto proto : protos) {
+ b.add(
+ AssigneeStatusUpdate.create(
+ new Timestamp(proto.getDate()),
+ Account.id(proto.getUpdatedBy()),
+ proto.getHasCurrentAssignee()
+ ? Optional.of(Account.id(proto.getCurrentAssignee()))
+ : Optional.empty()));
+ }
+ return b.build();
+ }
}
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java b/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
index a9f2357..b6443f1 100644
--- a/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
+++ b/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
@@ -16,10 +16,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.common.primitives.Bytes;
-import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -30,30 +27,16 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.util.MutableInteger;
-import org.eclipse.jgit.util.RawParseUtils;
class ChangeRevisionNote extends RevisionNote<Comment> {
- private static final byte[] CERT_HEADER = "certificate version ".getBytes(UTF_8);
- // See org.eclipse.jgit.transport.PushCertificateParser.END_SIGNATURE
- private static final byte[] END_SIGNATURE = "-----END PGP SIGNATURE-----\n".getBytes(UTF_8);
-
private final ChangeNoteJson noteJson;
- private final LegacyChangeNoteRead legacyChangeNoteRead;
- private final Change.Id changeId;
- private final PatchLineComment.Status status;
+ private final Comment.Status status;
private String pushCert;
ChangeRevisionNote(
- ChangeNoteJson noteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
- Change.Id changeId,
- ObjectReader reader,
- ObjectId noteId,
- PatchLineComment.Status status) {
+ ChangeNoteJson noteJson, ObjectReader reader, ObjectId noteId, Comment.Status status) {
super(reader, noteId);
- this.legacyChangeNoteRead = legacyChangeNoteRead;
this.noteJson = noteJson;
- this.changeId = changeId;
this.status = status;
}
@@ -67,29 +50,13 @@
MutableInteger p = new MutableInteger();
p.value = offset;
- if (isJson(raw, p.value)) {
- RevisionNoteData data = parseJson(noteJson, raw, p.value);
- if (status == PatchLineComment.Status.PUBLISHED) {
- pushCert = data.pushCert;
- } else {
- pushCert = null;
- }
- return data.comments;
- }
-
- if (status == PatchLineComment.Status.PUBLISHED) {
- pushCert = parsePushCert(changeId, raw, p);
- trimLeadingEmptyLines(raw, p);
+ RevisionNoteData data = parseJson(noteJson, raw, p.value);
+ if (status == Comment.Status.PUBLISHED) {
+ pushCert = data.pushCert;
} else {
pushCert = null;
}
- List<Comment> comments = legacyChangeNoteRead.parseNote(raw, p, changeId);
- comments.forEach(c -> c.legacyFormat = true);
- return comments;
- }
-
- static boolean isJson(byte[] raw, int offset) {
- return raw[offset] == '{' || raw[offset] == '[';
+ return data.comments;
}
private RevisionNoteData parseJson(ChangeNoteJson noteUtil, byte[] raw, int offset)
@@ -99,18 +66,4 @@
return noteUtil.getGson().fromJson(r, RevisionNoteData.class);
}
}
-
- private static String parsePushCert(Change.Id changeId, byte[] bytes, MutableInteger p)
- throws ConfigInvalidException {
- if (RawParseUtils.match(bytes, p.value, CERT_HEADER) < 0) {
- return null;
- }
- int end = Bytes.indexOf(bytes, END_SIGNATURE);
- if (end < 0) {
- throw ChangeNotes.parseException(changeId, "invalid push certificate in note");
- }
- int start = p.value;
- p.value = end + END_SIGNATURE.length;
- return new String(bytes, start, p.value, UTF_8);
- }
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index c51f7fb..c0cd173 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -53,7 +53,6 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.StorageException;
@@ -273,10 +272,10 @@
this.psDescription = psDescription;
}
- public void putComment(PatchLineComment.Status status, Comment c) {
+ public void putComment(Comment.Status status, Comment c) {
verifyComment(c);
createDraftUpdateIfNull();
- if (status == PatchLineComment.Status.DRAFT) {
+ if (status == Comment.Status.DRAFT) {
draftUpdate.putComment(c);
} else {
comments.add(c);
@@ -462,12 +461,7 @@
// Even though reading from changes might not be enabled, we need to
// parse any existing revision notes so we can merge them.
return RevisionNoteMap.parse(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- getId(),
- rw.getObjectReader(),
- noteMap,
- PatchLineComment.Status.PUBLISHED);
+ noteUtil.getChangeNoteJson(), rw.getObjectReader(), noteMap, Comment.Status.PUBLISHED);
}
private void checkComments(
diff --git a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
index 4ec4f20..9c8b369 100644
--- a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
+++ b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.entities.PatchLineComment.Status.PUBLISHED;
+import static com.google.gerrit.entities.Comment.Status;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -95,13 +95,13 @@
ObjectReader reader = revWalk.getObjectReader();
RevCommit newTipCommit = revWalk.next(); // The first commit will not be rewritten.
Map<String, Comment> parentComments =
- getPublishedComments(noteUtil, changeId, reader, NoteMap.read(reader, newTipCommit));
+ getPublishedComments(noteUtil, reader, NoteMap.read(reader, newTipCommit));
boolean rewrite = false;
RevCommit originalCommit;
while ((originalCommit = revWalk.next()) != null) {
NoteMap noteMap = NoteMap.read(reader, originalCommit);
- Map<String, Comment> currComments = getPublishedComments(noteUtil, changeId, reader, noteMap);
+ Map<String, Comment> currComments = getPublishedComments(noteUtil, reader, noteMap);
if (!rewrite && currComments.containsKey(uuid)) {
rewrite = true;
@@ -131,28 +131,18 @@
*/
@VisibleForTesting
public static Map<String, Comment> getPublishedComments(
- ChangeNoteJson changeNoteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
- Change.Id changeId,
- ObjectReader reader,
- NoteMap noteMap)
+ ChangeNoteJson changeNoteJson, ObjectReader reader, NoteMap noteMap)
throws IOException, ConfigInvalidException {
- return RevisionNoteMap.parse(
- changeNoteJson, legacyChangeNoteRead, changeId, reader, noteMap, PUBLISHED)
- .revisionNotes.values().stream()
+ return RevisionNoteMap.parse(changeNoteJson, reader, noteMap, Status.PUBLISHED).revisionNotes
+ .values().stream()
.flatMap(n -> n.getEntities().stream())
.collect(toMap(c -> c.key.uuid, Function.identity()));
}
public static Map<String, Comment> getPublishedComments(
- ChangeNoteUtil noteUtil, Change.Id changeId, ObjectReader reader, NoteMap noteMap)
+ ChangeNoteUtil noteUtil, ObjectReader reader, NoteMap noteMap)
throws IOException, ConfigInvalidException {
- return getPublishedComments(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- changeId,
- reader,
- noteMap);
+ return getPublishedComments(noteUtil.getChangeNoteJson(), reader, noteMap);
}
/**
* Gets the comments put in by the current commit. The message of the target comment will be
@@ -215,11 +205,9 @@
RevisionNoteMap<ChangeRevisionNote> revNotesMap =
RevisionNoteMap.parse(
noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- changeId,
reader,
NoteMap.read(reader, parentCommit),
- PUBLISHED);
+ Status.PUBLISHED);
RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(revNotesMap);
for (Comment c : putInComments) {
diff --git a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index e473768..3966396 100644
--- a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -27,7 +27,6 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment;
import com.google.gerrit.entities.Project;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -121,12 +120,7 @@
ObjectReader reader = handle.walk().getObjectReader();
revisionNoteMap =
RevisionNoteMap.parse(
- args.changeNoteJson,
- args.legacyChangeNoteRead,
- getChangeId(),
- reader,
- NoteMap.read(reader, tipCommit),
- PatchLineComment.Status.DRAFT);
+ args.changeNoteJson, reader, NoteMap.read(reader, tipCommit), Comment.Status.DRAFT);
ListMultimap<ObjectId, Comment> cs = MultimapBuilder.hashKeys().arrayListValues().build();
for (ChangeRevisionNote rn : revisionNoteMap.revisionNotes.values()) {
for (Comment c : rn.getEntities()) {
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java
deleted file mode 100644
index e663149..0000000
--- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java
+++ /dev/null
@@ -1,399 +0,0 @@
-// Copyright (C) 2018 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;
-
-import static com.google.gerrit.server.notedb.ChangeNotes.parseException;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.entities.Account;
-import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.CommentRange;
-import com.google.gerrit.entities.PatchSet;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.inject.Inject;
-import java.sql.Timestamp;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.util.GitDateParser;
-import org.eclipse.jgit.util.MutableInteger;
-import org.eclipse.jgit.util.QuotedString;
-import org.eclipse.jgit.util.RawParseUtils;
-
-public class LegacyChangeNoteRead {
- private final String serverId;
-
- @Inject
- public LegacyChangeNoteRead(@GerritServerId String serverId) {
- this.serverId = serverId;
- }
-
- public Account.Id parseIdent(PersonIdent ident, Change.Id changeId)
- throws ConfigInvalidException {
- return NoteDbUtil.parseIdent(ident)
- .orElseThrow(
- () ->
- parseException(
- changeId, "cannot retrieve account id: %s", ident.getEmailAddress()));
- }
-
- private static boolean match(byte[] note, MutableInteger p, byte[] expected) {
- int m = RawParseUtils.match(note, p.value, expected);
- return m == p.value + expected.length;
- }
-
- public List<Comment> parseNote(byte[] note, MutableInteger p, Change.Id changeId)
- throws ConfigInvalidException {
- if (p.value >= note.length) {
- return ImmutableList.of();
- }
- Set<Comment.Key> seen = new HashSet<>();
- List<Comment> result = new ArrayList<>();
- int sizeOfNote = note.length;
- byte[] psb = ChangeNoteUtil.PATCH_SET.getBytes(UTF_8);
- byte[] bpsb = ChangeNoteUtil.BASE_PATCH_SET.getBytes(UTF_8);
- byte[] bpn = ChangeNoteUtil.PARENT_NUMBER.getBytes(UTF_8);
-
- ObjectId commitId =
- ObjectId.fromString(parseStringField(note, p, changeId, ChangeNoteUtil.REVISION));
- String fileName = null;
- PatchSet.Id psId = null;
- boolean isForBase = false;
- Integer parentNumber = null;
-
- while (p.value < sizeOfNote) {
- boolean matchPs = match(note, p, psb);
- boolean matchBase = match(note, p, bpsb);
- if (matchPs) {
- fileName = null;
- psId = parsePsId(note, p, changeId, ChangeNoteUtil.PATCH_SET);
- isForBase = false;
- } else if (matchBase) {
- fileName = null;
- psId = parsePsId(note, p, changeId, ChangeNoteUtil.BASE_PATCH_SET);
- isForBase = true;
- if (match(note, p, bpn)) {
- parentNumber = parseParentNumber(note, p, changeId);
- }
- } else if (psId == null) {
- throw parseException(
- changeId,
- "missing %s or %s header",
- ChangeNoteUtil.PATCH_SET,
- ChangeNoteUtil.BASE_PATCH_SET);
- }
-
- Comment c = parseComment(note, p, fileName, psId, commitId, isForBase, parentNumber);
- fileName = c.key.filename;
- if (!seen.add(c.key)) {
- throw parseException(changeId, "multiple comments for %s in note", c.key);
- }
- result.add(c);
- }
- return result;
- }
-
- private Comment parseComment(
- byte[] note,
- MutableInteger curr,
- String currentFileName,
- PatchSet.Id psId,
- ObjectId commitId,
- boolean isForBase,
- Integer parentNumber)
- throws ConfigInvalidException {
- Change.Id changeId = psId.changeId();
-
- // Check if there is a new file.
- boolean newFile =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.FILE.getBytes(UTF_8))) != -1;
- if (newFile) {
- // If so, parse the new file name.
- currentFileName = parseFilename(note, curr, changeId);
- } else if (currentFileName == null) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.FILE);
- }
-
- CommentRange range = parseCommentRange(note, curr);
- if (range == null) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.COMMENT_RANGE);
- }
-
- Timestamp commentTime = parseTimestamp(note, curr, changeId);
- Account.Id aId = parseAuthor(note, curr, changeId, ChangeNoteUtil.AUTHOR);
- boolean hasRealAuthor =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.REAL_AUTHOR.getBytes(UTF_8))) != -1;
- Account.Id raId = null;
- if (hasRealAuthor) {
- raId = parseAuthor(note, curr, changeId, ChangeNoteUtil.REAL_AUTHOR);
- }
-
- boolean hasParent =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.PARENT.getBytes(UTF_8))) != -1;
- String parentUUID = null;
- boolean unresolved = false;
- if (hasParent) {
- parentUUID = parseStringField(note, curr, changeId, ChangeNoteUtil.PARENT);
- }
- boolean hasUnresolved =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.UNRESOLVED.getBytes(UTF_8))) != -1;
- if (hasUnresolved) {
- unresolved = parseBooleanField(note, curr, changeId, ChangeNoteUtil.UNRESOLVED);
- }
-
- String uuid = parseStringField(note, curr, changeId, ChangeNoteUtil.UUID);
-
- boolean hasTag =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.TAG.getBytes(UTF_8))) != -1;
- String tag = null;
- if (hasTag) {
- tag = parseStringField(note, curr, changeId, ChangeNoteUtil.TAG);
- }
-
- int commentLength = parseCommentLength(note, curr, changeId);
-
- String message = RawParseUtils.decode(UTF_8, note, curr.value, curr.value + commentLength);
- checkResult(message, "message contents", changeId);
-
- Comment c =
- new Comment(
- new Comment.Key(uuid, currentFileName, psId.get()),
- aId,
- commentTime,
- isForBase ? (short) (parentNumber == null ? 0 : -parentNumber) : (short) 1,
- message,
- serverId,
- unresolved);
- c.lineNbr = range.getEndLine();
- c.parentUuid = parentUUID;
- c.tag = tag;
- c.setCommitId(commitId);
- if (raId != null) {
- c.setRealAuthor(raId);
- }
-
- if (range.getStartCharacter() != -1) {
- c.setRange(range);
- }
-
- curr.value = RawParseUtils.nextLF(note, curr.value + commentLength);
- curr.value = RawParseUtils.nextLF(note, curr.value);
- return c;
- }
-
- private static String parseStringField(
- byte[] note, MutableInteger curr, Change.Id changeId, String fieldName)
- throws ConfigInvalidException {
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- checkHeaderLineFormat(note, curr, fieldName, changeId);
- int startOfField = RawParseUtils.endOfFooterLineKey(note, curr.value) + 2;
- curr.value = endOfLine;
- return RawParseUtils.decode(UTF_8, note, startOfField, endOfLine - 1);
- }
-
- /**
- * @return a comment range. If the comment range line in the note only has one number, we return a
- * CommentRange with that one number as the end line and the other fields as -1. If the
- * comment range line in the note contains a whole comment range, then we return a
- * CommentRange with all fields set. If the line is not correctly formatted, return null.
- */
- private static CommentRange parseCommentRange(byte[] note, MutableInteger ptr) {
- CommentRange range = new CommentRange(-1, -1, -1, -1);
-
- int last = ptr.value;
- int startLine = RawParseUtils.parseBase10(note, ptr.value, ptr);
- if (ptr.value == last) {
- return null;
- } else if (note[ptr.value] == '\n') {
- range.setEndLine(startLine);
- ptr.value += 1;
- return range;
- } else if (note[ptr.value] == ':') {
- range.setStartLine(startLine);
- ptr.value += 1;
- } else {
- return null;
- }
-
- last = ptr.value;
- int startChar = RawParseUtils.parseBase10(note, ptr.value, ptr);
- if (ptr.value == last) {
- return null;
- } else if (note[ptr.value] == '-') {
- range.setStartCharacter(startChar);
- ptr.value += 1;
- } else {
- return null;
- }
-
- last = ptr.value;
- int endLine = RawParseUtils.parseBase10(note, ptr.value, ptr);
- if (ptr.value == last) {
- return null;
- } else if (note[ptr.value] == ':') {
- range.setEndLine(endLine);
- ptr.value += 1;
- } else {
- return null;
- }
-
- last = ptr.value;
- int endChar = RawParseUtils.parseBase10(note, ptr.value, ptr);
- if (ptr.value == last) {
- return null;
- } else if (note[ptr.value] == '\n') {
- range.setEndCharacter(endChar);
- ptr.value += 1;
- } else {
- return null;
- }
- return range;
- }
-
- private static PatchSet.Id parsePsId(
- byte[] note, MutableInteger curr, Change.Id changeId, String fieldName)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, fieldName, changeId);
- int startOfPsId = RawParseUtils.endOfFooterLineKey(note, curr.value) + 1;
- MutableInteger i = new MutableInteger();
- int patchSetId = RawParseUtils.parseBase10(note, startOfPsId, i);
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- if (i.value != endOfLine - 1) {
- throw parseException(changeId, "could not parse %s", fieldName);
- }
- checkResult(patchSetId, "patchset id", changeId);
- curr.value = endOfLine;
- return PatchSet.id(changeId, patchSetId);
- }
-
- private static Integer parseParentNumber(byte[] note, MutableInteger curr, Change.Id changeId)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, ChangeNoteUtil.PARENT_NUMBER, changeId);
-
- int start = RawParseUtils.endOfFooterLineKey(note, curr.value) + 1;
- MutableInteger i = new MutableInteger();
- int parentNumber = RawParseUtils.parseBase10(note, start, i);
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- if (i.value != endOfLine - 1) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.PARENT_NUMBER);
- }
- checkResult(parentNumber, "parent number", changeId);
- curr.value = endOfLine;
- return Integer.valueOf(parentNumber);
- }
-
- private static String parseFilename(byte[] note, MutableInteger curr, Change.Id changeId)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, ChangeNoteUtil.FILE, changeId);
- int startOfFileName = RawParseUtils.endOfFooterLineKey(note, curr.value) + 2;
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- curr.value = endOfLine;
- curr.value = RawParseUtils.nextLF(note, curr.value);
- return QuotedString.GIT_PATH.dequote(
- RawParseUtils.decode(UTF_8, note, startOfFileName, endOfLine - 1));
- }
-
- private static Timestamp parseTimestamp(byte[] note, MutableInteger curr, Change.Id changeId)
- throws ConfigInvalidException {
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- Timestamp commentTime;
- String dateString = RawParseUtils.decode(UTF_8, note, curr.value, endOfLine - 1);
- try {
- commentTime = new Timestamp(GitDateParser.parse(dateString, null, Locale.US).getTime());
- } catch (ParseException e) {
- throw new ConfigInvalidException("could not parse comment timestamp", e);
- }
- curr.value = endOfLine;
- return checkResult(commentTime, "comment timestamp", changeId);
- }
-
- private Account.Id parseAuthor(
- byte[] note, MutableInteger curr, Change.Id changeId, String fieldName)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, fieldName, changeId);
- int startOfAccountId = RawParseUtils.endOfFooterLineKey(note, curr.value) + 2;
- PersonIdent ident = RawParseUtils.parsePersonIdent(note, startOfAccountId);
- Account.Id aId = parseIdent(ident, changeId);
- curr.value = RawParseUtils.nextLF(note, curr.value);
- return checkResult(aId, fieldName, changeId);
- }
-
- private static int parseCommentLength(byte[] note, MutableInteger curr, Change.Id changeId)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, ChangeNoteUtil.LENGTH, changeId);
- int startOfLength = RawParseUtils.endOfFooterLineKey(note, curr.value) + 1;
- MutableInteger i = new MutableInteger();
- i.value = startOfLength;
- int commentLength = RawParseUtils.parseBase10(note, startOfLength, i);
- if (i.value == startOfLength) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.LENGTH);
- }
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- if (i.value != endOfLine - 1) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.LENGTH);
- }
- curr.value = endOfLine;
- return checkResult(commentLength, "comment length", changeId);
- }
-
- private boolean parseBooleanField(
- byte[] note, MutableInteger curr, Change.Id changeId, String fieldName)
- throws ConfigInvalidException {
- String str = parseStringField(note, curr, changeId, fieldName);
- if ("true".equalsIgnoreCase(str)) {
- return true;
- } else if ("false".equalsIgnoreCase(str)) {
- return false;
- }
- throw parseException(changeId, "invalid boolean for %s: %s", fieldName, str);
- }
-
- private static <T> T checkResult(T o, String fieldName, Change.Id changeId)
- throws ConfigInvalidException {
- if (o == null) {
- throw parseException(changeId, "could not parse %s", fieldName);
- }
- return o;
- }
-
- private static int checkResult(int i, String fieldName, Change.Id changeId)
- throws ConfigInvalidException {
- if (i <= 0) {
- throw parseException(changeId, "could not parse %s", fieldName);
- }
- return i;
- }
-
- private static void checkHeaderLineFormat(
- byte[] note, MutableInteger curr, String fieldName, Change.Id changeId)
- throws ConfigInvalidException {
- boolean correct = RawParseUtils.match(note, curr.value, fieldName.getBytes(UTF_8)) != -1;
- int p = curr.value + fieldName.length();
- correct &= (p < note.length && note[p] == ':');
- p++;
- correct &= (p < note.length && note[p] == ' ');
- if (!correct) {
- throw parseException(changeId, "could not parse %s", fieldName);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
deleted file mode 100644
index a31a68b..0000000
--- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright (C) 2018 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;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ListMultimap;
-import com.google.gerrit.common.UsedAt;
-import com.google.gerrit.entities.Account;
-import com.google.gerrit.entities.Comment;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.inject.Inject;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.sql.Timestamp;
-import java.util.Date;
-import java.util.List;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.util.QuotedString;
-
-public class LegacyChangeNoteWrite {
-
- private final PersonIdent serverIdent;
- private final String serverId;
-
- @Inject
- public LegacyChangeNoteWrite(
- @GerritPersonIdent PersonIdent serverIdent, @GerritServerId String serverId) {
- this.serverIdent = serverIdent;
- this.serverId = serverId;
- }
-
- public PersonIdent newIdent(Account.Id authorId, Date when, PersonIdent serverIdent) {
- return new PersonIdent(
- authorId.toString(), authorId.get() + "@" + serverId, when, serverIdent.getTimeZone());
- }
-
- @VisibleForTesting
- public PersonIdent newIdent(Account author, Date when, PersonIdent serverIdent) {
- return new PersonIdent(
- author.toString(), author.id().get() + "@" + serverId, when, serverIdent.getTimeZone());
- }
-
- public String getServerId() {
- return serverId;
- }
-
- private void appendHeaderField(PrintWriter writer, String field, String value) {
- writer.print(field);
- writer.print(": ");
- writer.print(value);
- writer.print('\n');
- }
-
- /**
- * Build a note that contains the metadata for and the contents of all of the comments in the
- * given comments.
- *
- * @param comments Comments to be written to the output stream, keyed by patch set ID; multiple
- * patch sets are allowed since base revisions may be shared across patch sets. All of the
- * comments must share the same commitId, and all the comments for a given patch set must have
- * the same side.
- * @param out output stream to write to.
- */
- @UsedAt(UsedAt.Project.GOOGLE)
- public void buildNote(ListMultimap<Integer, Comment> comments, OutputStream out) {
- if (comments.isEmpty()) {
- return;
- }
-
- ImmutableList<Integer> psIds = comments.keySet().stream().sorted().collect(toImmutableList());
-
- OutputStreamWriter streamWriter = new OutputStreamWriter(out, UTF_8);
- try (PrintWriter writer = new PrintWriter(streamWriter)) {
- ObjectId commitId = comments.values().iterator().next().getCommitId();
- String commitName = commitId.name();
- appendHeaderField(writer, ChangeNoteUtil.REVISION, commitName);
-
- for (int psId : psIds) {
- List<Comment> psComments = COMMENT_ORDER.sortedCopy(comments.get(psId));
- Comment first = psComments.get(0);
-
- short side = first.side;
- appendHeaderField(
- writer,
- side <= 0 ? ChangeNoteUtil.BASE_PATCH_SET : ChangeNoteUtil.PATCH_SET,
- Integer.toString(psId));
- if (side < 0) {
- appendHeaderField(writer, ChangeNoteUtil.PARENT_NUMBER, Integer.toString(-side));
- }
-
- String currentFilename = null;
-
- for (Comment c : psComments) {
- checkArgument(
- commitId.equals(c.getCommitId()),
- "All comments being added must have all the same commitId. The "
- + "comment below does not have the same commitId as the others "
- + "(%s).\n%s",
- commitId,
- c);
- checkArgument(
- side == c.side,
- "All comments being added must all have the same side. The "
- + "comment below does not have the same side as the others "
- + "(%s).\n%s",
- side,
- c);
- String commentFilename = QuotedString.GIT_PATH.quote(c.key.filename);
-
- if (!commentFilename.equals(currentFilename)) {
- currentFilename = commentFilename;
- writer.print("File: ");
- writer.print(commentFilename);
- writer.print("\n\n");
- }
-
- appendOneComment(writer, c);
- }
- }
- }
- }
-
- private void appendOneComment(PrintWriter writer, Comment c) {
- // The CommentRange field for a comment is allowed to be null. If it is
- // null, then in the first line, we simply use the line number field for a
- // comment instead. If it isn't null, we write the comment range itself.
- Comment.Range range = c.range;
- if (range != null) {
- writer.print(range.startLine);
- writer.print(':');
- writer.print(range.startChar);
- writer.print('-');
- writer.print(range.endLine);
- writer.print(':');
- writer.print(range.endChar);
- } else {
- writer.print(c.lineNbr);
- }
- writer.print("\n");
-
- writer.print(NoteDbUtil.formatTime(serverIdent, c.writtenOn));
- writer.print("\n");
-
- appendIdent(writer, ChangeNoteUtil.AUTHOR, c.author.getId(), c.writtenOn);
- if (!c.getRealAuthor().equals(c.author)) {
- appendIdent(writer, ChangeNoteUtil.REAL_AUTHOR, c.getRealAuthor().getId(), c.writtenOn);
- }
-
- String parent = c.parentUuid;
- if (parent != null) {
- appendHeaderField(writer, ChangeNoteUtil.PARENT, parent);
- }
-
- appendHeaderField(writer, ChangeNoteUtil.UNRESOLVED, Boolean.toString(c.unresolved));
- appendHeaderField(writer, ChangeNoteUtil.UUID, c.key.uuid);
-
- if (c.tag != null) {
- appendHeaderField(writer, ChangeNoteUtil.TAG, c.tag);
- }
-
- byte[] messageBytes = c.message.getBytes(UTF_8);
- appendHeaderField(writer, ChangeNoteUtil.LENGTH, Integer.toString(messageBytes.length));
-
- writer.print(c.message);
- writer.print("\n\n");
- }
-
- private void appendIdent(PrintWriter writer, String header, Account.Id id, Timestamp ts) {
- PersonIdent ident = newIdent(id, ts, serverIdent);
- StringBuilder name = new StringBuilder();
- PersonIdent.appendSanitized(name, ident.getName());
- name.append(" <");
- PersonIdent.appendSanitized(name, ident.getEmailAddress());
- name.append('>');
- appendHeaderField(writer, header, name.toString());
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteMap.java b/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
index bb464f6..3e1bad1 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
@@ -15,9 +15,7 @@
package com.google.gerrit.server.notedb;
import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -32,18 +30,11 @@
final ImmutableMap<ObjectId, T> revisionNotes;
static RevisionNoteMap<ChangeRevisionNote> parse(
- ChangeNoteJson noteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
- Change.Id changeId,
- ObjectReader reader,
- NoteMap noteMap,
- PatchLineComment.Status status)
+ ChangeNoteJson noteJson, ObjectReader reader, NoteMap noteMap, Comment.Status status)
throws ConfigInvalidException, IOException {
Map<ObjectId, ChangeRevisionNote> result = new HashMap<>();
for (Note note : noteMap) {
- ChangeRevisionNote rn =
- new ChangeRevisionNote(
- noteJson, legacyChangeNoteRead, changeId, reader, note.getData(), status);
+ ChangeRevisionNote rn = new ChangeRevisionNote(noteJson, reader, note.getData(), status);
rn.parse();
result.put(note.copy(), rn);
}
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 3779fbf..44d9d98 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -217,6 +217,14 @@
r.load(update, id);
return r;
}
+
+ @UsedAt(UsedAt.Project.COLLABNET)
+ public ProjectConfig read(Repository repo, Project.NameKey name)
+ throws IOException, ConfigInvalidException {
+ ProjectConfig r = create(name);
+ r.load(repo);
+ return r;
+ }
}
private final StoredConfig baseConfig;
diff --git a/java/com/google/gerrit/server/query/account/AccountPredicates.java b/java/com/google/gerrit/server/query/account/AccountPredicates.java
index 647d70d..1eed7ea 100644
--- a/java/com/google/gerrit/server/query/account/AccountPredicates.java
+++ b/java/com/google/gerrit/server/query/account/AccountPredicates.java
@@ -44,7 +44,7 @@
List<Predicate<AccountState>> preds = Lists.newArrayListWithCapacity(3);
Integer id = Ints.tryParse(query);
if (id != null) {
- preds.add(id(Account.id(id)));
+ preds.add(id(schema, Account.id(id)));
}
if (canSeeSecondaryEmails) {
preds.add(equalsNameIncludingSecondaryEmails(query));
@@ -64,9 +64,11 @@
return Predicate.or(preds);
}
- public static Predicate<AccountState> id(Account.Id accountId) {
+ public static Predicate<AccountState> id(Schema<AccountState> schema, Account.Id accountId) {
return new AccountPredicate(
- AccountField.ID, AccountQueryBuilder.FIELD_ACCOUNT, accountId.toString());
+ schema.useLegacyNumericFields() ? AccountField.ID : AccountField.ID_STR,
+ AccountQueryBuilder.FIELD_ACCOUNT,
+ accountId.toString());
}
public static Predicate<AccountState> emailIncludingSecondaryEmails(String email) {
diff --git a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index 699c988..664df70 100644
--- a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -202,7 +202,7 @@
if ("self".equalsIgnoreCase(query) || "me".equalsIgnoreCase(query)) {
try {
- return Predicate.or(defaultPredicate, AccountPredicates.id(self()));
+ return Predicate.or(defaultPredicate, AccountPredicates.id(args.schema(), self()));
} catch (QueryParseException e) {
// Skip.
}
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index f44a54d..d2fc77d 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -38,7 +38,6 @@
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.index.IndexConfig;
-import com.google.gerrit.index.IndexType;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.SchemaUtil;
import com.google.gerrit.index.query.LimitPredicate;
@@ -461,7 +460,9 @@
if (PAT_LEGACY_ID.matcher(query).matches()) {
Integer id = Ints.tryParse(query);
if (id != null) {
- return new LegacyChangeIdPredicate(Change.id(id));
+ return args.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(Change.id(id))
+ : new LegacyChangeIdStrPredicate(Change.id(id));
}
} else if (PAT_CHANGE_ID.matcher(query).matches()) {
return new ChangeIdPredicate(parseChangeId(query));
@@ -736,9 +737,6 @@
@Operator
public Predicate<ChangeData> extension(String ext) throws QueryParseException {
if (args.getSchema().hasField(ChangeField.EXTENSION)) {
- if (ext.isEmpty() && IndexType.isElasticsearch(args.indexConfig.type())) {
- return new FileWithNoExtensionInElasticPredicate();
- }
return new FileExtensionPredicate(ext);
}
throw new QueryParseException("'extension' operator is not supported by change index version");
@@ -777,11 +775,6 @@
if (directory.startsWith("^")) {
return new RegexDirectoryPredicate(directory);
}
-
- if (IndexType.isElasticsearch(args.indexConfig.type())
- && (directory.isEmpty() || directory.equals("/"))) {
- return Predicate.any();
- }
return new DirectoryPredicate(directory);
}
throw new QueryParseException("'directory' operator is not supported by change index version");
diff --git a/java/com/google/gerrit/server/query/change/CommentPredicate.java b/java/com/google/gerrit/server/query/change/CommentPredicate.java
index d193bb6..4b14f08 100644
--- a/java/com/google/gerrit/server/query/change/CommentPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommentPredicate.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.query.change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
@@ -32,9 +33,15 @@
@Override
public boolean match(ChangeData object) {
try {
- Predicate<ChangeData> p = Predicate.and(new LegacyChangeIdPredicate(object.getId()), this);
+ Change.Id id = object.getId();
+ Predicate<ChangeData> p =
+ Predicate.and(
+ index.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(id)
+ : new LegacyChangeIdStrPredicate(id),
+ this);
for (ChangeData cData : index.getSource(p, IndexedChangeQuery.oneResult()).read()) {
- if (cData.getId().equals(object.getId())) {
+ if (cData.getId().equals(id)) {
return true;
}
}
diff --git a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
index 5ec1e87..17e4a59 100644
--- a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -87,7 +87,11 @@
List<Predicate<ChangeData>> and = new ArrayList<>(5);
and.add(new ProjectPredicate(c.getProject().get()));
and.add(new RefPredicate(c.getDest().branch()));
- and.add(Predicate.not(new LegacyChangeIdPredicate(c.getId())));
+ and.add(
+ Predicate.not(
+ args.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(c.getId())
+ : new LegacyChangeIdStrPredicate(c.getId())));
and.add(Predicate.or(filePredicates));
ChangeDataCache changeDataCache = new ChangeDataCache(cd, args.projectCache);
diff --git a/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java b/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java
deleted file mode 100644
index d886baf..0000000
--- a/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2019 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.query.change;
-
-import com.google.gerrit.index.query.PostFilterPredicate;
-import com.google.gerrit.server.index.change.ChangeField;
-
-public class FileWithNoExtensionInElasticPredicate extends PostFilterPredicate<ChangeData> {
-
- private static final String NO_EXT = "";
-
- public FileWithNoExtensionInElasticPredicate() {
- super(ChangeField.EXTENSION.getName(), NO_EXT);
- }
-
- @Override
- public boolean match(ChangeData cd) {
- return ChangeField.getExtensions(cd).contains(NO_EXT);
- }
-
- @Override
- public int getCost() {
- return 1;
- }
-}
diff --git a/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java b/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
index a5838e5..d558b0f 100644
--- a/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
@@ -43,7 +43,10 @@
return false;
}
try {
- Predicate<ChangeData> thisId = new LegacyChangeIdPredicate(cd.getId());
+ Predicate<ChangeData> thisId =
+ index.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(cd.getId())
+ : new LegacyChangeIdStrPredicate(cd.getId());
Iterable<ChangeData> results =
index.getSource(and(thisId, this), IndexedChangeQuery.oneResult()).read();
return !Iterables.isEmpty(results);
diff --git a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index 0bef209..6605c23 100644
--- a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -55,6 +55,11 @@
* holding on to a single instance.
*/
public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChangeQuery> {
+ @FunctionalInterface
+ static interface ChangeIdPredicateFactory {
+ Predicate<ChangeData> create(Change.Id id);
+ }
+
private static Predicate<ChangeData> ref(BranchNameKey branch) {
return new RefPredicate(branch.branch());
}
@@ -78,6 +83,9 @@
private final ChangeData.Factory changeDataFactory;
private final ChangeNotes.Factory notesFactory;
+ // TODO(davido): Remove the below fields when support for legacy numeric fields is removed.
+ private final ChangeIdPredicateFactory predicateFactory;
+
@Inject
InternalChangeQuery(
ChangeQueryProcessor queryProcessor,
@@ -88,6 +96,11 @@
super(queryProcessor, indexes, indexConfig);
this.changeDataFactory = changeDataFactory;
this.notesFactory = notesFactory;
+ predicateFactory =
+ (id) ->
+ schema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(id)
+ : new LegacyChangeIdStrPredicate(id);
}
public List<ChangeData> byKey(Change.Key key) {
@@ -99,13 +112,13 @@
}
public List<ChangeData> byLegacyChangeId(Change.Id id) {
- return query(new LegacyChangeIdPredicate(id));
+ return query(predicateFactory.create(id));
}
public List<ChangeData> byLegacyChangeIds(Collection<Change.Id> ids) {
List<Predicate<ChangeData>> preds = new ArrayList<>(ids.size());
for (Change.Id id : ids) {
- preds.add(new LegacyChangeIdPredicate(id));
+ preds.add(predicateFactory.create(id));
}
return query(or(preds));
}
diff --git a/java/com/google/gerrit/server/query/change/LegacyChangeIdStrPredicate.java b/java/com/google/gerrit/server/query/change/LegacyChangeIdStrPredicate.java
new file mode 100644
index 0000000..bae9c0d
--- /dev/null
+++ b/java/com/google/gerrit/server/query/change/LegacyChangeIdStrPredicate.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2019 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.query.change;
+
+import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID_STR;
+
+import com.google.gerrit.entities.Change;
+
+/** Predicate over change number (aka legacy ID or Change.Id). */
+public class LegacyChangeIdStrPredicate extends ChangeIndexPredicate {
+ protected final Change.Id id;
+
+ public LegacyChangeIdStrPredicate(Change.Id id) {
+ super(LEGACY_ID_STR, ChangeQueryBuilder.FIELD_CHANGE, id.toString());
+ this.id = id;
+ }
+
+ @Override
+ public boolean match(ChangeData object) {
+ return id.equals(object.getId());
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/java/com/google/gerrit/server/query/change/MessagePredicate.java b/java/com/google/gerrit/server/query/change/MessagePredicate.java
index 0bd8c88..44cbd8e 100644
--- a/java/com/google/gerrit/server/query/change/MessagePredicate.java
+++ b/java/com/google/gerrit/server/query/change/MessagePredicate.java
@@ -33,7 +33,12 @@
@Override
public boolean match(ChangeData object) {
try {
- Predicate<ChangeData> p = Predicate.and(new LegacyChangeIdPredicate(object.getId()), this);
+ Predicate<ChangeData> p =
+ Predicate.and(
+ index.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(object.getId())
+ : new LegacyChangeIdStrPredicate(object.getId()),
+ this);
for (ChangeData cData : index.getSource(p, IndexedChangeQuery.oneResult()).read()) {
if (cData.getId().equals(object.getId())) {
return true;
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index b326834..1973b00 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -70,6 +70,7 @@
import java.util.TimeZone;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.InvalidObjectIdException;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
@@ -381,7 +382,14 @@
throw new BadRequestException(String.format("Base %s doesn't represent a valid SHA-1", base));
}
- RevCommit baseCommit = revWalk.parseCommit(baseObjectId);
+ RevCommit baseCommit;
+ try {
+ baseCommit = revWalk.parseCommit(baseObjectId);
+ } catch (MissingObjectException e) {
+ throw new UnprocessableEntityException(
+ String.format("Base %s doesn't exist", baseObjectId.name()), e);
+ }
+
InternalChangeQuery changeQuery = queryProvider.get();
changeQuery.enforceVisibility(true);
List<ChangeData> changeDatas = changeQuery.byBranchCommit(project, destRef.getName(), base);
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index acc6465..758cf47 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -170,16 +170,28 @@
BatchUpdate.Factory updateFactory, TopLevelResource parent, ChangeInput input)
throws IOException, InvalidChangeOperationException, RestApiException, UpdateException,
PermissionBackendException, ConfigInvalidException {
+ if (Strings.isNullOrEmpty(input.project)) {
+ throw new BadRequestException("project must be non-empty");
+ }
+
+ return execute(updateFactory, input, projectsCollection.parse(input.project));
+ }
+
+ /** Creates the changes in the given project. This is public for reuse in the project API. */
+ public Response<ChangeInfo> execute(
+ BatchUpdate.Factory updateFactory, ChangeInput input, ProjectResource projectResource)
+ throws IOException, RestApiException, UpdateException, PermissionBackendException,
+ ConfigInvalidException {
if (!user.get().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
- IdentifiedUser me = user.get().asIdentifiedUser();
- checkAndSanitizeChangeInput(input, me);
- ProjectResource projectResource = projectsCollection.parse(input.project);
ProjectState projectState = projectResource.getProjectState();
projectState.checkStatePermitsWrite();
+ IdentifiedUser me = user.get().asIdentifiedUser();
+ checkAndSanitizeChangeInput(input, me);
+
Project.NameKey project = projectResource.getNameKey();
contributorAgreements.check(project, user.get());
@@ -202,10 +214,6 @@
*/
private void checkAndSanitizeChangeInput(ChangeInput input, IdentifiedUser me)
throws RestApiException, PermissionBackendException, IOException {
- if (Strings.isNullOrEmpty(input.project)) {
- throw new BadRequestException("project must be non-empty");
- }
-
if (Strings.isNullOrEmpty(input.branch)) {
throw new BadRequestException("branch must be non-empty");
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index 88938b6..f434e31 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -18,7 +18,6 @@
import com.google.common.base.Strings;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment.Status;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.common.CommentInfo;
@@ -121,7 +120,8 @@
setCommentCommitId(comment, patchListCache, ctx.getChange(), ps);
- commentsUtil.putComments(ctx.getUpdate(psId), Status.DRAFT, Collections.singleton(comment));
+ commentsUtil.putComments(
+ ctx.getUpdate(psId), Comment.Status.DRAFT, Collections.singleton(comment));
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Module.java b/java/com/google/gerrit/server/restapi/change/Module.java
index a57bd64..539463f 100644
--- a/java/com/google/gerrit/server/restapi/change/Module.java
+++ b/java/com/google/gerrit/server/restapi/change/Module.java
@@ -96,6 +96,7 @@
post(CHANGE_KIND, "hashtags").to(PostHashtags.class);
post(CHANGE_KIND, "restore").to(Restore.class);
post(CHANGE_KIND, "revert").to(Revert.class);
+ post(CHANGE_KIND, "revert_submission").to(RevertSubmission.class);
post(CHANGE_KIND, "submit").to(Submit.CurrentRevision.class);
get(CHANGE_KIND, "submitted_together").to(SubmittedTogether.class);
post(CHANGE_KIND, "rebase").to(Rebase.CurrentRevision.class);
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index a38e88f..974a72c 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -49,7 +49,6 @@
import com.google.gerrit.entities.FixSuggestion;
import com.google.gerrit.entities.LabelId;
import com.google.gerrit.entities.Patch;
-import com.google.gerrit.entities.PatchLineComment.Status;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.RobotComment;
@@ -249,7 +248,10 @@
}
ProjectState projectState = projectCache.checkedGet(revision.getProject());
LabelTypes labelTypes = projectState.getLabelTypes(revision.getNotes());
+
input.drafts = firstNonNull(input.drafts, DraftHandling.KEEP);
+ logger.atFine().log("draft handling = %s", input.drafts);
+
if (input.onBehalfOf != null) {
revision = onBehalfOf(revision, labelTypes, input);
}
@@ -971,7 +973,7 @@
break;
}
ChangeUpdate changeUpdate = ctx.getUpdate(psId);
- commentsUtil.putComments(changeUpdate, Status.PUBLISHED, toPublish);
+ commentsUtil.putComments(changeUpdate, Comment.Status.PUBLISHED, toPublish);
comments.addAll(toPublish);
return !toPublish.isEmpty();
}
diff --git a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index 948ad89..5696fcb 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -17,7 +17,6 @@
import static com.google.gerrit.server.CommentsUtil.setCommentCommitId;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment.Status;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.common.CommentInfo;
@@ -140,7 +139,7 @@
}
setCommentCommitId(comment, patchListCache, ctx.getChange(), ps);
commentsUtil.putComments(
- update, Status.DRAFT, Collections.singleton(update(comment, in, ctx.getWhen())));
+ update, Comment.Status.DRAFT, Collections.singleton(update(comment, in, ctx.getWhen())));
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
new file mode 100644
index 0000000..2c3930a
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
@@ -0,0 +1,166 @@
+// Copyright (C) 2019 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.restapi.change;
+
+import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+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.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.permissions.ChangePermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.project.ContributorAgreementsChecker;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryingRestModifyView;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang.RandomStringUtils;
+
+@Singleton
+public class RevertSubmission
+ extends RetryingRestModifyView<ChangeResource, RevertInput, RevertSubmissionInfo>
+ implements UiAction<ChangeResource> {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final Revert revert;
+ private final Provider<InternalChangeQuery> queryProvider;
+ private final ChangeResource.Factory changeResourceFactory;
+ private final Provider<CurrentUser> user;
+ private final PermissionBackend permissionBackend;
+ private final ProjectCache projectCache;
+ private final PatchSetUtil psUtil;
+ private final ContributorAgreementsChecker contributorAgreements;
+
+ @Inject
+ RevertSubmission(
+ RetryHelper retryHelper,
+ Revert revert,
+ Provider<InternalChangeQuery> queryProvider,
+ ChangeResource.Factory changeResourceFactory,
+ Provider<CurrentUser> user,
+ PermissionBackend permissionBackend,
+ ProjectCache projectCache,
+ PatchSetUtil psUtil,
+ ContributorAgreementsChecker contributorAgreements) {
+ super(retryHelper);
+ this.revert = revert;
+ this.queryProvider = queryProvider;
+ this.changeResourceFactory = changeResourceFactory;
+ this.user = user;
+ this.permissionBackend = permissionBackend;
+ this.projectCache = projectCache;
+ this.psUtil = psUtil;
+ this.contributorAgreements = contributorAgreements;
+ }
+
+ @Override
+ public Response<RevertSubmissionInfo> applyImpl(
+ BatchUpdate.Factory updateFactory, ChangeResource changeResource, RevertInput input)
+ throws Exception {
+
+ if (!changeResource.getChange().isMerged()) {
+ throw new ResourceConflictException(
+ String.format("change is %s.", ChangeUtil.status(changeResource.getChange())));
+ }
+
+ String submissionId =
+ requireNonNull(
+ changeResource.getChange().getSubmissionId(),
+ String.format("merged change %s has no submission ID", changeResource.getId()));
+
+ List<ChangeData> changeDatas = queryProvider.get().bySubmissionId(submissionId);
+
+ for (ChangeData changeData : changeDatas) {
+ Change change = changeData.change();
+
+ // Might do the permission tests multiple times, but these are necessary to ensure that the
+ // user has permissions to revert all changes. If they lack any permission, no revert will be
+ // done.
+
+ contributorAgreements.check(change.getProject(), changeResource.getUser());
+ permissionBackend.currentUser().ref(change.getDest()).check(CREATE_CHANGE);
+ permissionBackend.currentUser().change(changeData).check(ChangePermission.READ);
+ projectCache.checkedGet(change.getProject()).checkStatePermitsWrite();
+
+ requireNonNull(
+ psUtil.get(changeData.notes(), change.currentPatchSetId()),
+ String.format(
+ "current patch set %s of change %s not found",
+ change.currentPatchSetId(), change.currentPatchSetId()));
+ }
+ return Response.ok(revertSubmission(changeDatas, input, submissionId));
+ }
+
+ private RevertSubmissionInfo revertSubmission(
+ List<ChangeData> changeDatas, RevertInput input, String submissionId) throws Exception {
+ List<ChangeInfo> results;
+ results = new ArrayList<>();
+ if (input.topic == null) {
+ input.topic =
+ String.format(
+ "revert-%s-%s", submissionId, RandomStringUtils.randomAlphabetic(10).toUpperCase());
+ }
+ for (ChangeData changeData : changeDatas) {
+ ChangeResource change = changeResourceFactory.create(changeData.notes(), user.get());
+ // Reverts are done with retrying by using RetryingRestModifyView.
+ results.add(revert.apply(change, input).value());
+ }
+ RevertSubmissionInfo revertSubmissionInfo = new RevertSubmissionInfo();
+ revertSubmissionInfo.revertChanges = results;
+ return revertSubmissionInfo;
+ }
+
+ @Override
+ public Description getDescription(ChangeResource rsrc) {
+ Change change = rsrc.getChange();
+ boolean projectStatePermitsWrite = false;
+ try {
+ projectStatePermitsWrite = projectCache.checkedGet(rsrc.getProject()).statePermitsWrite();
+ } catch (IOException e) {
+ logger.atSevere().withCause(e).log(
+ "Failed to check if project state permits write: %s", rsrc.getProject());
+ }
+ return new UiAction.Description()
+ .setLabel(
+ "Revert this change and all changes that have been submitted together with this change")
+ .setTitle("Revert submission")
+ .setVisible(
+ and(
+ projectStatePermitsWrite,
+ permissionBackend
+ .user(rsrc.getUser())
+ .ref(change.getDest())
+ .testCond(CREATE_CHANGE)));
+ }
+}
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index 26f7873..676cc07 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -32,6 +32,7 @@
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.query.FieldBundle;
@@ -227,6 +228,13 @@
return suggestedReviewers;
}
+ private static Account.Id fromIdField(FieldBundle f, boolean useLegacyNumericFields) {
+ if (useLegacyNumericFields) {
+ return Account.id(f.getValue(AccountField.ID).intValue());
+ }
+ return Account.id(Integer.valueOf(f.getValue(AccountField.ID_STR)));
+ }
+
private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers)
throws BadRequestException {
try (Timer0.Context ctx = metrics.queryAccountsLatency.start()) {
@@ -239,6 +247,10 @@
accountQueryBuilder.defaultQuery(suggestReviewers.getQuery()));
logger.atFine().log("accounts index query: %s", pred);
accountIndexRewriter.validateMaxTermsInQuery(pred);
+ boolean useLegacyNumericFields =
+ accountIndexes.getSearchIndex().getSchema().useLegacyNumericFields();
+ FieldDef<AccountState, ?> idField =
+ useLegacyNumericFields ? AccountField.ID : AccountField.ID_STR;
ResultSet<FieldBundle> result =
accountIndexes
.getSearchIndex()
@@ -248,11 +260,11 @@
indexConfig,
0,
suggestReviewers.getLimit(),
- ImmutableSet.of(AccountField.ID.getName())))
+ ImmutableSet.of(idField.getName())))
.readRaw();
List<Account.Id> matches =
result.toList().stream()
- .map(f -> Account.id(f.getValue(AccountField.ID).intValue()))
+ .map(f -> fromIdField(f, useLegacyNumericFields))
.collect(toList());
logger.atFine().log("Matches: %s", matches);
return matches;
diff --git a/java/com/google/gerrit/server/restapi/project/CreateChange.java b/java/com/google/gerrit/server/restapi/project/CreateChange.java
new file mode 100644
index 0000000..de11ffe
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/project/CreateChange.java
@@ -0,0 +1,70 @@
+// Copyright (C) 2019 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.restapi.project;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.exceptions.InvalidNameException;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.ChangeInput;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.InvalidChangeOperationException;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryingRestModifyView;
+import com.google.gerrit.server.update.UpdateException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+
+@Singleton
+public class CreateChange extends RetryingRestModifyView<ProjectResource, ChangeInput, ChangeInfo> {
+ private final com.google.gerrit.server.restapi.change.CreateChange changeCreateChange;
+ private final Provider<CurrentUser> user;
+
+ @Inject
+ public CreateChange(
+ RetryHelper retryHelper,
+ Provider<CurrentUser> user,
+ com.google.gerrit.server.restapi.change.CreateChange changeCreateChange) {
+ super(retryHelper);
+ this.changeCreateChange = changeCreateChange;
+ this.user = user;
+ }
+
+ @Override
+ public Response<ChangeInfo> applyImpl(
+ BatchUpdate.Factory updateFactory, ProjectResource rsrc, ChangeInput input)
+ throws PermissionBackendException, IOException, ConfigInvalidException,
+ InvalidChangeOperationException, InvalidNameException, UpdateException, RestApiException {
+ if (!user.get().isIdentifiedUser()) {
+ throw new AuthException("Authentication required");
+ }
+
+ if (!Strings.isNullOrEmpty(input.project)) {
+ throw new BadRequestException("may not specify project");
+ }
+
+ input.project = rsrc.getName();
+ return changeCreateChange.execute(updateFactory, input, rsrc);
+ }
+}
diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java
index de5661d..065facd 100644
--- a/java/com/google/gerrit/server/restapi/project/Module.java
+++ b/java/com/google/gerrit/server/restapi/project/Module.java
@@ -77,6 +77,7 @@
child(PROJECT_KIND, "branches").to(BranchesCollection.class);
create(BRANCH_KIND).to(CreateBranch.class);
+ post(PROJECT_KIND, "create.change").to(CreateChange.class);
put(BRANCH_KIND).to(PutBranch.class);
get(BRANCH_KIND).to(GetBranch.class);
delete(BRANCH_KIND).to(DeleteBranch.class);
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index a06027a..406c0a1 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -504,7 +504,7 @@
try {
integrateIntoHistory(cs);
} catch (IntegrationException e) {
- logger.atSevere().withCause(e).log("Error from integrateIntoHistory");
+ logger.atWarning().withCause(e).log("Error from integrateIntoHistory");
throw new ResourceConflictException(e.getMessage(), e);
}
return null;
diff --git a/java/com/google/gerrit/server/util/ReplicaUtil.java b/java/com/google/gerrit/server/util/ReplicaUtil.java
new file mode 100644
index 0000000..bf6111a
--- /dev/null
+++ b/java/com/google/gerrit/server/util/ReplicaUtil.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 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.util;
+
+import org.eclipse.jgit.lib.Config;
+
+public class ReplicaUtil {
+ /** Provides backward compatibility for container.slave property. */
+ public static boolean isReplica(Config cfg) {
+ return cfg.getBoolean("container", "slave", false)
+ || cfg.getBoolean("container", "replica", false);
+ }
+}
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index 08835cc..fa9685e 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -86,6 +86,7 @@
import com.google.gerrit.server.securestore.SecureStore;
import com.google.gerrit.server.ssh.NoSshKeyCache;
import com.google.gerrit.server.submit.LocalMergeSuperSetComputation;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@@ -299,11 +300,10 @@
private Module indexModule(String moduleClassName) {
try {
- boolean slave = cfg.getBoolean("container", "slave", false);
Class<?> clazz = Class.forName(moduleClassName);
Method m =
clazz.getMethod("singleVersionWithExplicitVersions", Map.class, int.class, boolean.class);
- return (Module) m.invoke(null, getSingleSchemaVersions(), 0, slave);
+ return (Module) m.invoke(null, getSingleSchemaVersions(), 0, ReplicaUtil.isReplica(cfg));
} catch (ClassNotFoundException
| SecurityException
| NoSuchMethodException
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
index 5550d98..9059cb1 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
@@ -239,6 +239,28 @@
}
@Test
+ public void revertSubmissionWithoutCLA() throws Exception {
+ assume().that(isContributorAgreementsEnabled()).isTrue();
+
+ // Create a change succeeds when agreement is not required
+ setUseContributorAgreements(InheritableBoolean.FALSE);
+ ChangeInfo change = gApi.changes().create(newChangeInput()).get();
+
+ // Approve and submit it
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.changes().id(change.changeId).current().review(ReviewInput.approve());
+ gApi.changes().id(change.changeId).current().submit(new SubmitInput());
+
+ // Revert Submission is not allowed when CLA is required but not signed
+ requestScopeOperations.setApiUser(user.id());
+ setUseContributorAgreements(InheritableBoolean.TRUE);
+ AuthException thrown =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(change.changeId).revertSubmission());
+ assertThat(thrown).hasMessageThat().contains("Contributor Agreement");
+ }
+
+ @Test
public void revertExcludedProjectChangeWithoutCLA() throws Exception {
// Contributor agreements configured with excludeProjects = ExcludedProject
// in AbstractDaemonTest.configureContributorAgreement(...)
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
index 0607a3c..0e89de2 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
@@ -21,12 +21,16 @@
import static java.util.stream.Collectors.toList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.UseClockStep;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.RevertInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -36,6 +40,8 @@
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.PureRevertInfo;
+import com.google.gerrit.extensions.common.RevertSubmissionInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -45,8 +51,10 @@
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Repository;
import org.junit.Test;
@@ -361,17 +369,20 @@
@Test
public void cantCreateRevertWithoutProjectWritePermission() throws Exception {
- PushOneCommit.Result r = createChange();
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+ PushOneCommit.Result result = createChange();
+ gApi.changes()
+ .id(result.getChangeId())
+ .revision(result.getCommit().name())
+ .review(ReviewInput.approve());
+ gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
projectCache.checkedGet(project).getProject().setState(ProjectState.READ_ONLY);
+ String expected = "project state " + ProjectState.READ_ONLY + " does not permit write";
ResourceConflictException thrown =
assertThrows(
- ResourceConflictException.class, () -> gApi.changes().id(r.getChangeId()).revert());
- assertThat(thrown)
- .hasMessageThat()
- .contains("project state " + ProjectState.READ_ONLY + " does not permit write");
+ ResourceConflictException.class,
+ () -> gApi.changes().id(result.getChangeId()).revert());
+ assertThat(thrown).hasMessageThat().contains(expected);
}
@Test
@@ -412,6 +423,339 @@
assertThat(thrown).hasMessageThat().contains("Not found: " + r.getChangeId());
}
+ @Test
+ @GerritConfig(name = "change.submitWholeTopic", value = "true")
+ public void cantCreateRevertSubmissionWithoutProjectWritePermission() throws Exception {
+ String secondProject = "secondProject";
+ projectOperations.newProject().name(secondProject).create();
+ TestRepository<InMemoryRepository> secondRepo =
+ cloneProject(Project.nameKey("secondProject"), admin);
+ String topic = "topic";
+ PushOneCommit.Result result1 =
+ createChange(testRepo, "master", "first change", "a.txt", "message", topic);
+ PushOneCommit.Result result2 =
+ createChange(secondRepo, "master", "second change", "b.txt", "message", topic);
+ gApi.changes().id(result1.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result2.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result1.getChangeId()).revision(result1.getCommit().name()).submit();
+
+ // revoke write permissions for the first repository.
+ projectCache.checkedGet(project).getProject().setState(ProjectState.READ_ONLY);
+
+ String expected = "project state " + ProjectState.READ_ONLY + " does not permit write";
+
+ // assert that if first repository has no write permissions, it will fail.
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(result1.getChangeId()).revertSubmission());
+ assertThat(thrown).hasMessageThat().contains(expected);
+
+ // assert that if the first repository has no write permissions and a change from another
+ // repository is trying to revert the submission, it will fail.
+ thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(result2.getChangeId()).revertSubmission());
+ assertThat(thrown).hasMessageThat().contains(expected);
+ }
+
+ @Test
+ @GerritConfig(name = "change.submitWholeTopic", value = "true")
+ public void cantCreateRevertSubmissionWithoutCreateChangePermission() throws Exception {
+ String secondProject = "secondProject";
+ projectOperations.newProject().name(secondProject).create();
+ TestRepository<InMemoryRepository> secondRepo =
+ cloneProject(Project.nameKey("secondProject"), admin);
+ String topic = "topic";
+ PushOneCommit.Result result1 =
+ createChange(testRepo, "master", "first change", "a.txt", "message", topic);
+ PushOneCommit.Result result2 =
+ createChange(secondRepo, "master", "second change", "b.txt", "message", topic);
+ gApi.changes().id(result1.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result2.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result1.getChangeId()).revision(result1.getCommit().name()).submit();
+
+ // revoke create change permissions for the first repository.
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.PUSH).ref("refs/for/*").group(REGISTERED_USERS))
+ .update();
+
+ // assert that if first repository has no write create change, it will fail.
+ PermissionDeniedException thrown =
+ assertThrows(
+ PermissionDeniedException.class,
+ () -> gApi.changes().id(result1.getChangeId()).revertSubmission());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("not permitted: create change on refs/heads/master");
+
+ // assert that if the first repository has no create change permissions and a change from
+ // another repository is trying to revert the submission, it will fail.
+ thrown =
+ assertThrows(
+ PermissionDeniedException.class,
+ () -> gApi.changes().id(result2.getChangeId()).revertSubmission());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("not permitted: create change on refs/heads/master");
+ }
+
+ @Test
+ @GerritConfig(name = "change.submitWholeTopic", value = "true")
+ public void cantCreateRevertSubmissionWithoutReadPermission() throws Exception {
+ String secondProject = "secondProject";
+ projectOperations.newProject().name(secondProject).create();
+ TestRepository<InMemoryRepository> secondRepo =
+ cloneProject(Project.nameKey("secondProject"), admin);
+ String topic = "topic";
+ PushOneCommit.Result result1 =
+ createChange(testRepo, "master", "first change", "a.txt", "message", topic);
+ PushOneCommit.Result result2 =
+ createChange(secondRepo, "master", "second change", "b.txt", "message", topic);
+ gApi.changes().id(result1.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result2.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result1.getChangeId()).revision(result1.getCommit().name()).submit();
+
+ // revoke read permissions for the first repository.
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
+
+ // assert that if first repository has no read permissions, it will fail.
+ ResourceNotFoundException resourceNotFoundException =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(result1.getChangeId()).revertSubmission());
+ assertThat(resourceNotFoundException)
+ .hasMessageThat()
+ .isEqualTo("Not found: " + result1.getChangeId());
+
+ // assert that if the first repository has no READ permissions and a change from another
+ // repository is trying to revert the submission, it will fail.
+ AuthException authException =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(result2.getChangeId()).revertSubmission());
+ assertThat(authException).hasMessageThat().isEqualTo("read not permitted");
+ }
+
+ @Test
+ public void revertSubmissionPreservesReviewersAndCcs() throws Exception {
+ PushOneCommit.Result r = createChange("first change", "a.txt", "message");
+
+ ReviewInput in = ReviewInput.approve();
+ in.reviewer(user.email());
+ in.reviewer(accountCreator.user2().email(), ReviewerState.CC, true);
+ // Add user as reviewer that will create the revert
+ in.reviewer(accountCreator.admin2().email());
+
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+
+ // expect both the original reviewers and CCs to be preserved
+ // original owner should be added as reviewer, user requesting the revert (new owner) removed
+ requestScopeOperations.setApiUser(accountCreator.admin2().id());
+ Map<ReviewerState, Collection<AccountInfo>> result =
+ getChangeApis(gApi.changes().id(r.getChangeId()).revertSubmission()).get(0).get().reviewers;
+ assertThat(result).containsKey(ReviewerState.REVIEWER);
+
+ List<Integer> reviewers =
+ result.get(ReviewerState.REVIEWER).stream().map(a -> a._accountId).collect(toList());
+ assertThat(result).containsKey(ReviewerState.CC);
+ List<Integer> ccs =
+ result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
+ assertThat(ccs).containsExactly(accountCreator.user2().id().get());
+ assertThat(reviewers).containsExactly(user.id().get(), admin.id().get());
+ }
+
+ @Test
+ public void revertSubmissionNotifications() throws Exception {
+ PushOneCommit.Result firstResult = createChange("first change", "a.txt", "message");
+ approve(firstResult.getChangeId());
+ gApi.changes().id(firstResult.getChangeId()).addReviewer(user.email());
+ PushOneCommit.Result secondResult = createChange("second change", "b.txt", "other");
+ approve(secondResult.getChangeId());
+ gApi.changes().id(secondResult.getChangeId()).addReviewer(user.email());
+
+ gApi.changes()
+ .id(secondResult.getChangeId())
+ .revision(secondResult.getCommit().name())
+ .submit();
+
+ sender.clear();
+ RevertInput revertInput = new RevertInput();
+ revertInput.notify = NotifyHandling.ALL;
+
+ RevertSubmissionInfo revertChanges =
+ gApi.changes().id(secondResult.getChangeId()).revertSubmission(revertInput);
+
+ List<Message> messages = sender.getMessages();
+
+ assertThat(messages).hasSize(4);
+ assertThat(sender.getMessages(revertChanges.revertChanges.get(0).changeId, "newchange"))
+ .hasSize(1);
+ assertThat(sender.getMessages(firstResult.getChangeId(), "revert")).hasSize(1);
+ assertThat(sender.getMessages(revertChanges.revertChanges.get(1).changeId, "newchange"))
+ .hasSize(1);
+ assertThat(sender.getMessages(secondResult.getChangeId(), "revert")).hasSize(1);
+ }
+
+ @Test
+ public void suppressRevertSubmissionNotifications() throws Exception {
+ PushOneCommit.Result firstResult = createChange("first change", "a.txt", "message");
+ approve(firstResult.getChangeId());
+ gApi.changes().id(firstResult.getChangeId()).addReviewer(user.email());
+ PushOneCommit.Result secondResult = createChange("second change", "b.txt", "other");
+ approve(secondResult.getChangeId());
+ gApi.changes().id(secondResult.getChangeId()).addReviewer(user.email());
+
+ gApi.changes()
+ .id(secondResult.getChangeId())
+ .revision(secondResult.getCommit().name())
+ .submit();
+
+ RevertInput revertInput = new RevertInput();
+ revertInput.notify = NotifyHandling.NONE;
+
+ sender.clear();
+ gApi.changes().id(secondResult.getChangeId()).revertSubmission(revertInput);
+ assertThat(sender.getMessages()).isEmpty();
+ }
+
+ @Test
+ public void revertSubmissionOfSingleChange() throws Exception {
+ PushOneCommit.Result result = createChange("Change", "a.txt", "message");
+ approve(result.getChangeId());
+ gApi.changes().id(result.getChangeId()).current().submit();
+ List<ChangeApi> revertChanges =
+ getChangeApis(gApi.changes().id(result.getChangeId()).revertSubmission());
+
+ String sha1Commit = result.getCommit().getName();
+
+ assertThat(revertChanges.get(0).current().commit(false).parents.get(0).commit)
+ .isEqualTo(sha1Commit);
+
+ assertThat(revertChanges.get(0).current().files().get("a.txt").linesDeleted).isEqualTo(1);
+
+ assertThat(revertChanges.get(0).get().revertOf)
+ .isEqualTo(result.getChange().change().getChangeId());
+ assertThat(revertChanges.get(0).get().topic)
+ .startsWith("revert-" + result.getChange().change().getSubmissionId() + "-");
+ }
+
+ @Test
+ public void revertSubmissionWithSetTopic() throws Exception {
+ PushOneCommit.Result result = createChange();
+ gApi.changes().id(result.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result.getChangeId()).topic("topic");
+ gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
+ RevertInput revertInput = new RevertInput();
+ revertInput.topic = "reverted-not-default";
+ assertThat(
+ gApi.changes()
+ .id(result.getChangeId())
+ .revertSubmission(revertInput)
+ .revertChanges
+ .get(0)
+ .topic)
+ .isEqualTo(revertInput.topic);
+ }
+
+ @Test
+ public void revertSubmissionWithSetMessage() throws Exception {
+ PushOneCommit.Result result = createChange();
+ gApi.changes().id(result.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
+ RevertInput revertInput = new RevertInput();
+ revertInput.message = "Message from input";
+ assertThat(
+ gApi.changes()
+ .id(result.getChangeId())
+ .revertSubmission(revertInput)
+ .revertChanges
+ .get(0)
+ .subject)
+ .isEqualTo(revertInput.message);
+ }
+
+ @Test
+ @GerritConfig(name = "change.submitWholeTopic", value = "true")
+ @UseClockStep
+ public void revertSubmissionDifferentRepositoriesWithDependantChange() throws Exception {
+ projectOperations.newProject().name("secondProject").create();
+ TestRepository<InMemoryRepository> secondRepo =
+ cloneProject(Project.nameKey("secondProject"), admin);
+ List<PushOneCommit.Result> resultCommits = new ArrayList<>();
+ String topic = "topic";
+ resultCommits.add(createChange(testRepo, "master", "first change", "a.txt", "message", topic));
+ resultCommits.add(
+ createChange(secondRepo, "master", "first change", "a.txt", "message", topic));
+ resultCommits.add(
+ createChange(secondRepo, "master", "second change", "b.txt", "Other message", topic));
+ for (PushOneCommit.Result result : resultCommits) {
+ approve(result.getChangeId());
+ }
+ // submit all changes
+ gApi.changes().id(resultCommits.get(1).getChangeId()).current().submit();
+ List<ChangeApi> revertChanges =
+ getChangeApis(gApi.changes().id(resultCommits.get(1).getChangeId()).revertSubmission());
+
+ // The reverts are by update time, so the reversal ensures that
+ // revertChanges[i] is the revert of resultCommits[i]
+ Collections.reverse(revertChanges);
+
+ assertThat(revertChanges.get(0).current().files().get("a.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);
+ // has size 3 because of the same topic, and submitWholeTopic is true.
+ assertThat(gApi.changes().id(revertChanges.get(0).get()._number).submittedTogether())
+ .hasSize(3);
+
+ // expected messages on source change:
+ // 1. Uploaded patch set 1.
+ // 2. Patch Set 1: Code-Review+2
+ // 3. Change has been successfully merged by Administrator
+ // 4. Created a revert of this change as %s
+
+ for (int i = 0; i < resultCommits.size(); i++) {
+ assertThat(revertChanges.get(i).current().commit(false).parents.get(0).commit)
+ .isEqualTo(resultCommits.get(i).getCommit().getName());
+ assertThat(revertChanges.get(i).get().revertOf)
+ .isEqualTo(resultCommits.get(i).getChange().change().getChangeId());
+ List<ChangeMessageInfo> sourceMessages =
+ new ArrayList<>(gApi.changes().id(resultCommits.get(i).getChangeId()).get().messages);
+ assertThat(sourceMessages).hasSize(4);
+ String expectedMessage =
+ String.format(
+ "Created a revert of this change as %s", revertChanges.get(i).get().changeId);
+ assertThat(sourceMessages.get(3).message).isEqualTo(expectedMessage);
+ // Expected message on the created change: "Uploaded patch set 1."
+ List<ChangeMessageInfo> messages =
+ revertChanges.get(i).get().messages.stream().collect(toList());
+ assertThat(messages).hasSize(1);
+ assertThat(messages.get(0).message).isEqualTo("Uploaded patch set 1.");
+ assertThat(revertChanges.get(i).get().revertOf)
+ .isEqualTo(gApi.changes().id(resultCommits.get(i).getChangeId()).get()._number);
+ assertThat(revertChanges.get(i).get().topic)
+ .startsWith("revert-" + resultCommits.get(0).getChange().change().getSubmissionId());
+ }
+ }
+
+ @Test
+ public void cantRevertSubmissionWithAnOpenChange() throws Exception {
+ PushOneCommit.Result result = createChange("first change", "a.txt", "message");
+ approve(result.getChangeId());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(result.getChangeId()).revertSubmission());
+ assertThat(thrown).hasMessageThat().isEqualTo("change is new.");
+ }
+
@Override
protected PushOneCommit.Result createChange() throws Exception {
return createChange("refs/for/master");
@@ -451,4 +795,13 @@
.create();
}
}
+
+ private List<ChangeApi> getChangeApis(RevertSubmissionInfo revertSubmissionInfo)
+ throws Exception {
+ List<ChangeApi> results = new ArrayList<>();
+ for (ChangeInfo changeInfo : revertSubmissionInfo.revertChanges) {
+ results.add(gApi.changes().id(changeInfo._number));
+ }
+ return results;
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 2d94566..32941ff 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -53,6 +53,7 @@
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.DraftApi;
@@ -974,6 +975,42 @@
}
@Test
+ public void cherryPickToNonExistingBranch() throws Exception {
+ PushOneCommit.Result result = createChange();
+
+ CherryPickInput input = new CherryPickInput();
+ input.message = "foo bar";
+ input.destination = "non-existing";
+ // TODO(ekempin): This should rather result in an UnprocessableEntityException.
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(result.getChangeId()).current().cherryPick(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ String.format("Branch %s does not exist.", RefNames.REFS_HEADS + input.destination));
+ }
+
+ @Test
+ public void cherryPickToNonExistingBaseCommit() throws Exception {
+ createBranch(BranchNameKey.create(project, "foo"));
+ PushOneCommit.Result result = createChange();
+
+ CherryPickInput input = new CherryPickInput();
+ input.message = "foo bar";
+ input.destination = "foo";
+ input.base = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.changes().id(result.getChangeId()).current().cherryPick(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(String.format("Base %s doesn't exist", input.base));
+ }
+
+ @Test
public void canRebase() throws Exception {
PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r1 = push.to("refs/for/master");
diff --git a/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java b/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
index b77825a..cad0b83 100644
--- a/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
+++ b/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
@@ -241,7 +241,7 @@
}
private void enableSlaveMode() throws Exception {
- updateConfig(config -> config.setBoolean("container", null, "slave", true));
+ updateConfig(config -> config.setBoolean("container", null, "replica", true));
}
private void updateConfig(Consumer<Config> configConsumer) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
index 8a284d9..83bc3eb 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
@@ -84,6 +84,7 @@
RestCall.post("/changes/%s/rebase"),
RestCall.post("/changes/%s/restore"),
RestCall.post("/changes/%s/revert"),
+ RestCall.post("/changes/%s/revert_submission"),
RestCall.get("/changes/%s/pure_revert"),
RestCall.post("/changes/%s/submit"),
RestCall.get("/changes/%s/submitted_together"),
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
index f48a603..48dc89f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
@@ -69,6 +69,7 @@
RestCall.get("/projects/%s/statistics.git"),
RestCall.post("/projects/%s/index"),
RestCall.post("/projects/%s/gc"),
+ RestCall.post("/projects/%s/create.change"),
RestCall.get("/projects/%s/children"),
RestCall.get("/projects/%s/branches"),
RestCall.post("/projects/%s/branches:delete"),
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
index dda7bbd..8b51e7f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
@@ -63,11 +63,25 @@
return gApi.changes().id(id).revision(1).actions();
}
+ protected Map<String, ActionInfo> getChangeActions(String id) throws Exception {
+ return gApi.changes().id(id).get().actions;
+ }
+
protected String getETag(String id) throws Exception {
return gApi.changes().id(id).current().etag();
}
@Test
+ public void changeActionOneMergedChangeHasReverts() throws Exception {
+ String changeId = createChangeWithTopic().getChangeId();
+ gApi.changes().id(changeId).current().review(ReviewInput.approve());
+ gApi.changes().id(changeId).current().submit();
+ Map<String, ActionInfo> actions = getChangeActions(changeId);
+ assertThat(actions).containsKey("revert");
+ assertThat(actions).containsKey("revert_submission");
+ }
+
+ @Test
public void revisionActionsOneChangePerTopicUnapproved() throws Exception {
String changeId = createChangeWithTopic().getChangeId();
Map<String, ActionInfo> actions = getActions(changeId);
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateChangeIT.java
new file mode 100644
index 0000000..ca3707d
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateChangeIT.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.project;
+
+import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.entities.RefNames.REFS_HEADS;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.gerrit.extensions.common.ChangeInput;
+import org.junit.Test;
+
+public class CreateChangeIT extends AbstractDaemonTest {
+
+ // Just a basic test. The real functionality is tested under the restapi.change acceptance tests.
+ @Test
+ public void basic() throws Exception {
+ BranchInput branchInput = new BranchInput();
+ branchInput.ref = "foo";
+ assertThat(gApi.projects().name(project.get()).branches().get().stream().map(i -> i.ref))
+ .doesNotContain(REFS_HEADS + branchInput.ref);
+ RestResponse r =
+ adminRestSession.put(
+ "/projects/" + project.get() + "/branches/" + branchInput.ref, branchInput);
+ r.assertCreated();
+
+ ChangeInput input = new ChangeInput();
+ input.branch = "foo";
+ input.subject = "subject";
+ RestResponse cr = adminRestSession.post("/projects/" + project.get() + "/create.change", input);
+ cr.assertCreated();
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
index b22d319..bf2b915 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -1058,10 +1058,10 @@
Map<String, com.google.gerrit.entities.Comment> commentMapBefore =
DeleteCommentRewriter.getPublishedComments(
- noteUtil, changeId, reader, NoteMap.read(reader, commitBefore));
+ noteUtil, reader, NoteMap.read(reader, commitBefore));
Map<String, com.google.gerrit.entities.Comment> commentMapAfter =
DeleteCommentRewriter.getPublishedComments(
- noteUtil, changeId, reader, NoteMap.read(reader, commitAfter));
+ noteUtil, reader, NoteMap.read(reader, commitAfter));
if (commentMapBefore.containsKey(targetCommentUuid)) {
assertThat(commentMapAfter).containsKey(targetCommentUuid);
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java b/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
index 7be90134..15c66eb 100644
--- a/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
@@ -29,7 +29,7 @@
assertThat(
staticTemplateData(
"http://example.com/", null, null, new HashMap<>(), IndexHtmlUtilTest::ordain))
- .containsExactly("canonicalPath", "", "polymer2", "true", "staticResourcePath", ordain(""));
+ .containsExactly("canonicalPath", "", "staticResourcePath", ordain(""));
}
@Test
@@ -41,13 +41,7 @@
null,
new HashMap<>(),
IndexHtmlUtilTest::ordain))
- .containsExactly(
- "canonicalPath",
- "/gerrit",
- "polymer2",
- "true",
- "staticResourcePath",
- ordain("/gerrit"));
+ .containsExactly("canonicalPath", "/gerrit", "staticResourcePath", ordain("/gerrit"));
}
@Test
@@ -60,12 +54,7 @@
new HashMap<>(),
IndexHtmlUtilTest::ordain))
.containsExactly(
- "canonicalPath",
- "",
- "polymer2",
- "true",
- "staticResourcePath",
- ordain("http://my-cdn.com/foo/bar/"));
+ "canonicalPath", "", "staticResourcePath", ordain("http://my-cdn.com/foo/bar/"));
}
@Test
@@ -78,12 +67,7 @@
new HashMap<>(),
IndexHtmlUtilTest::ordain))
.containsExactly(
- "canonicalPath",
- "/gerrit",
- "polymer2",
- "true",
- "staticResourcePath",
- ordain("http://my-cdn.com/foo/bar/"));
+ "canonicalPath", "/gerrit", "staticResourcePath", ordain("http://my-cdn.com/foo/bar/"));
}
private static SanitizedContent ordain(String s) {
diff --git a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
index c46c3da..a23ccab 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
@@ -28,10 +28,11 @@
@Ignore
public class FakeChangeIndex implements ChangeIndex {
- static final Schema<ChangeData> V1 = new Schema<>(1, ImmutableList.of(ChangeField.STATUS));
+ static final Schema<ChangeData> V1 = new Schema<>(1, false, ImmutableList.of(ChangeField.STATUS));
static final Schema<ChangeData> V2 =
- new Schema<>(2, ImmutableList.of(ChangeField.STATUS, ChangeField.PATH, ChangeField.UPDATED));
+ new Schema<>(
+ 2, false, ImmutableList.of(ChangeField.STATUS, ChangeField.PATH, ChangeField.UPDATED));
private static class Source implements ChangeDataSource {
private final Predicate<ChangeData> p;
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
index ed79be6..013939a 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
@@ -214,6 +214,26 @@
}
@Test
+ public void parseAssignee() throws Exception {
+ assertParseSucceeds(
+ "Update change\n"
+ + "\n"
+ + "Branch: refs/heads/master\n"
+ + "Change-id: I577fb248e474018276351785930358ec0450e9f7\n"
+ + "Patch-set: 1\n"
+ + "Assignee: Change Owner <1@gerrit>\n"
+ + "Subject: This is a test change\n");
+ assertParseSucceeds(
+ "Update change\n"
+ + "\n"
+ + "Branch: refs/heads/master\n"
+ + "Change-id: I577fb248e474018276351785930358ec0450e9f7\n"
+ + "Patch-set: 2\n"
+ + "Assignee:\n"
+ + "Subject: This is a test change\n");
+ }
+
+ @Test
public void parseTopic() throws Exception {
assertParseSucceeds(
"Update change\n"
@@ -551,8 +571,6 @@
private ChangeNotesParser newParser(ObjectId tip) throws Exception {
walk.reset();
ChangeNoteJson changeNoteJson = injector.getInstance(ChangeNoteJson.class);
- LegacyChangeNoteRead reader = injector.getInstance(LegacyChangeNoteRead.class);
- return new ChangeNotesParser(
- newChange().getId(), tip, walk, changeNoteJson, reader, args.metrics);
+ return new ChangeNotesParser(newChange().getId(), tip, walk, changeNoteJson, args.metrics);
}
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index d381e99..0d7f2bd 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -38,10 +38,12 @@
import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
import com.google.gerrit.entities.converter.PatchSetProtoConverter;
import com.google.gerrit.mail.Address;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
+import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.AssigneeStatusUpdateProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ChangeColumnsProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
@@ -242,17 +244,6 @@
}
@Test
- public void serializeAssignee() throws Exception {
- assertRoundTrip(
- newBuilder().columns(cols.toBuilder().assignee(Account.id(2000)).build()).build(),
- ChangeNotesStateProto.newBuilder()
- .setMetaId(SHA_BYTES)
- .setChangeId(ID.get())
- .setColumns(colsProto.toBuilder().setAssignee(2000).setHasAssignee(true))
- .build());
- }
-
- @Test
public void serializeStatus() throws Exception {
assertRoundTrip(
newBuilder().columns(cols.toBuilder().status(Change.Status.MERGED).build()).build(),
@@ -308,19 +299,6 @@
}
@Test
- public void serializePastAssignees() throws Exception {
- assertRoundTrip(
- newBuilder().pastAssignees(ImmutableSet.of(Account.id(2002), Account.id(2001))).build(),
- ChangeNotesStateProto.newBuilder()
- .setMetaId(SHA_BYTES)
- .setChangeId(ID.get())
- .setColumns(colsProto)
- .addPastAssignee(2002)
- .addPastAssignee(2001)
- .build());
- }
-
- @Test
public void serializeHashtags() throws Exception {
assertRoundTrip(
newBuilder().hashtags(ImmutableSet.of("tag2", "tag1")).build(),
@@ -611,6 +589,35 @@
}
@Test
+ public void serializeAssigneeUpdates() throws Exception {
+ assertRoundTrip(
+ newBuilder()
+ .assigneeUpdates(
+ ImmutableList.of(
+ AssigneeStatusUpdate.create(
+ new Timestamp(1212L), Account.id(1000), Optional.of(Account.id(2001))),
+ AssigneeStatusUpdate.create(
+ new Timestamp(3434L), Account.id(1000), Optional.empty())))
+ .build(),
+ ChangeNotesStateProto.newBuilder()
+ .setMetaId(SHA_BYTES)
+ .setChangeId(ID.get())
+ .setColumns(colsProto)
+ .addAssigneeUpdate(
+ AssigneeStatusUpdateProto.newBuilder()
+ .setDate(1212L)
+ .setUpdatedBy(1000)
+ .setCurrentAssignee(2001)
+ .setHasCurrentAssignee(true))
+ .addAssigneeUpdate(
+ AssigneeStatusUpdateProto.newBuilder()
+ .setDate(3434L)
+ .setUpdatedBy(1000)
+ .setHasCurrentAssignee(false))
+ .build());
+ }
+
+ @Test
public void serializeSubmitRecords() throws Exception {
SubmitRecord sr1 = new SubmitRecord();
sr1.status = SubmitRecord.Status.OK;
@@ -721,7 +728,6 @@
.put("changeId", Change.Id.class)
.put("serverId", String.class)
.put("columns", ChangeColumns.class)
- .put("pastAssignees", new TypeLiteral<ImmutableSet<Account.Id>>() {}.getType())
.put("hashtags", new TypeLiteral<ImmutableSet<String>>() {}.getType())
.put(
"patchSets",
@@ -738,6 +744,9 @@
.put(
"reviewerUpdates",
new TypeLiteral<ImmutableList<ReviewerStatusUpdate>>() {}.getType())
+ .put(
+ "assigneeUpdates",
+ new TypeLiteral<ImmutableList<AssigneeStatusUpdate>>() {}.getType())
.put("submitRecords", new TypeLiteral<ImmutableList<SubmitRecord>>() {}.getType())
.put("changeMessages", new TypeLiteral<ImmutableList<ChangeMessage>>() {}.getType())
.put(
@@ -762,7 +771,6 @@
.put("topic", String.class)
.put("originalSubject", String.class)
.put("submissionId", String.class)
- .put("assignee", Account.Id.class)
.put("status", Change.Status.class)
.put("isPrivate", boolean.class)
.put("workInProgress", boolean.class)
@@ -844,6 +852,19 @@
}
@Test
+ public void assigneeStatusUpdateMethods() throws Exception {
+ assertThatSerializedClass(AssigneeStatusUpdate.class)
+ .hasAutoValueMethods(
+ ImmutableMap.of(
+ "date",
+ Timestamp.class,
+ "updatedBy",
+ Account.Id.class,
+ "currentAssignee",
+ new TypeLiteral<Optional<Account.Id>>() {}.getType()));
+ }
+
+ @Test
public void submitRecordFields() throws Exception {
assertThatSerializedClass(SubmitRecord.class)
.hasFields(
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 2ed145e..5993206 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -43,11 +43,11 @@
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.Comment;
import com.google.gerrit.entities.CommentRange;
-import com.google.gerrit.entities.PatchLineComment.Status;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.mail.Address;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ReviewerSet;
@@ -74,7 +74,6 @@
@Inject private DraftCommentNotes.Factory draftNotesFactory;
@Inject private ChangeNoteJson changeNoteJson;
- @Inject private LegacyChangeNoteRead legacyChangeNoteRead;
@Inject private @GerritServerId String serverId;
@@ -120,7 +119,7 @@
RevCommit commit = tr.commit().message("PS2").create();
ChangeUpdate update = newUpdate(c, changeOwner);
update.putComment(
- Status.PUBLISHED,
+ Comment.Status.PUBLISHED,
newComment(
c.currentPatchSetId(),
"a.txt",
@@ -182,7 +181,7 @@
RevCommit commit = tr.commit().message("PS2").create();
update = newUpdate(c, changeOwner);
update.putComment(
- Status.PUBLISHED,
+ Comment.Status.PUBLISHED,
newComment(
c.currentPatchSetId(),
"a.txt",
@@ -745,6 +744,35 @@
}
@Test
+ public void assigneeStatusUpdateChangeNotes() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, otherUser);
+ update.setAssignee(otherUserId);
+ update.commit();
+
+ update = newUpdate(c, changeOwner);
+ update.removeAssignee();
+ update.commit();
+
+ update = newUpdate(c, changeOwner);
+ update.setAssignee(changeOwner.getAccountId());
+ update.commit();
+
+ update = newUpdate(c, changeOwner);
+ update.setAssignee(otherUserId);
+ update.commit();
+
+ ChangeNotes notes = newNotes(c);
+ ImmutableList<AssigneeStatusUpdate> statusUpdates = notes.getAssigneeUpdates();
+ assertThat(statusUpdates).hasSize(4);
+ assertThat(statusUpdates.get(3).updatedBy()).isEqualTo(otherUserId);
+ assertThat(statusUpdates.get(3).currentAssignee()).hasValue(otherUserId);
+ assertThat(statusUpdates.get(2).currentAssignee()).isEmpty();
+ assertThat(statusUpdates.get(1).currentAssignee()).hasValue(changeOwner.getAccountId());
+ assertThat(statusUpdates.get(0).currentAssignee()).hasValue(otherUserId);
+ }
+
+ @Test
public void hashtagCommit() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
@@ -1061,7 +1089,7 @@
update.putApproval("Code-Review", (short) 1);
update.setChangeMessage("This is a message");
update.putComment(
- Status.PUBLISHED,
+ Comment.Status.PUBLISHED,
newComment(
c.currentPatchSetId(),
"a.txt",
@@ -1161,7 +1189,7 @@
update.setPatchSetId(psId2);
Timestamp ts = TimeUtil.nowTs();
update.putComment(
- Status.PUBLISHED,
+ Comment.Status.PUBLISHED,
newComment(
psId2,
"a.txt",
@@ -1246,7 +1274,7 @@
ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"),
false);
update1.setPatchSetId(psId);
- update1.putComment(Status.PUBLISHED, comment1);
+ update1.putComment(Comment.Status.PUBLISHED, comment1);
updateManager.add(update1);
ChangeUpdate update2 = newUpdate(c, otherUser);
@@ -1268,12 +1296,7 @@
try (ChangeNotesRevWalk rw = ChangeNotesCommit.newRevWalk(repo)) {
ChangeNotesParser notesWithComments =
new ChangeNotesParser(
- c.getId(),
- commitWithComments.copy(),
- rw,
- changeNoteJson,
- legacyChangeNoteRead,
- args.metrics);
+ c.getId(), commitWithComments.copy(), rw, changeNoteJson, args.metrics);
ChangeNotesState state = notesWithComments.parseAll();
assertThat(state.approvals()).isEmpty();
assertThat(state.publishedComments()).hasSize(1);
@@ -1282,12 +1305,7 @@
try (ChangeNotesRevWalk rw = ChangeNotesCommit.newRevWalk(repo)) {
ChangeNotesParser notesWithApprovals =
new ChangeNotesParser(
- c.getId(),
- commitWithApprovals.copy(),
- rw,
- changeNoteJson,
- legacyChangeNoteRead,
- args.metrics);
+ c.getId(), commitWithApprovals.copy(), rw, changeNoteJson, args.metrics);
ChangeNotesState state = notesWithApprovals.parseAll();
assertThat(state.approvals()).hasSize(1);
@@ -1470,7 +1488,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1500,7 +1518,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1530,7 +1548,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1560,7 +1578,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1629,9 +1647,9 @@
ChangeUpdate update = newUpdate(c, otherUser);
update.setPatchSetId(psId2);
- update.putComment(Status.PUBLISHED, comment3);
- update.putComment(Status.PUBLISHED, comment2);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment3);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1671,7 +1689,7 @@
false);
comment.setRealAuthor(changeOwner.getAccountId());
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1709,7 +1727,7 @@
ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"),
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1747,7 +1765,7 @@
commitId1,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, commentForBase);
+ update.putComment(Comment.Status.PUBLISHED, commentForBase);
update.commit();
update = newUpdate(c, otherUser);
@@ -1766,7 +1784,7 @@
commitId2,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, commentForPS);
+ update.putComment(Comment.Status.PUBLISHED, commentForPS);
update.commit();
assertThat(newNotes(c).getComments())
@@ -1805,7 +1823,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
update = newUpdate(c, otherUser);
@@ -1824,7 +1842,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
assertThat(newNotes(c).getComments())
@@ -1863,7 +1881,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
update = newUpdate(c, otherUser);
@@ -1882,7 +1900,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
assertThat(newNotes(c).getComments())
@@ -1921,7 +1939,7 @@
commitId1,
false);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
incrementPatchSet(c);
@@ -1944,7 +1962,7 @@
commitId2,
false);
update.setPatchSetId(ps2);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
assertThat(newNotes(c).getComments())
@@ -1981,7 +1999,7 @@
commitId,
false);
update.setPatchSetId(ps1);
- update.putComment(Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment1);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1991,7 +2009,7 @@
update = newUpdate(c, otherUser);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
notes = newNotes(c);
@@ -2044,8 +2062,8 @@
side,
commitId,
false);
- update.putComment(Status.DRAFT, comment1);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -2060,7 +2078,7 @@
// Publish first draft.
update = newUpdate(c, otherUser);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
notes = newNotes(c);
@@ -2115,8 +2133,8 @@
commitId2,
false);
- update.putComment(Status.DRAFT, baseComment);
- update.putComment(Status.DRAFT, psComment);
+ update.putComment(Comment.Status.DRAFT, baseComment);
+ update.putComment(Comment.Status.DRAFT, psComment);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -2131,8 +2149,8 @@
update = newUpdate(c, otherUser);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, baseComment);
- update.putComment(Status.PUBLISHED, psComment);
+ update.putComment(Comment.Status.PUBLISHED, baseComment);
+ update.putComment(Comment.Status.PUBLISHED, psComment);
update.commit();
notes = newNotes(c);
@@ -2171,7 +2189,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.DRAFT, comment);
+ update.putComment(Comment.Status.DRAFT, comment);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -2216,7 +2234,7 @@
commitId1,
false);
update.setPatchSetId(ps1);
- update.putComment(Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment1);
update.commit();
incrementPatchSet(c);
@@ -2239,7 +2257,7 @@
commitId2,
false);
update.setPatchSetId(ps2);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -2283,7 +2301,7 @@
side,
commitId,
false);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
assertThat(repo.exactRef(changeMetaRef(c.getId()))).isNotNull();
@@ -2316,7 +2334,7 @@
side,
commitId,
false);
- update.putComment(Status.DRAFT, draft);
+ update.putComment(Comment.Status.DRAFT, draft);
update.commit();
String draftRef = refsDraftComments(c.getId(), otherUser.getAccountId());
@@ -2338,7 +2356,7 @@
side,
commitId,
false);
- update.putComment(Status.PUBLISHED, pub);
+ update.putComment(Comment.Status.PUBLISHED, pub);
update.commit();
assertThat(exactRefAllUsers(draftRef)).isEqualTo(old);
@@ -2369,7 +2387,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
assertThat(newNotes(c).getComments())
@@ -2401,7 +2419,7 @@
commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
assertThat(newNotes(c).getComments())
@@ -2453,8 +2471,8 @@
side,
commitId2,
false);
- update.putComment(Status.DRAFT, comment1);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -2463,8 +2481,8 @@
update = newUpdate(c, otherUser);
update.setPatchSetId(ps2);
- update.putComment(Status.PUBLISHED, comment1);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
notes = newNotes(c);
@@ -2511,8 +2529,8 @@
side,
commitId1,
false);
- update.putComment(Status.DRAFT, comment1);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -2522,7 +2540,7 @@
update = newUpdate(c, otherUser);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
notes = newNotes(c);
@@ -2584,8 +2602,8 @@
side,
commitId1,
false);
- update.putComment(Status.DRAFT, comment1);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
String refName = refsDraftComments(c.getId(), otherUserId);
@@ -2593,7 +2611,7 @@
update = newUpdate(c, otherUser);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
assertThat(exactRefAllUsers(refName)).isNotNull();
assertThat(exactRefAllUsers(refName)).isNotEqualTo(oldDraftId);
@@ -2619,7 +2637,7 @@
update = newUpdate(c, otherUser);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
// Updating an unrelated comment causes the zombie comment to get fixed up.
@@ -2647,7 +2665,7 @@
(short) 1,
commitId,
false);
- update1.putComment(Status.PUBLISHED, comment1);
+ update1.putComment(Comment.Status.PUBLISHED, comment1);
ChangeUpdate update2 = newUpdate(c, otherUser);
Comment comment2 =
@@ -2664,7 +2682,7 @@
(short) 1,
commitId,
false);
- update2.putComment(Status.PUBLISHED, comment2);
+ update2.putComment(Comment.Status.PUBLISHED, comment2);
try (NoteDbUpdateManager manager = updateManagerFactory.create(project)) {
manager.add(update1);
@@ -2721,7 +2739,7 @@
(short) 1,
ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"),
false);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
notes = newNotes(c);
diff --git a/javatests/com/google/gerrit/server/notedb/DraftCommentNotesTest.java b/javatests/com/google/gerrit/server/notedb/DraftCommentNotesTest.java
index e53da4c..bf49884 100644
--- a/javatests/com/google/gerrit/server/notedb/DraftCommentNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/DraftCommentNotesTest.java
@@ -18,7 +18,6 @@
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
-import com.google.gerrit.entities.PatchLineComment.Status;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.server.util.time.TimeUtil;
import org.eclipse.jgit.lib.ObjectId;
@@ -32,7 +31,7 @@
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
update.setPatchSetId(c.currentPatchSetId());
- update.putComment(Status.PUBLISHED, comment(c.currentPatchSetId()));
+ update.putComment(Comment.Status.PUBLISHED, comment(c.currentPatchSetId()));
update.commit();
assertThat(newNotes(c).getDraftComments(otherUserId)).isEmpty();
@@ -45,13 +44,13 @@
ChangeUpdate update = newUpdate(c, otherUser);
update.setPatchSetId(c.currentPatchSetId());
- update.putComment(Status.DRAFT, comment(c.currentPatchSetId()));
+ update.putComment(Comment.Status.DRAFT, comment(c.currentPatchSetId()));
update.commit();
assertThat(newNotes(c).getDraftComments(otherUserId)).hasSize(1);
assertableFanOutExecutor.assertInteractions(0);
update = newUpdate(c, otherUser);
- update.putComment(Status.PUBLISHED, comment(c.currentPatchSetId()));
+ update.putComment(Comment.Status.PUBLISHED, comment(c.currentPatchSetId()));
update.commit();
assertThat(newNotes(c).getDraftComments(otherUserId)).isEmpty();
@@ -64,7 +63,7 @@
ChangeUpdate update = newUpdate(c, otherUser);
update.setPatchSetId(c.currentPatchSetId());
- update.putComment(Status.DRAFT, comment(c.currentPatchSetId()));
+ update.putComment(Comment.Status.DRAFT, comment(c.currentPatchSetId()));
update.commit();
ChangeNotes notes = newNotes(c);
diff --git a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
index 0f3e148..e7f0812 100644
--- a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -631,19 +631,22 @@
public void rawDocument() throws Exception {
AccountInfo userInfo = gApi.accounts().id(admin.getAccountId().get()).get();
+ Schema<AccountState> schema = indexes.getSearchIndex().getSchema();
Optional<FieldBundle> rawFields =
indexes
.getSearchIndex()
.getRaw(
Account.id(userInfo._accountId),
QueryOptions.create(
- IndexConfig.createDefault(),
- 0,
- 1,
- indexes.getSearchIndex().getSchema().getStoredFields().keySet()));
+ IndexConfig.createDefault(), 0, 1, schema.getStoredFields().keySet()));
assertThat(rawFields).isPresent();
- assertThat(rawFields.get().getValue(AccountField.ID)).isEqualTo(userInfo._accountId);
+ if (schema.useLegacyNumericFields()) {
+ assertThat(rawFields.get().getValue(AccountField.ID)).isEqualTo(userInfo._accountId);
+ } else {
+ assertThat(Integer.valueOf(rawFields.get().getValue(AccountField.ID_STR)))
+ .isEqualTo(userInfo._accountId);
+ }
// The field EXTERNAL_ID_STATE is only supported from schema version 6.
if (getSchemaVersion() < 6) {
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index d4f836e..2c5fcc4 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -154,7 +154,7 @@
@Inject protected AllUsersName allUsersName;
@Inject protected BatchUpdate.Factory updateFactory;
@Inject protected ChangeInserter.Factory changeFactory;
- @Inject protected ChangeQueryBuilder queryBuilder;
+ @Inject protected Provider<ChangeQueryBuilder> queryBuilderProvider;
@Inject protected GerritApi gApi;
@Inject protected IdentifiedUser.GenericFactory userFactory;
@Inject protected ChangeIndexCollection indexes;
@@ -3052,6 +3052,7 @@
assertQuery(ChangeIndexPredicate.none());
+ ChangeQueryBuilder queryBuilder = queryBuilderProvider.get();
for (Predicate<ChangeData> matchingOneChange :
ImmutableList.of(
// One index query, one post-filtering query.
diff --git a/modules/jgit b/modules/jgit
index 009e078..8e356fc 160000
--- a/modules/jgit
+++ b/modules/jgit
@@ -1 +1 @@
-Subproject commit 009e07882fee2b0ad2ebe7ba44e2ff52c101f858
+Subproject commit 8e356fc45e2b169572088ebdfb931ca788a5e220
diff --git a/plugins/replication b/plugins/replication
index e5881ac..fefba17 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit e5881ace0e39fdd15a3ce42d1626ebaf62595cbf
+Subproject commit fefba17b04224e9346a50f6e66944142f4878179
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index 54b780b..6d30a14 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -18,8 +18,7 @@
],
),
outs = ["polygerrit_ui.zip"],
- # TODO(taoalpha): replace with gr-app.html once p2 fully rolled out
- app = "elements/gr-app-p2.html",
+ app = "elements/gr-app.html",
)
bower_component_bundle(
diff --git a/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html b/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html
index 7e68669..5e4e8fd 100644
--- a/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html
@@ -136,6 +136,7 @@
* * num {number} The number identifying the patch set
* * desc {!string} Optional patch set description
* * wip {boolean} If true, this patch set was never subject to review.
+ * * sha {string} hash of the commit
*
* The wip property is determined by the change's current work_in_progress
* property and its log of change messages.
@@ -148,14 +149,18 @@
if (!change) { return []; }
let patchNums = [];
if (change.revisions && Object.keys(change.revisions).length) {
+ const revisions = Object.keys(change.revisions).map(sha => {
+ return Object.assign({sha}, change.revisions[sha]);
+ });
patchNums =
- Gerrit.PatchSetBehavior.sortRevisions(Object.values(change.revisions))
+ Gerrit.PatchSetBehavior.sortRevisions(revisions)
.map(e => {
// TODO(kaspern): Mark which patchset an edit was made on, if an
// edit exists -- perhaps with a temporary description.
return {
num: e._number,
desc: e.description,
+ sha: e.sha,
};
});
}
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index 8803c83..085ce6e 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -366,9 +366,10 @@
_sectionsChanged() {
// Flush DOM operations so that the list item elements will be loaded.
- Polymer.dom.flush();
- this.$.cursor.stops = this._getListItems();
- this.$.cursor.moveToStart();
+ Polymer.RenderStatus.afterNextRender(this, () => {
+ this.$.cursor.stops = this._getListItems();
+ this.$.cursor.moveToStart();
+ });
},
_isOutgoing(section) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
index dce2a55..4599f01 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
@@ -172,11 +172,11 @@
{_number: 2},
];
flushAsynchronousOperations();
- const elementItems = Polymer.dom(element.root).querySelectorAll(
- 'gr-change-list-item');
- assert.equal(elementItems.length, 3);
+ Polymer.RenderStatus.afterNextRender(element, () => {
+ const elementItems = Polymer.dom(element.root).querySelectorAll(
+ 'gr-change-list-item');
+ assert.equal(elementItems.length, 3);
- flush(() => {
assert.isTrue(elementItems[0].hasAttribute('selected'));
MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
assert.equal(element.selectedIndex, 1);
@@ -470,42 +470,44 @@
},
];
flushAsynchronousOperations();
- const elementItems = Polymer.dom(element.root).querySelectorAll(
- 'gr-change-list-item');
- assert.equal(elementItems.length, 9);
+ Polymer.RenderStatus.afterNextRender(element, () => {
+ const elementItems = Polymer.dom(element.root).querySelectorAll(
+ 'gr-change-list-item');
+ assert.equal(elementItems.length, 9);
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- assert.equal(element.selectedIndex, 1);
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ assert.equal(element.selectedIndex, 1);
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- const navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
- assert.equal(element.selectedIndex, 2);
+ const navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
+ assert.equal(element.selectedIndex, 2);
- MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
- assert.deepEqual(navStub.lastCall.args[0], {_number: 2},
- 'Should navigate to /c/2/');
+ MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
+ assert.deepEqual(navStub.lastCall.args[0], {_number: 2},
+ 'Should navigate to /c/2/');
- MockInteractions.pressAndReleaseKeyOn(element, 75); // 'k'
- assert.equal(element.selectedIndex, 1);
- MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
- assert.deepEqual(navStub.lastCall.args[0], {_number: 1},
- 'Should navigate to /c/1/');
+ MockInteractions.pressAndReleaseKeyOn(element, 75); // 'k'
+ assert.equal(element.selectedIndex, 1);
+ MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
+ assert.deepEqual(navStub.lastCall.args[0], {_number: 1},
+ 'Should navigate to /c/1/');
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- assert.equal(element.selectedIndex, 4);
- MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
- assert.deepEqual(navStub.lastCall.args[0], {_number: 4},
- 'Should navigate to /c/4/');
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ assert.equal(element.selectedIndex, 4);
+ MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
+ assert.deepEqual(navStub.lastCall.args[0], {_number: 4},
+ 'Should navigate to /c/4/');
- MockInteractions.pressAndReleaseKeyOn(element, 82); // 'r'
- const change = element._changeForIndex(element.selectedIndex);
- assert.equal(change.reviewed, true,
- 'Should mark change as reviewed');
- MockInteractions.pressAndReleaseKeyOn(element, 82); // 'r'
- assert.equal(change.reviewed, false,
- 'Should mark change as unreviewed');
+ MockInteractions.pressAndReleaseKeyOn(element, 82); // 'r'
+ const change = element._changeForIndex(element.selectedIndex);
+ assert.equal(change.reviewed, true,
+ 'Should mark change as reviewed');
+ MockInteractions.pressAndReleaseKeyOn(element, 82); // 'r'
+ assert.equal(change.reviewed, false,
+ 'Should mark change as unreviewed');
+ });
});
test('highlight attribute is updated correctly', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 9f842c1..3b09270 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -912,7 +912,7 @@
_handleActionTap(e) {
e.preventDefault();
let el = Polymer.dom(e).localTarget;
- while (el.is !== 'gr-button') {
+ while (el.tagName.toLowerCase() !== 'gr-button') {
if (!el.parentElement) { return; }
el = el.parentElement;
}
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 2994c8d..00d5b42 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -120,6 +120,8 @@
}
.changeMetadata {
border-right: 1px solid var(--border-color);
+ /* Limit meta section to half of the screen at max */
+ max-width: 50%;
}
.commitMessage {
font-family: var(--monospace-font-family);
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index 6ac2195..7378acd 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -293,6 +293,12 @@
<div class="comments">Comments</div>
<div class="sizeBars">Size</div>
<div class="header-stats">Delta</div>
+ <template is="dom-if" if="[[_showDynamicColumns]]">
+ <template is="dom-repeat" items="[[_dynamicHeaderEndpoints]]" as="headerEndpoint">
+ <gr-endpoint-decorator name$="[[headerEndpoint]]">
+ </gr-endpoint-decorator>
+ </template>
+ </template>
<!-- Empty div here exists to keep spacing in sync with file rows. -->
<div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]"></div>
<div class="editFileControls showOnEdit"></div>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 561e21c..6b84901 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -183,7 +183,8 @@
_showDynamicColumns: {
type: Boolean,
- computed: '_computeShowDynamicColumns(_dynamicHeaderEndpoints)',
+ computed: '_computeShowDynamicColumns(_dynamicHeaderEndpoints, ' +
+ '_dynamicContentEndpoints, _dynamicSummaryEndpoints)',
},
/** @type {Array<string>} */
_dynamicHeaderEndpoints: {
@@ -1297,13 +1298,17 @@
return `sizeBars desktop ${hideClass}`;
},
- _computeShowDynamicColumns(dynamicHeaderEndpoints) {
- // During a design review, it was decided that dynamic columns should
- // remain hidden until column headers (including existing columns such as
- // "Comments") are in place to avoid confusion.
- // TODO(crbug.com/939904): Enable dispaying dynamic columns when there is
- // at least one of them registered.
- return false;
+ /**
+ * Shows registered dynamic columns iff the 'header', 'content' and
+ * 'summary' endpoints are regiestered the exact same number of times.
+ * Ideally, there should be a better way to enforce the expectation of the
+ * dependencies between dynamic endpoints.
+ */
+ _computeShowDynamicColumns(
+ headerEndpoints, contentEndpoints, summaryEndpoints) {
+ return headerEndpoints && contentEndpoints && summaryEndpoints &&
+ headerEndpoints.length === contentEndpoints.length &&
+ headerEndpoints.length === summaryEndpoints.length;
},
/**
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
index 8db000c..bb1743b 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -901,17 +901,17 @@
test('patch set from revisions', () => {
const expected = [
- {num: 4, desc: 'test'},
- {num: 3, desc: 'test'},
- {num: 2, desc: 'test'},
- {num: 1, desc: 'test'},
+ {num: 4, desc: 'test', sha: 'rev4'},
+ {num: 3, desc: 'test', sha: 'rev3'},
+ {num: 2, desc: 'test', sha: 'rev2'},
+ {num: 1, desc: 'test', sha: 'rev1'},
];
const patchNums = element.computeAllPatchSets({
revisions: {
- rev3: {_number: 3, description: 'test'},
- rev1: {_number: 1, description: 'test'},
- rev4: {_number: 4, description: 'test'},
- rev2: {_number: 2, description: 'test'},
+ rev3: {_number: 3, description: 'test', date: 3},
+ rev1: {_number: 1, description: 'test', date: 1},
+ rev4: {_number: 4, description: 'test', date: 4},
+ rev2: {_number: 2, description: 'test', date: 2},
},
});
assert.equal(patchNums.length, expected.length);
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
index a9fde2b..f889bfc 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
@@ -62,6 +62,10 @@
behaviors: [Gerrit.PatchSetBehavior],
+ _getShaForPatch(patch) {
+ return patch.sha.substring(0, 10);
+ },
+
_computeBaseDropdownContent(availablePatches, patchNum, _sortedRevisions,
changeComments, revisionInfo) {
// Polymer 2: check for undefined
@@ -85,7 +89,7 @@
for (const basePatch of availablePatches) {
const basePatchNum = basePatch.num;
const entry = this._createDropdownEntry(basePatchNum, 'Patchset ',
- _sortedRevisions, changeComments);
+ _sortedRevisions, changeComments, this._getShaForPatch(basePatch));
dropdownContent.push(Object.assign({}, entry, {
disabled: this._computeLeftDisabled(
basePatch.num, patchNum, _sortedRevisions),
@@ -133,7 +137,7 @@
const patchNum = patch.num;
const entry = this._createDropdownEntry(
patchNum, patchNum === 'edit' ? '' : 'Patchset ', _sortedRevisions,
- changeComments);
+ changeComments, this._getShaForPatch(patch));
dropdownContent.push(Object.assign({}, entry, {
disabled: this._computeRightDisabled(basePatchNum, patchNum,
_sortedRevisions),
@@ -142,12 +146,17 @@
return dropdownContent;
},
- _createDropdownEntry(patchNum, prefix, sortedRevisions, changeComments) {
+ _computeText(patchNum, prefix, changeComments, sha) {
+ return `${prefix}${patchNum}` +
+ `${this._computePatchSetCommentsString(changeComments, patchNum)}`
+ + (` | ${sha}`);
+ },
+
+ _createDropdownEntry(patchNum, prefix, sortedRevisions, changeComments,
+ sha) {
const entry = {
triggerText: `${prefix}${patchNum}`,
- text: `${prefix}${patchNum}` +
- `${this._computePatchSetCommentsString(
- changeComments, patchNum)}`,
+ text: this._computeText(patchNum, prefix, changeComments, sha),
mobileText: this._computeMobileText(patchNum, changeComments,
sortedRevisions),
bottomText: `${this._computePatchSetDescription(
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
index 922a11c..66fa974 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
@@ -120,10 +120,10 @@
test('_computeBaseDropdownContent', () => {
const availablePatches = [
- {num: 'edit'},
- {num: 3},
- {num: 2},
- {num: 1},
+ {num: 'edit', sha: '1'},
+ {num: 3, sha: '2'},
+ {num: 2, sha: '3'},
+ {num: 1, sha: '4'},
];
const revisions = [
{
@@ -147,7 +147,7 @@
{
disabled: true,
triggerText: 'Patchset edit',
- text: 'Patchset edit',
+ text: 'Patchset edit | 1',
mobileText: 'edit',
bottomText: '',
value: 'edit',
@@ -155,7 +155,7 @@
{
disabled: true,
triggerText: 'Patchset 3',
- text: 'Patchset 3',
+ text: 'Patchset 3 | 2',
mobileText: '3',
bottomText: '',
value: 3,
@@ -164,7 +164,7 @@
{
disabled: true,
triggerText: 'Patchset 2',
- text: 'Patchset 2',
+ text: 'Patchset 2 | 3',
mobileText: '2 description',
bottomText: 'description',
value: 2,
@@ -172,7 +172,7 @@
{
disabled: true,
triggerText: 'Patchset 1',
- text: 'Patchset 1',
+ text: 'Patchset 1 | 4',
mobileText: '1',
bottomText: '',
value: 1,
@@ -197,10 +197,10 @@
];
element.revisionInfo = getInfo(element.revisions);
element.availablePatches = [
- {num: 1},
- {num: 2},
- {num: 3},
- {num: 'edit'},
+ {num: 1, sha: '1'},
+ {num: 2, sha: '2'},
+ {num: 3, sha: '3'},
+ {num: 'edit', sha: '4'},
];
element.patchNum = 2;
element.basePatchNum = 'PARENT';
@@ -223,10 +223,10 @@
];
element.revisionInfo = getInfo(element.revisions);
element.availablePatches = [
- {num: 'edit'},
- {num: 3},
- {num: 2},
- {num: 1},
+ {num: 'edit', sha: '1'},
+ {num: 3, sha: '2'},
+ {num: 2, sha: '3'},
+ {num: 1, sha: '4'},
];
element.patchNum = 2;
element.basePatchNum = 'PARENT';
@@ -250,10 +250,10 @@
];
element.revisionInfo = getInfo(element.revisions);
element.availablePatches = [
- {num: 1},
- {num: 2},
- {num: 3},
- {num: 'edit'},
+ {num: 1, sha: '1'},
+ {num: 2, sha: '2'},
+ {num: 3, sha: '3'},
+ {num: 'edit', sha: '4'},
];
element.patchNum = 2;
element.basePatchNum = 'PARENT';
@@ -274,10 +274,10 @@
];
element.revisionInfo = getInfo(element.revisions);
element.availablePatches = [
- {num: 1},
- {num: 2},
- {num: 3},
- {num: 'edit'},
+ {num: 1, sha: '1'},
+ {num: 2, sha: '2'},
+ {num: 3, sha: '3'},
+ {num: 'edit', sha: '4'},
];
element.patchNum = 2;
element.basePatchNum = 'PARENT';
@@ -293,10 +293,10 @@
test('_computePatchDropdownContent', () => {
const availablePatches = [
- {num: 'edit'},
- {num: 3},
- {num: 2},
- {num: 1},
+ {num: 'edit', sha: '1'},
+ {num: 3, sha: '2'},
+ {num: 2, sha: '3'},
+ {num: 1, sha: '4'},
];
const basePatchNum = 1;
const sortedRevisions = [
@@ -310,7 +310,7 @@
{
disabled: false,
triggerText: 'edit',
- text: 'edit',
+ text: 'edit | 1',
mobileText: 'edit',
bottomText: '',
value: 'edit',
@@ -318,7 +318,7 @@
{
disabled: false,
triggerText: 'Patchset 3',
- text: 'Patchset 3',
+ text: 'Patchset 3 | 2',
mobileText: '3',
bottomText: '',
value: 3,
@@ -327,7 +327,7 @@
{
disabled: false,
triggerText: 'Patchset 2',
- text: 'Patchset 2',
+ text: 'Patchset 2 | 3',
mobileText: '2 description',
bottomText: 'description',
value: 2,
@@ -335,7 +335,7 @@
{
disabled: true,
triggerText: 'Patchset 1',
- text: 'Patchset 1',
+ text: 'Patchset 1 | 4',
mobileText: '1',
bottomText: '',
value: 1,
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
index 383687f..25c7738 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
@@ -259,12 +259,14 @@
lastNotify: {left: 1, right: 1},
};
+ const rangesCache = new Map();
+
this._processPromise = util.makeCancelable(this._loadHLJS()
.then(() => {
return new Promise(resolve => {
const nextStep = () => {
this._processHandle = null;
- this._processNextLine(state);
+ this._processNextLine(state, rangesCache);
// Move to the next line in the section.
state.lineIndex++;
@@ -321,12 +323,21 @@
* Highlight.js emits and emit a list of text ranges and classes for the
* markers.
* @param {string} str The string of HTML.
+ * @param {Map<string, !Array<!Object>>} rangesCache A map for caching
+ * ranges for each string. A cache is read and written by this method.
+ * Since diff is mostly comparing same file on two sides, there is good rate
+ * of duplication at least for parts that are on left and right parts.
* @return {!Array<!Object>} The list of ranges.
*/
- _rangesFromString(str) {
+ _rangesFromString(str, rangesCache) {
+ const cached = rangesCache.get(str);
+ if (cached) return cached;
+
const div = document.createElement('div');
div.innerHTML = str;
- return this._rangesFromElement(div, 0);
+ const ranges = this._rangesFromElement(div, 0);
+ rangesCache.set(str, ranges);
+ return ranges;
},
_rangesFromElement(elem, offset) {
@@ -357,7 +368,7 @@
* lines).
* @param {!Object} state The processing state for the layer.
*/
- _processNextLine(state) {
+ _processNextLine(state, rangesCache) {
let baseLine;
let revisionLine;
@@ -386,7 +397,8 @@
baseLine = this._workaround(this._baseLanguage, baseLine);
result = this._hljs.highlight(this._baseLanguage, baseLine, true,
state.baseContext);
- this.push('_baseRanges', this._rangesFromString(result.value));
+ this.push('_baseRanges',
+ this._rangesFromString(result.value, rangesCache));
state.baseContext = result.top;
}
@@ -395,7 +407,8 @@
revisionLine = this._workaround(this._revisionLanguage, revisionLine);
result = this._hljs.highlight(this._revisionLanguage, revisionLine,
true, state.revisionContext);
- this.push('_revisionRanges', this._rangesFromString(result.value));
+ this.push('_revisionRanges',
+ this._rangesFromString(result.value, rangesCache));
state.revisionContext = result.top;
}
},
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
index 472db21..4e3492f 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
@@ -388,10 +388,20 @@
'<span class="non-whtelisted-class">',
'<span class="gr-diff gr-syntax gr-syntax-keyword">public</span>',
'</span>'].join('');
- const result = element._rangesFromString(str);
+ const result = element._rangesFromString(str, new Map());
assert.notEqual(result.length, 0);
});
+ test('_rangesFromString cache same syntax markers', () => {
+ sandbox.spy(element, '_rangesFromElement');
+ const str =
+ '<span class="gr-diff gr-syntax gr-syntax-keyword">public</span>';
+ const cacheMap = new Map();
+ element._rangesFromString(str, cacheMap);
+ element._rangesFromString(str, cacheMap);
+ assert.isTrue(element._rangesFromElement.calledOnce);
+ });
+
test('_isSectionDone', () => {
let state = {sectionIndex: 0, lineIndex: 0};
assert.isFalse(element._isSectionDone(state));
diff --git a/polygerrit-ui/app/elements/gr-app-p2.html b/polygerrit-ui/app/elements/gr-app-p2.html
deleted file mode 100644
index 01727f3..0000000
--- a/polygerrit-ui/app/elements/gr-app-p2.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!--
-@license
-Copyright (C) 2019 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.
--->
-
-<!--
- TODO(taoalpha): This file only used by google,
- remove once polymer 2 fully rolled out.
--->
-<script>
- window.Gerrit = window.Gerrit || {};
-
- // Disable extra font load from paper-styles
- window.polymerSkipLoadingFontRoboto = true;
-</script>
-
-<link rel="import" href="/bower_components/polymer/polymer.html">
-<link rel="import" href="/bower_components/polymer-resin/standalone/polymer-resin.html">
-<link rel="import" href="/bower_components/polymer/lib/legacy/legacy-data-mixin.html">
-<link rel="import" href="../behaviors/safe-types-behavior/safe-types-behavior.html">
-<script>
- security.polymer_resin.install({
- allowedIdentifierPrefixes: [''],
- reportHandler: security.polymer_resin.CONSOLE_LOGGING_REPORT_HANDLER,
- safeTypesBridge: Gerrit.SafeTypes.safeTypesBridge,
- });
-</script>
-
-<link rel="import" href="./gr-app-element.html">
-<dom-module id="gr-app-p2">
- <template>
- <gr-app-element id="app-element"></gr-app-element>
- </template>
- <script src="gr-app-p2.js" crossorigin="anonymous"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/gr-app-p2.js b/polygerrit-ui/app/elements/gr-app-p2.js
deleted file mode 100644
index 638a1bc..0000000
--- a/polygerrit-ui/app/elements/gr-app-p2.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * @license
- * Copyright (C) 2019 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.
- */
-
-// TODO(taoalpha): This file is only used by google,
-// remove once polymer fully rolled out.
-(function() {
- 'use strict';
-
- Polymer({
- is: 'gr-app-p2',
- });
-})();
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index a3364b4..d23de3d 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -15,26 +15,13 @@
limitations under the License.
-->
<script>
- // TODO(taoalpha): clean up after p2 fully rolled out
- if (!window.POLYMER2) {
- // This must be set prior to loading Polymer for the first time.
- if (localStorage.getItem('USE_SHADOW_DOM') === 'true') {
- window.Polymer = {
- dom: 'shadow',
- passiveTouchGestures: true,
- lazyRegister: true,
- };
- } else if (!window.Polymer) {
- window.Polymer = {
- passiveTouchGestures: true,
- lazyRegister: true,
- };
- }
+ if (!window.Polymer) {
+ window.Polymer = {
+ passiveTouchGestures: true,
+ lazyRegister: true,
+ };
}
window.Gerrit = window.Gerrit || {};
-
- // Disable extra font load from paper-styles
- window.polymerSkipLoadingFontRoboto = true;
</script>
<link rel="import" href="/bower_components/polymer/polymer.html">
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 1f6c4a0..ac8ea1a 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -17,15 +17,6 @@
(function() {
'use strict';
- // Eagerly render Polymer components when backgrounded. (Skips
- // requestAnimationFrame.)
- // @see https://github.com/Polymer/polymer/issues/3851
- // @see Issue 4699
- // TODO(taoalpha): Remove once p2 fully rolled out
- if (!window.POLYMER2) {
- Polymer.RenderStatus._makeReady();
- }
-
Polymer({
is: 'gr-app',
});
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
index 312cc34..2b63d99 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
@@ -68,7 +68,7 @@
assert.equal(element._computeMobileText(item), item.mobileText);
});
- test('options are selected and laid out correctly', () => {
+ test('options are selected and laid out correctly', done => {
element.value = 2;
element.items = [
{
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index f0df879..dec5b1d 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -1776,7 +1776,7 @@
opt_workInProgress, opt_baseChange, opt_baseCommit) {
return this._restApiHelper.send({
method: 'POST',
- url: '/changes/',
+ url: `/projects/${encodeURIComponent(project)}/create.change`,
body: {
project,
branch,
diff --git a/polygerrit-ui/server.go b/polygerrit-ui/server.go
index 1a2d299..35bdefd 100644
--- a/polygerrit-ui/server.go
+++ b/polygerrit-ui/server.go
@@ -43,6 +43,8 @@
host = flag.String("host", "gerrit-review.googlesource.com", "Host to proxy requests to")
scheme = flag.String("scheme", "https", "URL scheme")
cdnPattern = regexp.MustCompile("https://cdn.googlesource.com/polygerrit_ui/[0-9.]*")
+ webComponentPattern = regexp.MustCompile("webcomponentsjs-p2")
+ grAppPattern = regexp.MustCompile("gr-app-p2")
bundledPluginsPattern = regexp.MustCompile("https://cdn.googlesource.com/polygerrit_assets/[0-9.]*")
)
@@ -184,9 +186,13 @@
buf.ReadFrom(reader)
original := buf.String()
+ // Replace the webcomponentsjs-p2 with webcomponentsjs
+ replaced := webComponentPattern.ReplaceAllString(original, "webcomponentsjs")
+ replaced = grAppPattern.ReplaceAllString(replaced, "gr-app")
+
// Simply remove all CDN references, so files are loaded from the local file system or the proxy
// server instead.
- replaced := cdnPattern.ReplaceAllString(original, "")
+ replaced = cdnPattern.ReplaceAllString(replaced, "")
// Modify window.INITIAL_DATA so that it has the same effect as injectLocalPlugins. To achieve
// this let's add JavaScript lines at the end of the <script>...</script> snippet that also
diff --git a/proto/cache.proto b/proto/cache.proto
index 2a07640..10e0216 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -75,7 +75,7 @@
// Instead, we just take the tedious yet simple approach of having a "has_foo"
// field for each nullable field "foo", indicating whether or not foo is null.
//
-// Next ID: 20
+// Next ID: 23
message ChangeNotesStateProto {
// Effectively required, even though the corresponding ChangeNotesState field
// is optional, since the field is only absent when NoteDb is disabled, in
@@ -110,8 +110,8 @@
string submission_id = 13;
bool has_submission_id = 14;
- int32 assignee = 15;
- bool has_assignee = 16;
+ reserved 15; // assignee
+ reserved 16; // has_assignee
string status = 17;
bool has_status = 18;
@@ -130,7 +130,7 @@
// which case attempting to use the ChangeNotesCache is programmer error.
ChangeColumnsProto columns = 3;
- repeated int32 past_assignee = 4;
+ reserved 4; // past_assignee
repeated string hashtag = 5;
@@ -189,6 +189,14 @@
string server_id = 20;
bool has_server_id = 21;
+
+ message AssigneeStatusUpdateProto {
+ int64 date = 1;
+ int32 updated_by = 2;
+ int32 current_assignee = 3;
+ bool has_current_assignee = 4;
+ }
+ repeated AssigneeStatusUpdateProto assignee_update = 22;
}
diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index 8598229b..3feb1b4 100644
--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -24,7 +24,6 @@
{@param? assetsBundle: ?} /** {string} Assets bundle .html file, served from $assetsPath. */
{@param? faviconPath: ?}
{@param? versionInfo: ?}
- {@param? polymer2: ?}
{@param? polyfillCE: ?}
{@param? polyfillSD: ?}
{@param? polyfillSC: ?}
@@ -40,9 +39,9 @@
</noscript>
<script>
+ // Disable extra font load from paper-styles
+ window.polymerSkipLoadingFontRoboto = true;
window.CLOSURE_NO_DEPS = true;
- // TODO(taoalpha): clean up once p2 fully rolled out
- {if $polymer2}window.POLYMER2 = true;{/if}
{if $canonicalPath != ''}window.CANONICAL_PATH = '{$canonicalPath}';{/if}
{if $versionInfo}window.VERSION_INFO = '{$versionInfo}';{/if}
{if $staticResourcePath != ''}window.STATIC_RESOURCE_PATH = '{$staticResourcePath}';{/if}
@@ -92,17 +91,8 @@
<link rel="import" href="{$assetsPath}/{$assetsBundle}">{\n}
{/if}
- // TODO(taoalpha): only used by Google, remove once polymer 2 fully rolled out
- {if $polymer2}
- <link rel="import" href="{$staticResourcePath}/elements/gr-app-p2.html">{\n}
- {else}
- <link rel="import" href="{$staticResourcePath}/elements/gr-app.html">{\n}
- {/if}
+ <link rel="import" href="{$staticResourcePath}/elements/gr-app.html">{\n}
<body unresolved>{\n}
- {if $polymer2}
- <gr-app-p2 id="app"></gr-app-p2>{\n}
- {else}
- <gr-app id="app"></gr-app>{\n}
- {/if}
+ <gr-app id="app"></gr-app>{\n}
{/template}
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 0bc41fc..7f0e88b 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -203,7 +203,7 @@
testAtt.setAttribute('name', 'test')
testAtt.setAttribute('value', 'true')
atts.appendChild(testAtt)
- if "apt_generated" in path:
+ if "apt_generated" in path or "modules/jgit" in path:
if not atts:
atts = doc.createElement('attributes')
ignoreOptionalProblems = doc.createElement('attribute')
diff --git a/tools/maven/api.sh b/tools/maven/api.sh
index e72e3cb..c6049bf 100755
--- a/tools/maven/api.sh
+++ b/tools/maven/api.sh
@@ -60,7 +60,13 @@
set -o xtrace
fi
-bazel build //tools/maven:gen_${command} "$@" || \
- { echo "bazel failed to build gen_${command}. Use VERBOSE=1 for more info" ; exit 1 ; }
+if [[ `which bazelisk` ]]; then
+ BAZEL_CMD=bazelisk
+else
+ BAZEL_CMD=bazel
+fi
-./bazel-genfiles/tools/maven/${command}.sh
+${BAZEL_CMD} build //tools/maven:gen_${command} "$@" || \
+ { echo "${BAZEL_CMD} failed to build gen_${command}. Use VERBOSE=1 for more info" ; exit 1 ; }
+
+./bazel-bin/tools/maven/${command}.sh
diff --git a/tools/maven/gerrit-acceptance-framework_pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
index 14a07d1..4240a9b 100644
--- a/tools/maven/gerrit-acceptance-framework_pom.xml
+++ b/tools/maven/gerrit-acceptance-framework_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-acceptance-framework</artifactId>
- <version>3.1.0-SNAPSHOT</version>
+ <version>3.2.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Acceptance Test Framework</name>
<description>Framework for Gerrit's acceptance tests</description>
diff --git a/tools/maven/gerrit-extension-api_pom.xml b/tools/maven/gerrit-extension-api_pom.xml
index 0cf1448..cf2b080 100644
--- a/tools/maven/gerrit-extension-api_pom.xml
+++ b/tools/maven/gerrit-extension-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-extension-api</artifactId>
- <version>3.1.0-SNAPSHOT</version>
+ <version>3.2.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Extension API</name>
<description>API for Gerrit Extensions</description>
diff --git a/tools/maven/gerrit-plugin-api_pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
index ebb66b4..7d3c4f0 100644
--- a/tools/maven/gerrit-plugin-api_pom.xml
+++ b/tools/maven/gerrit-plugin-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-plugin-api</artifactId>
- <version>3.1.0-SNAPSHOT</version>
+ <version>3.2.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Plugin API</name>
<description>API for Gerrit Plugins</description>
diff --git a/tools/maven/gerrit-war_pom.xml b/tools/maven/gerrit-war_pom.xml
index 51e517b..9478283 100644
--- a/tools/maven/gerrit-war_pom.xml
+++ b/tools/maven/gerrit-war_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-war</artifactId>
- <version>3.1.0-SNAPSHOT</version>
+ <version>3.2.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Gerrit Code Review - WAR</name>
<description>Gerrit WAR</description>
diff --git a/version.bzl b/version.bzl
index 42a576c..fb1e5ca 100644
--- a/version.bzl
+++ b/version.bzl
@@ -2,4 +2,4 @@
# Used by :api_install and :api_deploy targets
# when talking to the destination repository.
#
-GERRIT_VERSION = "3.1.0-SNAPSHOT"
+GERRIT_VERSION = "3.2.0-SNAPSHOT"